Merge branch 'master' into haroon_dev

# Conflicts:
#	lib/presentation/home/landing_page.dart
#	lib/presentation/medical_file/medical_file_page.dart
#	lib/widgets/chip/app_custom_chip_widget.dart
pull/77/head
haroon amjad 3 weeks ago
commit b27e3268b1

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.9375 9C0.9375 13.4528 4.54725 17.0625 9 17.0625C13.4528 17.0625 17.0625 13.4528 17.0625 9C17.0625 4.54725 13.4528 0.9375 9 0.9375C4.54725 0.9375 0.9375 4.54725 0.9375 9ZM12.507 6.19724C12.8123 6.47699 12.8325 6.95173 12.5528 7.25698L8.42776 11.757C8.28976 11.9077 8.0955 11.9955 7.8915 12C7.68675 12.0045 7.4895 11.925 7.34475 11.7803L5.46975 9.90527C5.1765 9.61277 5.1765 9.13723 5.46975 8.84473C5.76225 8.55148 6.23776 8.55148 6.53026 8.84473L7.85175 10.1655L11.4472 6.24302C11.727 5.93777 12.2018 5.91749 12.507 6.19724Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 697 B

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.04288 1.3125C10.6859 1.31249 11.9764 1.31248 12.984 1.44795C14.0165 1.58676 14.8357 1.87704 15.4793 2.52069C16.123 3.16433 16.4132 3.98352 16.5521 5.01602C16.6875 6.02358 16.6875 7.31408 16.6875 8.9571V9.04288C16.6875 10.6859 16.6875 11.9764 16.5521 12.984C16.4132 14.0165 16.123 14.8357 15.4793 15.4793C14.8357 16.123 14.0165 16.4132 12.984 16.5521C11.9764 16.6875 10.6859 16.6875 9.0429 16.6875H8.95712C7.3141 16.6875 6.02358 16.6875 5.01602 16.5521C3.98352 16.4132 3.16433 16.123 2.52068 15.4793C1.87704 14.8357 1.58676 14.0165 1.44795 12.984C1.31248 11.9764 1.31249 10.6859 1.3125 9.04288V8.95712C1.31249 7.31409 1.31248 6.02358 1.44795 5.01602C1.58676 3.98352 1.87704 3.16433 2.52068 2.52069C3.16433 1.87704 3.98352 1.58676 5.01602 1.44795C6.02358 1.31248 7.31409 1.31249 8.95712 1.3125H9.04288ZM8.25 12.375C8.25 11.9608 8.58428 11.625 8.99665 11.625H9.00335C9.41571 11.625 9.75 11.9608 9.75 12.375C9.75 12.7892 9.41571 13.125 9.00335 13.125H8.99665C8.58428 13.125 8.25 12.7892 8.25 12.375ZM8.25009 9.375C8.25009 9.78921 8.58588 10.125 9.00009 10.125C9.4143 10.125 9.75009 9.78921 9.75009 9.375V6.375C9.75009 5.96079 9.4143 5.625 9.00009 5.625C8.58588 5.625 8.25009 5.96079 8.25009 6.375V9.375Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.41841 13.0471C6.53944 12.9557 6.90075 12.6828 7.10903 12.5202C7.52619 12.1947 8.08051 11.7498 8.63318 11.27C9.18864 10.7877 9.72961 10.2811 10.1273 9.83098C10.3267 9.6053 10.4782 9.40705 10.5769 9.24345C10.6696 9.08959 10.6889 8.9987 10.6889 8.9987C10.6889 8.9987 10.6696 8.91048 10.5769 8.75662C10.4782 8.59303 10.3267 8.39477 10.1273 8.16909C9.72961 7.71901 9.18864 7.21237 8.63317 6.73009C8.08049 6.25024 7.52617 5.80541 7.10901 5.47985C6.90072 5.31731 6.53992 5.04475 6.41889 4.95334C6.16875 4.76911 6.1148 4.41661 6.29902 4.16647C6.48325 3.91633 6.83537 3.86289 7.08551 4.04711L7.08742 4.04855C7.21436 4.14443 7.58862 4.42711 7.80114 4.59296C8.22772 4.92586 8.7984 5.38369 9.37073 5.8806C9.94026 6.37509 10.5243 6.91935 10.9704 7.42418C11.1928 7.67596 11.3929 7.93118 11.5403 8.1758C11.6785 8.40499 11.8144 8.69516 11.8144 9.00004C11.8144 9.30492 11.6785 9.59509 11.5403 9.82427C11.3929 10.0689 11.1928 10.3241 10.9704 10.5759C10.5243 11.0807 9.94027 11.625 9.37074 12.1195C8.79842 12.6164 8.22775 13.0742 7.80117 13.4071C7.5885 13.5731 7.21423 13.8558 7.08755 13.9515L7.08591 13.9527C6.83577 14.1369 6.48329 14.0837 6.29906 13.8336C6.11484 13.5835 6.16829 13.2313 6.41841 13.0471Z" fill="#161616"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,3 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 0.25C5.06294 0.25 0.25 5.06294 0.25 11C0.25 16.9371 5.06294 21.75 11 21.75C16.9371 21.75 21.75 16.9371 21.75 11C21.75 5.06294 16.9371 0.25 11 0.25ZM7 10C6.44772 10 6 10.4477 6 11C6 11.5523 6.44772 12 7 12H15C15.5523 12 16 11.5523 16 11C16 10.4477 15.5523 10 15 10H7Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 441 B

@ -0,0 +1,8 @@
<svg width="17" height="18" viewBox="0 0 17 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.37427 0.9375C3.12631 0.9375 2.12427 1.95016 2.12427 3.1875C2.12427 4.42484 3.12631 5.4375 4.37427 5.4375C5.62222 5.4375 6.62427 4.42484 6.62427 3.1875C6.62427 1.95016 5.62222 0.9375 4.37427 0.9375Z" fill="#ED1C2B"/>
<path d="M6.67566 6.3702C5.2645 5.6266 3.48447 5.6266 2.0733 6.3702C2.03796 6.38882 1.99128 6.41204 1.93632 6.43938C1.67362 6.57008 1.22162 6.79494 0.913308 7.062C0.724359 7.22567 0.488314 7.48646 0.444083 7.84456C0.394558 8.24552 0.602042 8.58044 0.898469 8.83036C1.3683 9.22647 1.95554 9.5625 2.72106 9.5625L6.02791 9.5625C6.79342 9.5625 7.38067 9.22647 7.85049 8.83035C8.14692 8.58044 8.35441 8.24552 8.30488 7.84456C8.26065 7.48646 8.0246 7.22567 7.83565 7.062C7.52733 6.79493 7.07534 6.57007 6.81264 6.43938C6.75768 6.41204 6.71101 6.38882 6.67566 6.3702Z" fill="#ED1C2B"/>
<path d="M9.36201 1.8557C9.49867 1.63459 9.74007 1.5 10 1.5C12.9021 1.5 15.25 3.84793 15.25 6.75C15.25 7.16421 14.9142 7.5 14.5 7.5C14.0858 7.5 13.75 7.16421 13.75 6.75C13.75 5.30962 12.9412 4.06093 11.7522 3.43259L11.1504 4.03441C11.0364 4.14842 10.9065 4.25631 10.7469 4.27867C10.4366 4.32214 10.1199 4.16679 9.97204 3.87112L9.32918 2.58541C9.21293 2.35292 9.22536 2.07681 9.36201 1.8557Z" fill="#ED1C2B"/>
<path d="M2.5 10.5C2.91421 10.5 3.25 10.8358 3.25 11.25C3.25 12.6904 4.05882 13.9391 5.24779 14.5674L5.84961 13.9656C5.96361 13.8516 6.09348 13.7437 6.25314 13.7213C6.56336 13.6779 6.88013 13.8332 7.02796 14.1289L7.67082 15.4146C7.78707 15.6471 7.77464 15.9232 7.63799 16.1443C7.50133 16.3654 7.25993 16.5 7 16.5C4.09793 16.5 1.75 14.1521 1.75 11.25C1.75 10.8358 2.08579 10.5 2.5 10.5Z" fill="#ED1C2B"/>
<path d="M10.375 10.6875C10.375 9.45016 11.377 8.4375 12.625 8.4375C13.873 8.4375 14.875 9.45016 14.875 10.6875C14.875 11.9248 13.873 12.9375 12.625 12.9375C11.377 12.9375 10.375 11.9248 10.375 10.6875Z" fill="#ED1C2B"/>
<path d="M10.324 13.8702C11.7352 13.1266 13.5152 13.1266 14.9264 13.8702C14.9617 13.8888 15.0084 13.912 15.0634 13.9394C15.3261 14.0701 15.7781 14.2949 16.0864 14.562C16.2753 14.7257 16.5114 14.9865 16.5556 15.3446C16.6051 15.7455 16.3977 16.0804 16.1012 16.3304C15.6314 16.7265 15.0442 17.0625 14.2786 17.0625H10.9718C10.2063 17.0625 9.61903 16.7265 9.1492 16.3304C8.85277 16.0804 8.64529 15.7455 8.69482 15.3446C8.73905 14.9865 8.97509 14.7257 9.16404 14.562C9.47235 14.2949 9.92433 14.0701 10.187 13.9394C10.242 13.9121 10.2887 13.8888 10.324 13.8702Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -88,16 +88,16 @@ class ApiClientImp implements ApiClient {
@override
post(
String endPoint, {
required Map<String, dynamic> body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false,
bool isPaymentServices = false,
bool bypassConnectionCheck = false,
}) async {
String endPoint, {
required Map<String, dynamic> body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false,
bool isPaymentServices = false,
bool bypassConnectionCheck = false,
}) async {
String url;
if (isExternal) {
url = endPoint;
@ -173,8 +173,8 @@ class ApiClientImp implements ApiClient {
body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken;
}
body['TokenID'] = "@dm!n";
body['PatientID'] = 4772172;
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 4772172;
// body['PatientTypeID'] = 1;
//
// body['PatientOutSA'] = 0;

@ -727,7 +727,7 @@ const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatu
class ApiConsts {
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
@ -832,6 +832,13 @@ class ApiConsts {
static final String registerUser = 'Services/Authentication.svc/REST/PatientRegistration';
static final String addFamilyFile = 'Services/Patients.svc/REST/ShareFamilyFileService';
static final String sendFamilyFileActivation = 'Services/Authentication.svc/REST/SendActivationCodeForFamilyFile';
static final String checkActivationCodeForFamily = 'Services/Authentication.svc/REST/CheckActivationCodeForFamilyFile';
static final String getAllPendingRecordsByResponseId = 'Services/Authentication.svc/REST/GetAllPendingRecordsByResponseId';
static final String getAllSharedRecordsByStatus = 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus';
static final String removeFileFromFamilyMembers = 'Services/Authentication.svc/REST/ActiveDeactive_PatientFile';
static final String acceptAndRejectFamilyFile = 'Services/Authentication.svc/REST/Update_FileStatus';
// static values for Api
static final double appVersionID = 18.7;

@ -152,6 +152,9 @@ class AppAssets {
static const String ic_filters = '$svgBasePath/filters.svg';
static const String ic_close = '$svgBasePath/ic_close.svg';
static const String ic_cross_circle = '$svgBasePath/cross_circle.svg';
static const String switch_user = '$svgBasePath/switch_user.svg';
static const String activeCheck = '$svgBasePath/active-check.svg';
static const String deleteIcon = '$svgBasePath/delete_icon.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';
@ -163,6 +166,8 @@ class AppAssets {
static const String feedback = '$svgBasePath/feedback.svg';
static const String news = '$svgBasePath/news.svg';
static const String heart = '$svgBasePath/heart.svg';
static const String alertSquare = '$svgBasePath/alert-square.svg';
static const String arrowRight = '$svgBasePath/arrow-right.svg';
// PNGS //
static const String hmg_logo = '$pngBasePath/hmg_logo.png';
@ -177,6 +182,7 @@ class AppAssets {
static const String tamara_en = '$pngBasePath/tamara_en.png';
static const String visa = '$pngBasePath/visa.png';
static const String lockIcon = '$pngBasePath/lock-icon.png';
static const String dummy_user = '$pngBasePath/dummy_user.png';
}
class AppAnimations {

@ -37,14 +37,16 @@ class AppState {
AuthenticatedUser? _authenticatedRootUser;
AuthenticatedUser? _authenticatedChildUser;
void setAuthenticatedUser(AuthenticatedUser authenticatedUser, {bool isFamily = false}) {
int? _superUserID;
bool isChildLoggedIn = false;
void setAuthenticatedUser(AuthenticatedUser? authenticatedUser, {bool isFamily = false}) {
if (isFamily) {
_authenticatedChildUser = authenticatedUser;
} else {
setIsAuthenticated = true;
_authenticatedRootUser = authenticatedUser;
}
}
AuthenticatedUser? getAuthenticatedUser({bool isFamily = false}) {
@ -55,6 +57,14 @@ class AppState {
}
}
int? get getSuperUserID => _superUserID;
bool get getIsChildLoggedIn => isChildLoggedIn;
set setSuperUserID(int? value) => _superUserID = value;
set setIsChildLoggedIn(bool value) => isChildLoggedIn = value;
String _userBloodGroup = "";
String get getUserBloodGroup => _userBloodGroup;
@ -97,6 +107,14 @@ class AppState {
set setDeviceTypeID(v) => deviceTypeID = v;
String _familyFileTokenID = "";
String get getFamilyFileTokenID => _familyFileTokenID;
set setFamilyFileTokenID(String value) {
_familyFileTokenID = value;
}
List<VidaPlusProjectListModel> vidaPlusProjectList = [];
List<ListPrivilege> privilegeModelList = [];
List<HMCProjectListModel> hMCProjectListModel = [];
@ -135,9 +153,8 @@ class AppState {
}
///this will be called if there is any problem in getting the user location
void resetLocation(){
void resetLocation() {
userLong = 0.0;
userLong = 0.0;
}
}

@ -26,6 +26,7 @@ import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
@ -103,11 +104,7 @@ class AppDependencies {
// Global/shared VMs LazySingleton
getIt.registerLazySingleton<LabViewModel>(
() => LabViewModel(
labRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt()
),
() => LabViewModel(labRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
);
getIt.registerLazySingleton<RadiologyViewModel>(
@ -173,6 +170,7 @@ class AppDependencies {
() => AuthenticationViewModel(
authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()),
);
getIt.registerLazySingleton<ProfileSettingsViewModel>(() => ProfileSettingsViewModel());
getIt.registerLazySingleton<DateRangeSelectorRangeViewModel>(
() => DateRangeSelectorRangeViewModel(),

@ -30,6 +30,8 @@ enum LoginTypeEnum { sms, whatsapp, face, fingerprint }
enum AppEnvironmentTypeEnum { dev, uat, preProd, qa, staging, prod }
enum FamilyFileEnum { active, inactive, blocked, deleted, pending, rejected }
extension CalenderExtension on CalenderEnum {
int get toInt {
switch (this) {
@ -138,6 +140,61 @@ extension OTPTypeEnumExtension on OTPTypeEnum {
}
}
extension FamilyFileEnumExtenshion on FamilyFileEnum {
int get toInt {
switch (this) {
case FamilyFileEnum.active:
return 3;
case FamilyFileEnum.blocked:
return 1;
case FamilyFileEnum.deleted:
return 0;
case FamilyFileEnum.pending:
return 2;
case FamilyFileEnum.inactive:
return 6;
case FamilyFileEnum.rejected:
return 4;
}
}
String get displayName {
AppState appState = getIt.get<AppState>();
bool isArabic = appState.getLanguageID() == 1 ? true : false;
switch (this) {
case FamilyFileEnum.active:
return isArabic ? 'نشط' : 'Active';
case FamilyFileEnum.inactive:
return isArabic ? 'غير نشط' : 'Inactive';
case FamilyFileEnum.blocked:
return isArabic ? 'محظور' : 'Blocked';
case FamilyFileEnum.deleted:
return isArabic ? 'محذوف' : 'Deleted';
case FamilyFileEnum.pending:
return isArabic ? 'قيد الانتظار' : 'Pending';
case FamilyFileEnum.rejected:
return isArabic ? 'مرفوض' : 'Rejected';
}
}
static FamilyFileEnum? fromValue(int value) {
switch (value) {
case 0:
return FamilyFileEnum.pending;
case 2:
return FamilyFileEnum.blocked;
case 1:
return FamilyFileEnum.deleted;
case 3:
return FamilyFileEnum.active;
case 4:
return FamilyFileEnum.inactive;
default:
return null;
}
}
}
enum ServiceTypeEnum {
advancePayment, //3
ancillaryOrder, //3

@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.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/request_models/send_activation_request_model.dart';
import 'package:hmg_patient_app_new/features/common/models/commong_authanticated_req_model.dart';
import 'package:hmg_patient_app_new/features/common/models/family_file_request.dart';
class RequestUtils {
static dynamic getPatientAuthenticationRequest({
@ -125,6 +126,9 @@ class RequestUtils {
required bool isForRegister,
required bool isFileNo,
dynamic payload,
required bool isExcludedUser,
required bool isFormFamilyFile,
int? responseID,
}) {
AppState _appState = getIt.get<AppState>();
var request = SendActivationRequest();
@ -156,6 +160,15 @@ class RequestUtils {
request.isRegister = false;
}
request.deviceTypeID = request.searchType;
if (isFormFamilyFile) {
//INFO: Only for Excluded User Family Member Addition
request.isPatientExcluded = isExcludedUser;
request.responseID = responseID;
request.status = 2;
request.familyRegionID = zipCode == CountryEnum.saudiArabia.countryCode ? 1 : 2;
}
return request;
}
@ -247,19 +260,29 @@ class RequestUtils {
};
}
static dynamic getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode, required int loginType}) {
var request = <String, dynamic>{};
static Future<FamilyFileRequest> getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode}) async {
FamilyFileRequest request = FamilyFileRequest();
int? loginType = 0;
if (countryCode == CountryEnum.saudiArabia.countryCode || countryCode == '+966') {
loginType = (nationalIDorFile.length == 10) ? 1 : 2;
} else if (countryCode == CountryEnum.unitedArabEmirates.countryCode || countryCode == '+971') {
loginType = (nationalIDorFile.length == 15) ? 1 : 2;
}
if (loginType == 1) {
request["sharedPatientID"] = 0;
request["sharedPatientIdentificationID"] = nationalIDorFile;
request.sharedPatientId = 0;
request.sharedPatientIdentificationId = nationalIDorFile;
} else if (loginType == 2) {
request["sharedPatientID"] = int.parse(nationalIDorFile);
request["sharedPatientIdentificationID"] = '';
request.sharedPatientId = int.parse(nationalIDorFile);
request.sharedPatientIdentificationId = '';
}
request["searchType"] = loginType;
request["sharedPatientMobileNumber"] = mobileNo;
request["zipCode"] = countryCode;
request["isRegister"] = false;
request["patientStatus"] = 2;
request.searchType = loginType;
request.sharedPatientMobileNumber = mobileNo;
request.zipCode = countryCode;
request.isRegister = false;
request.patientStatus = 2;
request.isDentalAllowedBackend = false;
return request;
}
}

@ -629,8 +629,17 @@ class Utils {
double width = 24,
double height = 24,
BoxFit fit = BoxFit.cover,
double? border,
double? borderRadius,
}) {
return Image.asset(icon, width: width, height: height, fit: fit);
return Container(
decoration: BoxDecoration(
border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null,
borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 0) : null,
),
child: Image.asset(icon, width: width, height: height, fit: fit),
);
// return Image.asset(icon, width: width, height: height, fit: fit, );
}
/// Widget to build an SVG from network

@ -1,4 +1,6 @@
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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:auto_size_text/auto_size_text.dart';
@ -365,7 +367,8 @@ extension DynamicTextStyleExtension on BuildContext {
TextBaseline? textBaseline,
FontStyle? fontStyle,
bool isLanguageSwitcher = false}) {
final family = FontUtils.getFontFamilyForLanguage(false); // TODO: @Aamir make it dynamic based on app language
AppState appState = getIt.get<AppState>();
final family = appState.getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins';
return TextStyle(
fontFamily: family,
fontSize: fontSize,

@ -3,8 +3,10 @@ import 'dart:async';
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.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/resp_models/select_device_by_imei.dart';
@ -17,12 +19,18 @@ abstract class AuthenticationRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq});
Future<Either<Failure, GenericApiModel<dynamic>>> sendActivationCodeRepo({required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false});
Future<Either<Failure, GenericApiModel<dynamic>>> sendActivationCodeRepo({required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false, bool isFormFamilyFile = false});
Future<Either<Failure, GenericApiModel<dynamic>>> checkActivationCodeRepo(
{required dynamic newRequest, // could be CheckActivationCodeReq or CheckActivationCodeRegisterReq
{required dynamic newRequest,
required String? activationCode,
required bool isRegister});
required bool isRegister,
bool isFormFamilyFile = false,
int? patientShareRequestID,
int? responseID,
bool isSwitchUser = false,
int? patientID,
int? loginType});
Future<Either<Failure, GenericApiModel<dynamic>>> checkIfUserAgreed({required dynamic commonAuthanticatedRequest});
@ -134,18 +142,33 @@ class AuthenticationRepoImp implements AuthenticationRepo {
required dynamic sendActivationCodeReq,
String? languageID,
bool isRegister = false,
bool isFormFamilyFile = false,
}) async {
int isOutKsa = (sendActivationCodeReq.zipCode == '966' || sendActivationCodeReq.zipCode == '+966') ? 0 : 1;
sendActivationCodeReq.patientOutSA = isOutKsa;
sendActivationCodeReq.isDentalAllowedBackend = false;
final payload = sendActivationCodeReq.toJson();
if (isFormFamilyFile) {
payload.remove("MobileNo");
payload.remove("NationalID");
payload.remove("SMSSignature");
payload.remove("ResponseID");
print("=================== Final Payload ===================");
print(payload);
print("=====================================================");
}
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
isRegister ? ApiConsts.sendActivationCodeRegister : ApiConsts.sendActivationCode,
body: sendActivationCodeReq.toJson(),
isFormFamilyFile
? ApiConsts.sendFamilyFileActivation
: isRegister
? ApiConsts.sendActivationCodeRegister
: ApiConsts.sendActivationCode,
body: isFormFamilyFile ? payload : sendActivationCodeReq.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
@ -172,24 +195,122 @@ class AuthenticationRepoImp implements AuthenticationRepo {
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> checkActivationCodeRepo({
required dynamic newRequest, // could be CheckActivationCodeReq or CheckActivationCodeRegisterReq
required String? activationCode,
required bool isRegister,
}) async {
Future<Either<Failure, GenericApiModel<dynamic>>> checkActivationCodeRepo(
{required dynamic newRequest, // could be CheckActivationCodeReq or CheckActivationCodeRegisterReq
required String? activationCode,
required bool isRegister,
bool isFormFamilyFile = false,
int? patientShareRequestID,
int? responseID,
bool isSwitchUser = false,
int? patientID,
int? loginType}) async {
AppState appState = getIt.get<AppState>();
// if (isRegister) {
// newRequest["activationCode"] = activationCode ?? "0000";
// newRequest["isSilentLogin"] = activationCode != null ? false : true;
// } else {
// newRequest.activationCode = activationCode ?? "0000";
// newRequest.isSilentLogin = activationCode != null ? false : true;
// newRequest.projectOutSA = newRequest.zipCode == '966' ? false : true;
// newRequest.isDentalAllowedBackend = false;
// newRequest.forRegisteration = newRequest.isRegister ?? false;
// newRequest.isRegister = false;
// }
// Map<String, dynamic> familyRequest = {};
// if (isFormFamilyFile) {
// AppState appState = getIt.get<AppState>();
// familyRequest = {};
// familyRequest['PatientShareRequestID'] = patientShareRequestID;
// familyRequest['ResponseID'] = responseID;
// familyRequest['Status'] = 3;
// familyRequest["PatientID"] = appState.getAuthenticatedUser()!.patientId ?? 0;
// familyRequest["LogInTokenID"] = appState.getFamilyFileTokenID;
// familyRequest["activationCode"] = activationCode ?? "0000";
// familyRequest["PatientMobileNumber"] = newRequest.patientMobileNumber;
// familyRequest["PatientIdentificationID"] = newRequest.patientIdentificationID;
// }
// Map<String, dynamic> switchRequest = {};
// if (isSwitchUser) {
// switchRequest = newRequest.toJson();
//
// switchRequest['PatientID'] = responseID;
// switchRequest['IsSilentLogin'] = true;
// switchRequest['LogInTokenID'] = null;
// switchRequest['SearchType'] = 2;
// if (loginType != 0) {
// switchRequest['SuperUser'] = patientID;
// switchRequest['DeviceToken'] = null;
// } else {
// switchRequest["LoginType"] = 2;
// }
//
// if (appState.getSuperUserID == responseID) {
// // switchRequest['LoginType'] = 3;
// switchRequest['PatientIdentificationID'] = "";
// // switchRequest['ProjectOutSA'] = newRequest.zipCode == '966' ? false : true;
// switchRequest.remove('NationalID');
// switchRequest.remove('isDentalAllowedBackend');
// switchRequest.remove('ProjectOutSA');
// switchRequest.remove('ForRegisteration');
// appState.setSuperUserID = null;
// }
// }
if (isRegister) {
newRequest["activationCode"] = activationCode ?? "0000";
newRequest["isSilentLogin"] = activationCode != null ? false : true;
newRequest["isSilentLogin"] = activationCode == null;
} else {
newRequest.activationCode = activationCode ?? "0000";
newRequest.isSilentLogin = activationCode != null ? false : true;
newRequest.projectOutSA = newRequest.zipCode == '966' ? false : true;
newRequest.isSilentLogin = activationCode == null;
newRequest.projectOutSA = newRequest.zipCode != '966';
newRequest.isDentalAllowedBackend = false;
newRequest.forRegisteration = newRequest.isRegister ?? false;
newRequest.isRegister = false;
}
final endpoint = isRegister ? ApiConsts.checkActivationCodeRegister : ApiConsts.checkActivationCode;
Map<String, dynamic> familyRequest = {};
if (isFormFamilyFile) {
familyRequest = {
'PatientShareRequestID': patientShareRequestID,
'ResponseID': responseID,
'Status': 3,
// 'PatientID': appState.getAuthenticatedUser()?.patientId ?? 0,
'LogInTokenID': appState.getFamilyFileTokenID,
'activationCode': activationCode ?? "0000",
'PatientMobileNumber': newRequest.patientMobileNumber,
'PatientIdentificationID': newRequest.patientIdentificationID,
};
}
Map<String, dynamic> switchRequest = {};
if (isSwitchUser) {
switchRequest = newRequest.toJson();
switchRequest.addAll({
'PatientID': responseID,
'IsSilentLogin': true,
'LogInTokenID': null,
'SearchType': 2,
});
if (loginType != 0) {
switchRequest['SuperUser'] = patientID;
switchRequest['DeviceToken'] = null;
} else {
switchRequest['LoginType'] = 2;
}
if (appState.getSuperUserID == responseID) {
switchRequest['PatientIdentificationID'] = "";
switchRequest.removeWhere((key, value) => ['NationalID', 'isDentalAllowedBackend', 'ProjectOutSA', 'ForRegisteration'].contains(key));
}
}
final endpoint = isFormFamilyFile
? ApiConsts.checkActivationCodeForFamily
: isRegister
? ApiConsts.checkActivationCodeRegister
: ApiConsts.checkActivationCode;
try {
GenericApiModel<dynamic>? apiResponse;
@ -197,7 +318,13 @@ class AuthenticationRepoImp implements AuthenticationRepo {
await apiClient.post(
endpoint,
body: isRegister ? newRequest : newRequest.toJson(),
body: isFormFamilyFile
? familyRequest
: isRegister
? newRequest
: isSwitchUser
? switchRequest
: newRequest.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
@ -226,7 +353,6 @@ class AuthenticationRepoImp implements AuthenticationRepo {
@override
Future<Either<Failure, GenericApiModel<dynamic>>> checkIfUserAgreed({required dynamic commonAuthanticatedRequest}) async {
commonAuthanticatedRequest['Region'] = 1;
print("dsfsdd");
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
@ -261,7 +387,6 @@ class AuthenticationRepoImp implements AuthenticationRepo {
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getUserAgreementContent({required dynamic commonAuthanticatedRequest}) async {
commonAuthanticatedRequest['Region'] = 1;
print("dsfsdd");
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;

@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/core/common_models/nationality_country_model
import 'package:hmg_patient_app_new/core/common_models/privilege/HMCProjectListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/VidaPlusProjectListModel.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/loading_utils.dart';
import 'package:hmg_patient_app_new/core/utils/request_utils.dart';
@ -26,6 +27,9 @@ import 'package:hmg_patient_app_new/features/authentication/models/resp_models/a
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';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/authentication/saved_login_screen.dart';
@ -243,7 +247,7 @@ class AuthenticationViewModel extends ChangeNotifier {
await selectDeviceImei(onSuccess: (dynamic respData) async {
try {
if (respData != null) {
dynamic data = SelectDeviceByImeiRespModelElement.fromJson(respData.toJson());
dynamic data = await SelectDeviceByImeiRespModelElement.fromJson(respData.toJson());
_appState.setSelectDeviceByImeiRespModelElement(data);
LoaderBottomSheet.hideLoader();
@ -342,24 +346,33 @@ class AuthenticationViewModel extends ChangeNotifier {
);
}
Future<void> sendActivationCode(
{required OTPTypeEnum otpTypeEnum, required String nationalIdOrFileNumber, required String phoneNumber, required bool isForRegister, dynamic payload, bool isComingFromResendOTP = false}) async {
Future<void> sendActivationCode({required OTPTypeEnum otpTypeEnum,
required String nationalIdOrFileNumber,
required String phoneNumber,
required bool isForRegister,
dynamic payload,
bool isComingFromResendOTP = false,
bool isExcludedUser = false,
bool isFormFamilyFile = false,
int? responseID}) async {
var request = RequestUtils.getCommonRequestSendActivationCode(
otpTypeEnum: otpTypeEnum,
mobileNumber: phoneNumber,
selectedLoginType: otpTypeEnum.toInt(),
zipCode: selectedCountrySignup.countryCode,
nationalId: int.parse(nationalIdOrFileNumber),
isFileNo: isForRegister ? isPatientHasFile(request: payload) : false,
patientId: 0,
isForRegister: isForRegister,
patientOutSA: isForRegister
? isPatientOutsideSA(request: payload)
: selectedCountrySignup.countryCode == CountryEnum.saudiArabia
? false
: true,
payload: payload,
);
otpTypeEnum: otpTypeEnum,
mobileNumber: phoneNumber,
selectedLoginType: otpTypeEnum.toInt(),
zipCode: selectedCountrySignup.countryCode,
nationalId: int.parse(nationalIdOrFileNumber),
isFileNo: isForRegister ? isPatientHasFile(request: payload) : false,
patientId: 0,
isForRegister: isForRegister,
patientOutSA: isForRegister
? isPatientOutsideSA(request: payload)
: selectedCountrySignup.countryCode == CountryEnum.saudiArabia.countryCode
? false
: true,
payload: payload,
isExcludedUser: isExcludedUser,
isFormFamilyFile: isFormFamilyFile,
responseID: responseID);
// TODO: GET APP SMS SIGNATURE HERE
request.sMSSignature = await getSignature();
@ -368,11 +381,17 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState.setUserRegistrationPayload = RegistrationDataModelPayload.fromJson(payload);
}
final resultEither = await _authenticationRepo.sendActivationCodeRepo(sendActivationCodeReq: request, isRegister: checkIsUserComingForRegister(request: payload), languageID: 'er');
final resultEither = await _authenticationRepo.sendActivationCodeRepo(
sendActivationCodeReq: request, isRegister: checkIsUserComingForRegister(request: payload), languageID: 'er', isFormFamilyFile: isFormFamilyFile);
resultEither.fold(
(failure) async => await _errorHandlerService.handleError(failure: failure),
(apiResponse) async {
int? patientShareRequestID = 0;
if (isFormFamilyFile) {
patientShareRequestID = apiResponse.data['PatientShareRequestID'];
_appState.setFamilyFileTokenID = apiResponse.data['LogInTokenID'];
}
if (apiResponse.messageStatus == 2) {
LoaderBottomSheet.hideLoader();
await _dialogService.showCommonBottomSheetWithoutH(
@ -383,7 +402,18 @@ class AuthenticationViewModel extends ChangeNotifier {
} else {
if (apiResponse.data != null && apiResponse.data['isSMSSent'] == true) {
LoaderBottomSheet.hideLoader();
if (!isComingFromResendOTP) navigateToOTPScreen(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber, isComingFromRegister: checkIsUserComingForRegister(request: payload), payload: payload);
if (!isComingFromResendOTP) {
navigateToOTPScreen(
otpTypeEnum: otpTypeEnum,
phoneNumber: phoneNumber,
isComingFromRegister: checkIsUserComingForRegister(request: payload),
payload: payload,
isFormFamilyFile: isFormFamilyFile,
isExcludedUser: isExcludedUser,
responseID: responseID,
patientShareRequestID: patientShareRequestID,
);
}
} else {
// TODO: Handle isSMSSent false
// navigateToOTPScreen(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber);
@ -401,8 +431,17 @@ class AuthenticationViewModel extends ChangeNotifier {
return isUserComingForRegister;
}
Future<void> checkActivationCode(
{required String? activationCode, required OTPTypeEnum otpTypeEnum, required Function(String? message) onWrongActivationCode, Function()? onResendActivation}) async {
Future<void> checkActivationCode({
required String? activationCode,
required OTPTypeEnum otpTypeEnum,
required Function(String? message) onWrongActivationCode,
Function()? onResendActivation,
bool isFormFamilyFile = false,
dynamic patientShareRequestID,
dynamic responseID,
bool isSwitchUser = false,
int? patientID,
}) async {
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1);
final request = RequestUtils.getCommonRequestWelcome(
@ -428,6 +467,7 @@ class AuthenticationViewModel extends ChangeNotifier {
//TODO: Error Here IN Zip Code.
loginType: loginTypeEnum.toInt)
.toJson();
LoaderBottomSheet.showLoader();
if (isForRegister) {
if (_appState.getUserRegistrationPayload.patientOutSa == 0) request['DOB'] = _appState.getUserRegistrationPayload.dob;
@ -463,7 +503,17 @@ class AuthenticationViewModel extends ChangeNotifier {
}
});
} else {
final resultEither = await _authenticationRepo.checkActivationCodeRepo(newRequest: CheckActivationCodeRegisterReq.fromJson(request), activationCode: activationCode, isRegister: false);
final resultEither = await _authenticationRepo.checkActivationCodeRepo(
newRequest: CheckActivationCodeRegisterReq.fromJson(request),
activationCode: activationCode,
isRegister: false,
isFormFamilyFile: isFormFamilyFile,
patientShareRequestID: patientShareRequestID,
responseID: responseID,
isSwitchUser: isSwitchUser,
patientID: patientID,
loginType: _appState.getSuperUserID != null ? 0 : 2,
);
resultEither.fold(
(failure) async => await _errorHandlerService.handleError(
@ -494,48 +544,86 @@ class AuthenticationViewModel extends ChangeNotifier {
// Navigator.popUntil(context, (route) => Utils.route(route, equalsTo: RegisterNew));
return;
} 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;
final request = RequestUtils.getAuthanticatedCommonRequest().toJson();
bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request);
//updating the last login type in app state to show the fingerprint/face id option on home screen
if (_appState.getSelectDeviceByImeiRespModelElement != null) {
_appState.getSelectDeviceByImeiRespModelElement!.logInType = loginTypeEnum.toInt;
}
LoaderBottomSheet.hideLoader();
insertPatientIMEIData(loginTypeEnum.toInt);
await clearDefaultInputValues();
if (isUserAgreedBefore) {
navigateToHomeScreen();
if (isFormFamilyFile) {
// await navigateToFamilyFilePage();
MedicalFileViewModel medicalVm = getIt<MedicalFileViewModel>();
if (!_appState.getIsChildLoggedIn) {
await medicalVm.getFamilyFiles(status: 0);
await medicalVm.getAllPendingRecordsByResponseId();
_navigationService.popUntilNamed(AppRoutes.landingScreen);
}
} else {
navigateToHomeScreen();
//Agreement page not designed yet so we are navigating to home screen directly
// getUserAgreementContent(request: request);
if (activation.list != null && activation.list!.isNotEmpty) {
if (isSwitchUser) {
if (_appState.getIsChildLoggedIn) {
_appState.setSuperUserID = null;
_appState.setIsChildLoggedIn = false;
activation.list!.first.isParentUser = true;
} else {
_appState.setSuperUserID = _appState.getAuthenticatedUser()?.patientId;
_appState.setIsChildLoggedIn = true;
activation.list!.first.isParentUser = false;
}
} else {
activation.list!.first.isParentUser = true;
}
activation.list!.first.bloodGroup = activation.patientBlodType;
_appState.setAuthenticatedUser(activation.list!.first);
_appState.setPrivilegeModelList(activation.list!.first.listPrivilege!);
}
// _appState.setUserBloodGroup = (activation.patientBlodType ?? "");
_appState.setAppAuthToken = activation.authenticationTokenId;
final request = await RequestUtils.getAuthanticatedCommonRequest().toJson();
bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request);
//updating the last login type in app state to show the fingerprint/face id option on home screen
if (_appState.getSelectDeviceByImeiRespModelElement != null) {
_appState.getSelectDeviceByImeiRespModelElement!.logInType = loginTypeEnum.toInt;
}
LoaderBottomSheet.hideLoader();
//
if (!isSwitchUser && !_appState.getIsChildLoggedIn) {
MedicalFileViewModel medicalVm = getIt<MedicalFileViewModel>();
await insertPatientIMEIData(loginTypeEnum.toInt);
await medicalVm.getFamilyFiles(status: 0); //TODO: Remove status: 1 by Aamir Need to Discuss With Sultan
await medicalVm.getAllPendingRecordsByResponseId();
}
await clearDefaultInputValues();
if (isUserAgreedBefore) {
navigateToHomeScreen();
} else {
MyAppointmentsViewModel myAppointmentsVM = getIt<MyAppointmentsViewModel>();
myAppointmentsVM.setIsAppointmentDataToBeLoaded(true);
navigateToHomeScreen();
//Agreement page not designed yet so we are navigating to home screen directly
// getUserAgreementContent(request: request);
}
// TODO: setPreferences and stuff
// sharedPref.remove(FAMILY_FILE);
// activation.list!.isFamily = false;
// userData = activation.list;
// sharedPref.setString(BLOOD_TYPE, activation.patientBloodType ?? "");
// authenticatedUserObject.user = activation.list!;
// projectViewModel.setPrivilege(privilegeList: res);
// await sharedPref.setObject(MAIN_USER, activation.list);
// await sharedPref.setObject(USER_PROFILE, activation.list);
// loginTokenID = activation.logInTokenID;
// await sharedPref.setObject(LOGIN_TOKEN_ID, activation.logInTokenID);
// await sharedPref.setString(TOKEN, activation.authenticationTokenID!);
// projectViewModel.analytics.loginRegistration.login_successful();
}
// TODO: setPreferences and stuff
// sharedPref.remove(FAMILY_FILE);
// activation.list!.isFamily = false;
// userData = activation.list;
// sharedPref.setString(BLOOD_TYPE, activation.patientBloodType ?? "");
// authenticatedUserObject.user = activation.list!;
// projectViewModel.setPrivilege(privilegeList: res);
// await sharedPref.setObject(MAIN_USER, activation.list);
// await sharedPref.setObject(USER_PROFILE, activation.list);
// loginTokenID = activation.logInTokenID;
// await sharedPref.setObject(LOGIN_TOKEN_ID, activation.logInTokenID);
// await sharedPref.setString(TOKEN, activation.authenticationTokenID!);
// projectViewModel.analytics.loginRegistration.login_successful();
}
});
}
}
Future<void> navigateToFamilyFilePage() async {
MedicalFileViewModel medicalFileVM = GetIt.instance<MedicalFileViewModel>();
medicalFileVM.handleFamilyFileRequestOTPVerification();
}
Future<bool> checkIfUserAgreedBefore({required dynamic request}) async {
bool isAgreed = false;
if (havePrivilege(109)) {
@ -573,13 +661,25 @@ class AuthenticationViewModel extends ChangeNotifier {
_navigationService.pushAndReplace(AppRoutes.landingScreen);
}
Future<void> navigateToOTPScreen({required OTPTypeEnum otpTypeEnum, required String phoneNumber, required bool isComingFromRegister, dynamic payload}) async {
Future<void> navigateToOTPScreen(
{required OTPTypeEnum otpTypeEnum,
required String phoneNumber,
required bool isComingFromRegister,
dynamic payload,
bool isFormFamilyFile = false,
bool isExcludedUser = false,
int? responseID,
int? patientShareRequestID}) async {
_navigationService.pushToOtpScreen(
phoneNumber: phoneNumber,
isFormFamilyFile: isFormFamilyFile,
checkActivationCode: (int activationCode) async {
await checkActivationCode(
activationCode: activationCode.toString(),
isFormFamilyFile: isFormFamilyFile,
otpTypeEnum: otpTypeEnum,
responseID: responseID,
patientShareRequestID: patientShareRequestID,
onWrongActivationCode: (String? value) {
onWrongActivationCode(message: value);
},
@ -587,12 +687,16 @@ class AuthenticationViewModel extends ChangeNotifier {
},
onResendOTPPressed: (String phoneNumber) async {
await sendActivationCode(
otpTypeEnum: otpTypeEnum,
phoneNumber: phoneNumberController.text,
nationalIdOrFileNumber: nationalIdController.text,
isForRegister: isComingFromRegister,
isComingFromResendOTP: true,
payload: payload);
otpTypeEnum: otpTypeEnum,
phoneNumber: phoneNumberController.text,
nationalIdOrFileNumber: nationalIdController.text,
isForRegister: isComingFromRegister,
isComingFromResendOTP: true,
payload: payload,
isFormFamilyFile: isFormFamilyFile,
isExcludedUser: isExcludedUser,
responseID: responseID,
);
},
);
}

@ -69,6 +69,8 @@ class AuthenticatedUser {
dynamic authenticatedUserPatientPayType;
dynamic authenticatedUserPatientType;
dynamic authenticatedUserStatus;
int? superUser;
bool? isParentUser;
AuthenticatedUser({
this.setupId,
@ -139,6 +141,8 @@ class AuthenticatedUser {
this.authenticatedUserPatientPayType,
this.authenticatedUserPatientType,
this.authenticatedUserStatus,
this.superUser,
this.isParentUser,
});
factory AuthenticatedUser.fromRawJson(String str) => AuthenticatedUser.fromJson(json.decode(str));
@ -146,146 +150,150 @@ class AuthenticatedUser {
String toRawJson() => json.encode(toJson());
factory AuthenticatedUser.fromJson(Map<String, dynamic> json) => AuthenticatedUser(
setupId: json["SetupID"],
patientType: json["PatientType"],
patientId: json["PatientID"],
firstName: json["FirstName"],
middleName: json["MiddleName"],
lastName: json["LastName"],
firstNameN: json["FirstNameN"],
middleNameN: json["MiddleNameN"],
lastNameN: json["LastNameN"],
relationshipId: json["RelationshipID"],
gender: json["Gender"],
dateofBirth: json["DateofBirth"],
dateofBirthN: json["DateofBirthN"],
nationalityId: json["NationalityID"],
phoneResi: json["PhoneResi"],
phoneOffice: json["PhoneOffice"],
mobileNumber: json["MobileNumber"],
faxNumber: json["FaxNumber"],
emailAddress: json["EmailAddress"],
bloodGroup: json["BloodGroup"],
rhFactor: json["RHFactor"],
isEmailAlertRequired: json["IsEmailAlertRequired"],
isSmsAlertRequired: json["IsSMSAlertRequired"],
preferredLanguage: json["PreferredLanguage"],
isPrivilegedMember: json["IsPrivilegedMember"],
memberId: json["MemberID"],
expiryDate: json["ExpiryDate"],
isHmgEmployee: json["IsHmgEmployee"],
employeeId: json["EmployeeID"],
emergencyContactName: json["EmergencyContactName"],
emergencyContactNo: json["EmergencyContactNo"],
patientPayType: json["PatientPayType"],
dhccPatientRefId: json["DHCCPatientRefID"],
isPatientDummy: json["IsPatientDummy"],
status: json["Status"],
isStatusCleared: json["IsStatusCleared"],
patientIdentificationType: json["PatientIdentificationType"],
patientIdentificationNo: json["PatientIdentificationNo"],
projectId: json["ProjectID"],
infoSourceId: json["InfoSourceID"],
address: json["Address"],
age: json["Age"],
ageDesc: json["AgeDesc"],
areaId: json["AreaID"],
crsVerificationStatus: json["CRSVerificationStatus"],
crsVerificationStatusDesc: json["CRSVerificationStatusDesc"],
crsVerificationStatusDescN: json["CRSVerificationStatusDescN"],
createdBy: json["CreatedBy"],
genderDescription: json["GenderDescription"],
healthIdFromNhicViaVida: json["HealthIDFromNHICViaVida"],
ir: json["IR"],
isoCityId: json["ISOCityID"],
isoCountryId: json["ISOCountryID"],
isVerfiedFromNhic: json["IsVerfiedFromNHIC"],
listPrivilege: json["ListPrivilege"] == null ? [] : List<ListPrivilege>.from(json["ListPrivilege"]!.map((x) => ListPrivilege.fromJson(x))),
marital: json["Marital"],
occupationId: json["OccupationID"],
outSa: json["OutSA"],
poBox: json["POBox"],
receiveHealthSummaryReport: json["ReceiveHealthSummaryReport"],
sourceType: json["SourceType"],
strDateofBirth: json["StrDateofBirth"],
tempAddress: json["TempAddress"],
zipCode: json["ZipCode"],
eHealthIdField: json["eHealthIDField"],
authenticatedUserPatientPayType: json["patientPayType"],
authenticatedUserPatientType: json["patientType"],
authenticatedUserStatus: json["status"],
);
setupId: json["SetupID"],
patientType: json["PatientType"],
patientId: json["PatientID"],
firstName: json["FirstName"],
middleName: json["MiddleName"],
lastName: json["LastName"],
firstNameN: json["FirstNameN"],
middleNameN: json["MiddleNameN"],
lastNameN: json["LastNameN"],
relationshipId: json["RelationshipID"],
gender: json["Gender"],
dateofBirth: json["DateofBirth"],
dateofBirthN: json["DateofBirthN"],
nationalityId: json["NationalityID"],
phoneResi: json["PhoneResi"],
phoneOffice: json["PhoneOffice"],
mobileNumber: json["MobileNumber"],
faxNumber: json["FaxNumber"],
emailAddress: json["EmailAddress"],
bloodGroup: json["BloodGroup"],
rhFactor: json["RHFactor"],
isEmailAlertRequired: json["IsEmailAlertRequired"],
isSmsAlertRequired: json["IsSMSAlertRequired"],
preferredLanguage: json["PreferredLanguage"],
isPrivilegedMember: json["IsPrivilegedMember"],
memberId: json["MemberID"],
expiryDate: json["ExpiryDate"],
isHmgEmployee: json["IsHmgEmployee"],
employeeId: json["EmployeeID"],
emergencyContactName: json["EmergencyContactName"],
emergencyContactNo: json["EmergencyContactNo"],
patientPayType: json["PatientPayType"],
dhccPatientRefId: json["DHCCPatientRefID"],
isPatientDummy: json["IsPatientDummy"],
status: json["Status"],
isStatusCleared: json["IsStatusCleared"],
patientIdentificationType: json["PatientIdentificationType"],
patientIdentificationNo: json["PatientIdentificationNo"],
projectId: json["ProjectID"],
infoSourceId: json["InfoSourceID"],
address: json["Address"],
age: json["Age"],
ageDesc: json["AgeDesc"],
areaId: json["AreaID"],
crsVerificationStatus: json["CRSVerificationStatus"],
crsVerificationStatusDesc: json["CRSVerificationStatusDesc"],
crsVerificationStatusDescN: json["CRSVerificationStatusDescN"],
createdBy: json["CreatedBy"],
genderDescription: json["GenderDescription"],
healthIdFromNhicViaVida: json["HealthIDFromNHICViaVida"],
ir: json["IR"],
isoCityId: json["ISOCityID"],
isoCountryId: json["ISOCountryID"],
isVerfiedFromNhic: json["IsVerfiedFromNHIC"],
listPrivilege: json["ListPrivilege"] == null ? [] : List<ListPrivilege>.from(json["ListPrivilege"]!.map((x) => ListPrivilege.fromJson(x))),
marital: json["Marital"],
occupationId: json["OccupationID"],
outSa: json["OutSA"],
poBox: json["POBox"],
receiveHealthSummaryReport: json["ReceiveHealthSummaryReport"],
sourceType: json["SourceType"],
strDateofBirth: json["StrDateofBirth"],
tempAddress: json["TempAddress"],
zipCode: json["ZipCode"],
eHealthIdField: json["eHealthIDField"],
authenticatedUserPatientPayType: json["patientPayType"],
authenticatedUserPatientType: json["patientType"],
authenticatedUserStatus: json["status"],
superUser: json["superUser"],
isParentUser: json["isParentUser"] ?? false,
);
Map<String, dynamic> toJson() => {
"SetupID": setupId,
"PatientType": patientType,
"PatientID": patientId,
"FirstName": firstName,
"MiddleName": middleName,
"LastName": lastName,
"FirstNameN": firstNameN,
"MiddleNameN": middleNameN,
"LastNameN": lastNameN,
"RelationshipID": relationshipId,
"Gender": gender,
"DateofBirth": dateofBirth,
"DateofBirthN": dateofBirthN,
"NationalityID": nationalityId,
"PhoneResi": phoneResi,
"PhoneOffice": phoneOffice,
"MobileNumber": mobileNumber,
"FaxNumber": faxNumber,
"EmailAddress": emailAddress,
"BloodGroup": bloodGroup,
"RHFactor": rhFactor,
"IsEmailAlertRequired": isEmailAlertRequired,
"IsSMSAlertRequired": isSmsAlertRequired,
"PreferredLanguage": preferredLanguage,
"IsPrivilegedMember": isPrivilegedMember,
"MemberID": memberId,
"ExpiryDate": expiryDate,
"IsHmgEmployee": isHmgEmployee,
"EmployeeID": employeeId,
"EmergencyContactName": emergencyContactName,
"EmergencyContactNo": emergencyContactNo,
"PatientPayType": patientPayType,
"DHCCPatientRefID": dhccPatientRefId,
"IsPatientDummy": isPatientDummy,
"Status": status,
"IsStatusCleared": isStatusCleared,
"PatientIdentificationType": patientIdentificationType,
"PatientIdentificationNo": patientIdentificationNo,
"ProjectID": projectId,
"InfoSourceID": infoSourceId,
"Address": address,
"Age": age,
"AgeDesc": ageDesc,
"AreaID": areaId,
"CRSVerificationStatus": crsVerificationStatus,
"CRSVerificationStatusDesc": crsVerificationStatusDesc,
"CRSVerificationStatusDescN": crsVerificationStatusDescN,
"CreatedBy": createdBy,
"GenderDescription": genderDescription,
"HealthIDFromNHICViaVida": healthIdFromNhicViaVida,
"IR": ir,
"ISOCityID": isoCityId,
"ISOCountryID": isoCountryId,
"IsVerfiedFromNHIC": isVerfiedFromNhic,
"ListPrivilege": listPrivilege == null ? [] : List<dynamic>.from(listPrivilege!.map((x) => x.toJson())),
"Marital": marital,
"OccupationID": occupationId,
"OutSA": outSa,
"POBox": poBox,
"ReceiveHealthSummaryReport": receiveHealthSummaryReport,
"SourceType": sourceType,
"StrDateofBirth": strDateofBirth,
"TempAddress": tempAddress,
"ZipCode": zipCode,
"eHealthIDField": eHealthIdField,
"patientPayType": authenticatedUserPatientPayType,
"patientType": authenticatedUserPatientType,
"status": authenticatedUserStatus,
};
"SetupID": setupId,
"PatientType": patientType,
"PatientID": patientId,
"FirstName": firstName,
"MiddleName": middleName,
"LastName": lastName,
"FirstNameN": firstNameN,
"MiddleNameN": middleNameN,
"LastNameN": lastNameN,
"RelationshipID": relationshipId,
"Gender": gender,
"DateofBirth": dateofBirth,
"DateofBirthN": dateofBirthN,
"NationalityID": nationalityId,
"PhoneResi": phoneResi,
"PhoneOffice": phoneOffice,
"MobileNumber": mobileNumber,
"FaxNumber": faxNumber,
"EmailAddress": emailAddress,
"BloodGroup": bloodGroup,
"RHFactor": rhFactor,
"IsEmailAlertRequired": isEmailAlertRequired,
"IsSMSAlertRequired": isSmsAlertRequired,
"PreferredLanguage": preferredLanguage,
"IsPrivilegedMember": isPrivilegedMember,
"MemberID": memberId,
"ExpiryDate": expiryDate,
"IsHmgEmployee": isHmgEmployee,
"EmployeeID": employeeId,
"EmergencyContactName": emergencyContactName,
"EmergencyContactNo": emergencyContactNo,
"PatientPayType": patientPayType,
"DHCCPatientRefID": dhccPatientRefId,
"IsPatientDummy": isPatientDummy,
"Status": status,
"IsStatusCleared": isStatusCleared,
"PatientIdentificationType": patientIdentificationType,
"PatientIdentificationNo": patientIdentificationNo,
"ProjectID": projectId,
"InfoSourceID": infoSourceId,
"Address": address,
"Age": age,
"AgeDesc": ageDesc,
"AreaID": areaId,
"CRSVerificationStatus": crsVerificationStatus,
"CRSVerificationStatusDesc": crsVerificationStatusDesc,
"CRSVerificationStatusDescN": crsVerificationStatusDescN,
"CreatedBy": createdBy,
"GenderDescription": genderDescription,
"HealthIDFromNHICViaVida": healthIdFromNhicViaVida,
"IR": ir,
"ISOCityID": isoCityId,
"ISOCountryID": isoCountryId,
"IsVerfiedFromNHIC": isVerfiedFromNhic,
"ListPrivilege": listPrivilege == null ? [] : List<dynamic>.from(listPrivilege!.map((x) => x.toJson())),
"Marital": marital,
"OccupationID": occupationId,
"OutSA": outSa,
"POBox": poBox,
"ReceiveHealthSummaryReport": receiveHealthSummaryReport,
"SourceType": sourceType,
"StrDateofBirth": strDateofBirth,
"TempAddress": tempAddress,
"ZipCode": zipCode,
"eHealthIDField": eHealthIdField,
"patientPayType": authenticatedUserPatientPayType,
"patientType": authenticatedUserPatientType,
"status": authenticatedUserStatus,
"superUser": superUser,
"isParentUser": isParentUser,
};
}
class ListPrivilege {
@ -306,16 +314,16 @@ class ListPrivilege {
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"],
);
id: json["ID"],
serviceName: json["ServiceName"],
previlege: json["Previlege"],
region: json["Region"],
);
Map<String, dynamic> toJson() => {
"ID": id,
"ServiceName": serviceName,
"Previlege": previlege,
"Region": region,
};
"ID": id,
"ServiceName": serviceName,
"Previlege": previlege,
"Region": region,
};
}

@ -428,13 +428,9 @@ class OTPVerificationScreen extends StatefulWidget {
final String phoneNumber;
final Function(int code) checkActivationCode;
final Function(String phoneNumber) onResendOTPPressed;
final bool isFormFamilyFile;
const OTPVerificationScreen({
super.key,
required this.phoneNumber,
required this.checkActivationCode,
required this.onResendOTPPressed,
});
const OTPVerificationScreen({super.key, required this.phoneNumber, required this.checkActivationCode, required this.onResendOTPPressed, required this.isFormFamilyFile});
@override
State<OTPVerificationScreen> createState() => _OTPVerificationScreenState();
@ -559,7 +555,7 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
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),
(widget.isFormFamilyFile ? LoginTypeEnum.sms.displayName : 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),

@ -0,0 +1,57 @@
import 'dart:convert';
class FamilyFileRequest {
int? sharedPatientId;
String? sharedPatientIdentificationId;
int? searchType;
String? sharedPatientMobileNumber;
String? zipCode;
bool? isRegister;
int? patientStatus;
bool? isDentalAllowedBackend;
bool? isPatientExcluded;
int? responseID;
FamilyFileRequest({
this.sharedPatientId,
this.sharedPatientIdentificationId,
this.searchType,
this.sharedPatientMobileNumber,
this.zipCode,
this.isRegister,
this.patientStatus,
this.isDentalAllowedBackend,
this.isPatientExcluded,
this.responseID,
});
factory FamilyFileRequest.fromRawJson(String str) => FamilyFileRequest.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory FamilyFileRequest.fromJson(Map<String, dynamic> json) => FamilyFileRequest(
sharedPatientId: json["sharedPatientID"],
sharedPatientIdentificationId: json["sharedPatientIdentificationID"],
searchType: json["searchType"],
sharedPatientMobileNumber: json["sharedPatientMobileNumber"],
zipCode: json["zipCode"],
isRegister: json["isRegister"],
patientStatus: json["patientStatus"],
isDentalAllowedBackend: json["isDentalAllowedBackend"],
isPatientExcluded: json["IsPatientExcluded"],
responseID: json["ReponseID"],
);
Map<String, dynamic> toJson() => {
"SharedPatientID": sharedPatientId,
"SharedPatientIdentificationID": sharedPatientIdentificationId,
"SearchType": searchType,
"SharedPatientMobileNumber": sharedPatientMobileNumber,
"zipCode": zipCode,
"isRegister": isRegister,
"PatientStatus": patientStatus,
"isDentalAllowedBackend": isDentalAllowedBackend,
"IsPatientExcluded": isPatientExcluded,
"ReponseID": responseID,
};
}

@ -25,13 +25,19 @@ abstract class MedicalFileRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser);
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getPatientFamilyFiles();
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getPatientFamilyFiles(int? status, int patientId);
Future<Either<Failure, GenericApiModel<List<dynamic>>>> addFamilyFile({required dynamic request});
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getAllPendingRecordsByResponseId({required Map<String, dynamic> request});
Future<Either<Failure, GenericApiModel<dynamic>>> addFamilyFile({required dynamic request});
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointmentsForMedicalReport();
Future<Either<Failure, GenericApiModel<dynamic>>> insertRequestForMedicalReport({required PatientAppointmentHistoryResponseModel appointmentHistoryResponseModel});
Future<Either<Failure, GenericApiModel<dynamic>>> removeFamilyFile({required int? id});
Future<Either<Failure, GenericApiModel<dynamic>>> acceptRejectFamilyFile({required int? id, required int? status});
}
class MedicalFileRepoImp implements MedicalFileRepo {
@ -279,13 +285,13 @@ class MedicalFileRepoImp implements MedicalFileRepo {
}
@override
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getPatientFamilyFiles() async {
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getPatientFamilyFiles(int? status, int patientID) async {
try {
GenericApiModel<List<FamilyFileResponseModelLists>>? apiResponse;
Failure? failure;
await apiClient.post(
FAMILY_FILES,
body: {"Status": 3},
ApiConsts.getAllSharedRecordsByStatus,
body: {if (status != null) "Status": status, "PatientID": patientID},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
@ -318,9 +324,44 @@ class MedicalFileRepoImp implements MedicalFileRepo {
}
@override
Future<Either<Failure, GenericApiModel<List<dynamic>>>> addFamilyFile({dynamic request}) async {
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getAllPendingRecordsByResponseId({required Map<String, dynamic> request}) async {
try {
GenericApiModel<List<dynamic>>? apiResponse;
GenericApiModel<List<FamilyFileResponseModelLists>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getAllPendingRecordsByResponseId,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['GetAllPendingRecordsList'];
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<dynamic>>> addFamilyFile({dynamic request}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.addFamilyFile,
@ -330,17 +371,82 @@ class MedicalFileRepoImp implements MedicalFileRepo {
},
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,
// );
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response["ShareFamilyFileObj"] ?? null,
);
} 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<dynamic>>> removeFamilyFile({required int? id}) async {
Map<String, dynamic> request = {};
request["ID"] = id;
request['IsActive'] = false;
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.removeFileFromFamilyMembers,
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<dynamic>>> acceptRejectFamilyFile({required int? id, required int? status}) async {
Map<String, dynamic> request = {};
request["ID"] = id;
request['Status'] = status;
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.acceptAndRejectFamilyFile,
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());
}

@ -1,7 +1,15 @@
import 'dart:convert';
import 'dart:developer';
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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/request_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_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/common/models/family_file_request.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';
@ -10,6 +18,8 @@ import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_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/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
class MedicalFileViewModel extends ChangeNotifier {
int selectedTabIndex = 0;
@ -34,13 +44,26 @@ class MedicalFileViewModel extends ChangeNotifier {
PatientAppointmentHistoryResponseModel? patientMedicalReportSelectedAppointment;
List<FamilyFileResponseModelLists> patientFamilyFiles = [];
List<FamilyFileResponseModelLists> pendingFamilyFiles = [];
String patientSickLeavePDFBase64 = "";
String patientMedicalReportPDFBase64 = "";
int selectedMedicalReportsTabIndex = 0;
int _selectedFamilyFileTabIndex = 0;
int get getSelectedFamilyFileTabIndex => _selectedFamilyFileTabIndex;
set setSelectedFamilyFileTabIndex(int value) {
if (_selectedFamilyFileTabIndex != value) {
_selectedFamilyFileTabIndex = value;
notifyListeners();
}
}
static final DialogService _dialogService = getIt.get<DialogService>();
AppState _appState = getIt<AppState>();
final AppState _appState = getIt<AppState>();
AuthenticationViewModel authVM = getIt.get<AuthenticationViewModel>();
MedicalFileViewModel({required this.medicalFileRepo, required this.errorHandlerService});
@ -64,6 +87,11 @@ class MedicalFileViewModel extends ChangeNotifier {
notifyListeners();
}
void onFamilyFileTabChange(int index) {
setSelectedFamilyFileTabIndex = index;
notifyListeners();
}
setIsPatientVaccineListLoading(bool isLoading) {
isPatientVaccineListLoading = isLoading;
notifyListeners();
@ -247,8 +275,8 @@ class MedicalFileViewModel extends ChangeNotifier {
);
}
Future<void> getFamilyFiles({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await medicalFileRepo.getPatientFamilyFiles();
Future<void> getFamilyFiles({int? status, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await medicalFileRepo.getPatientFamilyFiles(status, _appState.getSuperUserID != null ? _appState.getSuperUserID! : _appState.getAuthenticatedUser()!.patientId!);
result.fold(
(failure) async => await errorHandlerService.handleError(
@ -261,16 +289,85 @@ class MedicalFileViewModel extends ChangeNotifier {
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),
);
if (apiResponse.data != null) {
// Add current user as the first active family file
final currentUser = _appState.getAuthenticatedUser()!;
final currentUserFamilyFile = FamilyFileResponseModelLists(
patientId: currentUser.patientId,
patientName: '${currentUser.firstName!} ${currentUser.lastName!}',
isActive: true,
gender: currentUser.gender!,
responseId: currentUser.patientId,
age: currentUser.age,
mobileNumber: currentUser.mobileNumber,
patientIdenficationNumber: currentUser.patientIdentificationNo,
emaiLAddress: currentUser.emailAddress,
genderDescription: currentUser.genderDescription,
);
// Clear and start fresh with current user
patientFamilyFiles.clear();
patientFamilyFiles.add(currentUserFamilyFile);
final List<FamilyFileResponseModelLists> activeFamilyFiles = [];
final List<FamilyFileResponseModelLists> pendingFamilyFiles = [];
for (var element in apiResponse.data!) {
if (element.status == null) {
continue;
}
final isPending = element.status == FamilyFileEnum.pending.toInt || element.status == FamilyFileEnum.rejected.toInt;
final isActive = element.status == FamilyFileEnum.active.toInt;
if (!isPending && !isActive) {
continue;
}
final familyFile = FamilyFileResponseModelLists(
id: element.id,
patientId: element.patientId,
patientName: element.patientName!,
isActive: element.isActive,
gender: element.gender!,
responseId: element.responseId,
mobileNumber: element.mobileNumber,
age: element.age,
patientIdenficationNumber: element.patientIdenficationNumber,
relationship: element.relationship,
relationshipId: element.relationshipId,
relationshipN: element.relationshipN,
status: element.status,
statusDescription: element.statusDescription,
createdOn: element.createdOn,
editedOn: element.editedOn,
patientDataVerified: element.patientDataVerified,
regionId: element.regionId,
familyRegionId: element.familyRegionId,
genderDescription: element.genderDescription,
genderImage: element.genderImage,
emaiLAddress: element.emaiLAddress,
);
if (isPending) {
familyFile.isRequestFromMySide = true;
pendingFamilyFiles.add(familyFile);
}
if (isActive) {
activeFamilyFiles.add(familyFile);
}
}
for (var activeFile in activeFamilyFiles) {
if (!patientFamilyFiles.any((e) => e.responseId == activeFile.responseId)) {
patientFamilyFiles.add(activeFile);
}
}
this.pendingFamilyFiles.clear();
this.pendingFamilyFiles.addAll(pendingFamilyFiles);
}
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@ -280,39 +377,151 @@ class MedicalFileViewModel extends ChangeNotifier {
);
}
Future<void> switchFamilyFiles({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await medicalFileRepo.getPatientFamilyFiles();
Future<void> getAllPendingRecordsByResponseId() async {
AppState appState = getIt<AppState>();
final result = await medicalFileRepo.getAllPendingRecordsByResponseId(request: {'ResponseID': appState.getAuthenticatedUser()!.patientId ?? "0", "Status": 2});
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
(failure) async => await errorHandlerService.handleError(failure: failure),
(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);
if (apiResponse.data != null) {
final List<FamilyFileResponseModelLists> tempPendingFamilyFiles = [];
for (var element in apiResponse.data!) {
if (element.status != null && element.status == FamilyFileEnum.pending.toInt || element.status == FamilyFileEnum.active.toInt) {
tempPendingFamilyFiles.add(FamilyFileResponseModelLists(
id: element.id,
patientId: element.patientId,
patientName: element.patientName!,
isActive: element.status,
gender: element.gender,
responseId: element.patientId,
mobileNumber: element.mobileNumber,
age: element.age,
patientIdenficationNumber: element.patientIdenficationNumber,
relationship: element.relationship,
relationshipId: element.relationshipId,
relationshipN: element.relationshipN,
status: element.status,
statusDescription: element.statusDescription,
createdOn: element.createdOn,
editedOn: element.editedOn,
patientDataVerified: element.patientDataVerified,
regionId: element.regionId,
familyRegionId: element.familyRegionId,
genderDescription: element.genderDescription,
genderImage: element.genderImage,
emaiLAddress: element.emaiLAddress));
}
}
// pendingFamilyFiles.addAll(tempPendingFamilyFiles.where((element) => !pendingFamilyFiles.any((e) => e.responseId == element.responseId)));
pendingFamilyFiles.addAll(tempPendingFamilyFiles.where((element) => !pendingFamilyFiles.any((e) => e.patientId == element.patientId)));
}
notifyListeners();
}
},
);
}
Future<void> switchFamilyFiles({Function(dynamic)? onSuccess, int? responseID, int? patientID, String? phoneNumber, Function(String)? onError}) async {
authVM.phoneNumberController.text = phoneNumber!.startsWith("0") ? phoneNumber.replaceFirst("0", "") : phoneNumber;
await authVM.checkActivationCode(
activationCode: '0000',
otpTypeEnum: OTPTypeEnum.sms,
onWrongActivationCode: (String? str) {},
responseID: responseID,
isFormFamilyFile: false,
isSwitchUser: true,
patientID: patientID,
);
}
Future<void> addFamilyFile({required OTPTypeEnum otpTypeEnum}) async {
LoaderBottomSheet.showLoader();
AuthenticationViewModel authVM = getIt.get<AuthenticationViewModel>();
NavigationService navigationService = getIt.get<NavigationService>();
FamilyFileRequest request =
await RequestUtils.getAddFamilyRequest(nationalIDorFile: authVM.nationalIdController.text, mobileNo: authVM.phoneNumberController.text, countryCode: authVM.selectedCountrySignup.countryCode);
final resultEither = await medicalFileRepo.addFamilyFile(request: request.toJson());
resultEither.fold((failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.messageStatus == 2) {
LoaderBottomSheet.hideLoader();
_dialogService.showErrorBottomSheet(
message: apiResponse.errorMessage!,
onOkPressed: () {
navigationService.pop();
});
} else if (apiResponse.messageStatus == 1) {
if (apiResponse.data != null) {
request.isPatientExcluded = apiResponse.data["IsPatientExcluded"];
request.responseID = apiResponse.data["ReponseID"];
LoaderBottomSheet.hideLoader();
_dialogService.showExceptionBottomSheet(
message: apiResponse.data["Message"],
onOkPressed: () {
LoaderBottomSheet.showLoader();
authVM.sendActivationCode(
otpTypeEnum: otpTypeEnum,
nationalIdOrFileNumber: request.sharedPatientIdentificationId!,
phoneNumber: request.sharedPatientMobileNumber!,
isForRegister: false,
isExcludedUser: apiResponse.data['IsPatientExcluded'],
responseID: apiResponse.data["ReponseID"],
isFormFamilyFile: true);
},
onCancelPressed: () {
navigationService.pop();
});
}
}
});
}
Future<void> handleFamilyFileRequestOTPVerification() async {
LoaderBottomSheet.showLoader();
if (!_appState.getIsChildLoggedIn) {
await getFamilyFiles(status: 0);
await getAllPendingRecordsByResponseId();
}
LoaderBottomSheet.hideLoader();
}
Future<void> removeFileFromFamilyMembers({int? id}) async {
NavigationService navigationService = getIt.get<NavigationService>();
_dialogService.showExceptionBottomSheet(
message: "Remove this member?",
onOkPressed: () async {
LoaderBottomSheet.showLoader();
final result = await medicalFileRepo.removeFamilyFile(id: id);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
LoaderBottomSheet.hideLoader();
_dialogService.showErrorBottomSheet(
message: apiResponse.errorMessage!,
onOkPressed: () {
navigationService.pop();
});
} else if (apiResponse.messageStatus == 1) {
patientFamilyFiles.removeWhere((element) => element.id == id);
LoaderBottomSheet.hideLoader();
notifyListeners();
navigationService.pop();
}
},
);
},
onCancelPressed: () {
navigationService.pop();
});
}
Future<void> getPatientMedicalReportAppointmentsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientMedicalReportAppointmentHistoryList.clear();
notifyListeners();
@ -357,8 +566,33 @@ class MedicalFileViewModel extends ChangeNotifier {
);
}
Future<void> addFamilyFile() async {
final resultEither = await medicalFileRepo.addFamilyFile(request: {});
resultEither.fold((failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) async {});
Future<void> acceptRejectFileFromFamilyMembers({int? id, int? status}) async {
NavigationService navigationService = getIt.get<NavigationService>();
LoaderBottomSheet.showLoader();
final result = await medicalFileRepo.acceptRejectFamilyFile(id: id, status: status);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
LoaderBottomSheet.hideLoader();
_dialogService.showErrorBottomSheet(
message: apiResponse.errorMessage!,
onOkPressed: () {
navigationService.pop();
});
} else if (apiResponse.messageStatus == 1) {
// FamilyFileResponseModelLists moveProfile = pendingFamilyFiles.firstWhere((element) => element.id == patientID);
// moveProfile.status = 3;
// moveProfile.statusDescription = "Approved";
// patientFamilyFiles.add(moveProfile);
pendingFamilyFiles.removeWhere((element) => element.id == id);
//TODO: Call Api Here To Load Family Members
getFamilyFiles(status: 0);
getAllPendingRecordsByResponseId();
LoaderBottomSheet.hideLoader();
onFamilyFileTabChange(0);
}
},
);
}
}

@ -23,83 +23,90 @@ class FamilyFileResponseModelLists {
String? patientIdenficationNumber;
String? patientName;
String? statusDescription;
bool? isSuperUser = false;
bool? isRequestFromMySide;
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,
});
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,
this.isSuperUser,
this.isRequestFromMySide});
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"],
);
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"],
isSuperUser: json["isSuperUser"] ?? false,
isRequestFromMySide: json["isRequestFromMySide"] ?? false,
);
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,
};
"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,
"isSuperUser": isSuperUser,
"isRequestFromMySide": isRequestFromMySide,
};
}

@ -92,6 +92,9 @@ class MyAppointmentsViewModel extends ChangeNotifier {
}
setIsAppointmentDataToBeLoaded(bool val) {
if (val) {
isMyAppointmentsLoading = true;
}
isAppointmentDataToBeLoaded = val;
notifyListeners();
}
@ -112,9 +115,11 @@ class MyAppointmentsViewModel extends ChangeNotifier {
Future<void> getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isAppointmentDataToBeLoaded) return;
filteredAppointmentList.clear();
patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear();
patientArrivedAppointmentsHistoryList.clear();
notifyListeners();
final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: isActiveAppointment, isArrivedAppointments: isArrivedAppointments);
final resultArrived = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true);

@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/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/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';
@ -76,6 +77,11 @@ class _LandingPageState extends State<LandingPage> {
void initState() {
authVM = context.read<AuthenticationViewModel>();
habibWalletVM = context.read<HabibWalletViewModel>();
// myAppointmentsViewModel = context.read<MyAppointmentsViewModel>();
// prescriptionsViewModel = context.read<PrescriptionsViewModel>();
// insuranceViewModel = context.read<InsuranceViewModel>();
// immediateLiveCareViewModel = context.read<ImmediateLiveCareViewModel>();
authVM.savePushTokenToAppState();
if (mounted) {
authVM.checkLastLoginStatus(() {
@ -100,13 +106,12 @@ class _LandingPageState extends State<LandingPage> {
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
NavigationService navigationService = getIt.get<NavigationService>();
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context, listen: false);
insuranceViewModel = Provider.of<InsuranceViewModel>(context, listen: false);
immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: SingleChildScrollView(

@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/resp_model
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_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/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';
@ -32,6 +33,8 @@ import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_upd
import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/presentation/radiology/radiology_orders_page.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/medical_report/medical_reports_page.dart';
@ -75,6 +78,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
@override
void initState() {
appState = getIt.get<AppState>();
scheduleMicrotask(() {
if (appState.isAuthenticated) {
labViewModel.initLabProvider();
@ -94,116 +98,140 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false);
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
appState = getIt.get<AppState>();
NavigationService navigationService = getIt.get<NavigationService>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.medicalFile.tr(context: context),
isLeading: false,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
TextInputWidget(
labelText: LocaleKeys.search.tr(context: context),
hintText: "Type any record".needTranslation,
controller: TextEditingController(),
keyboardType: TextInputType.number,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
leadingIcon: AppAssets.student_card,
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),
Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText18(isBold: true),
SizedBox(height: 4.h),
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
icon: AppAssets.file_icon,
labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}",
),
AppCustomChipWidget(
icon: AppAssets.checkmark_icon,
labelText: LocaleKeys.verified.tr(context: context),
iconColor: AppColors.successColor,
),
],
),
],
)
],
),
SizedBox(height: 16.h),
Divider(color: AppColors.dividerColor, height: 1.h),
SizedBox(height: 16.h),
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
labelText: "${appState.getAuthenticatedUser()!.age} Years Old",
),
AppCustomChipWidget(
icon: AppAssets.blood_icon,
labelText: "${LocaleKeys.bloodType.tr(context: context)}: ${appState.getUserBloodGroup}",
iconColor: AppColors.primaryRedColor,
),
],
),
],
),
),
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),
Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) {
return Column(
return CollapsingListView(
title: "Medical File".needTranslation,
trailing: Row(
children: [
Wrap(spacing: -15, children: [
Utils.buildImgWithAssets(icon: AppAssets.babyGirlImg, width: 32.h, height: 32.h, border: 1.5.h, borderRadius: 50.h),
Utils.buildImgWithAssets(icon: AppAssets.femaleImg, width: 32.h, height: 32.h, border: 1.5.h, borderRadius: 50.h),
Utils.buildImgWithAssets(icon: AppAssets.male_img, width: 32.h, height: 32.h, border: 1.5.h, borderRadius: 50.h),
]),
SizedBox(width: 4.h),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
],
).onPress(() {
DialogService dialogService = getIt.get<DialogService>();
dialogService.showFamilyBottomSheetWithoutH(
label: "Who do you want to book for?".needTranslation,
message: "This clinic or doctor is only available for the below eligible profiles.".needTranslation,
onSwitchPress: (FamilyFileResponseModelLists profile) {
medicalFileViewModel.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber);
},
profiles: medicalFileViewModel.patientFamilyFiles);
}),
isLeading: false,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
TextInputWidget(
labelText: LocaleKeys.search.tr(context: context),
hintText: "Type any record".needTranslation,
controller: TextEditingController(),
keyboardType: TextInputType.number,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
leadingIcon: AppAssets.student_card,
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),
Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTabBar(
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
tabs: [
CustomTabBarModel(AppAssets.myFilesBottom, LocaleKeys.general.tr(context: context).needTranslation),
CustomTabBarModel(AppAssets.insurance, LocaleKeys.insurance.tr(context: context)),
CustomTabBarModel(AppAssets.requests, LocaleKeys.request.tr(context: context).needTranslation),
CustomTabBarModel(AppAssets.more, "More".needTranslation),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}"
.toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1),
SizedBox(height: 4.h),
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
icon: AppAssets.file_icon,
labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}",
onChipTap: () {
navigationService.pushPage(
page: FamilyMedicalScreen(
profiles: medicalFileViewModel.patientFamilyFiles,
onSelect: (FamilyFileResponseModelLists p1) {},
));
},
),
AppCustomChipWidget(
icon: AppAssets.checkmark_icon,
labelText: LocaleKeys.verified.tr(context: context),
iconColor: AppColors.successColor,
),
],
),
],
)
],
onTabChange: (index) {
medicalFileVM.onTabChanged(index);
},
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 24.h),
getSelectedTabData(medicalFileVM.selectedTabIndex),
),
SizedBox(height: 16.h),
Divider(color: AppColors.dividerColor, height: 1.h),
SizedBox(height: 16.h),
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
labelText: "${appState.getAuthenticatedUser()!.age} Years Old",
),
AppCustomChipWidget(
icon: AppAssets.blood_icon,
labelText: "${LocaleKeys.bloodType.tr(context: context)}: ${appState.getUserBloodGroup}",
iconColor: AppColors.primaryRedColor,
),
],
),
],
);
}),
],
),
),
),
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),
Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) {
return Column(
children: [
CustomTabBar(
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
tabs: [
CustomTabBarModel(AppAssets.myFilesBottom, LocaleKeys.general.tr(context: context).needTranslation),
CustomTabBarModel(AppAssets.insurance, LocaleKeys.insurance.tr(context: context)),
CustomTabBarModel(AppAssets.requests, LocaleKeys.request.tr(context: context).needTranslation),
CustomTabBarModel(AppAssets.more, "More".needTranslation),
],
onTabChange: (index) {
medicalFileVM.onTabChanged(index);
},
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 24.h),
getSelectedTabData(medicalFileVM.selectedTabIndex),
],
);
}),
],
),
),
);

@ -1,23 +1,29 @@
import 'dart:convert';
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/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/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/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/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/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';
import 'package:provider/provider.dart';
class FamilyMedicalScreen extends StatefulWidget {
final List<FamilyFileResponseModelLists> profiles;
@ -34,161 +40,95 @@ class FamilyMedicalScreen extends StatefulWidget {
}
class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
List<CustomTabBarModel> tabs = [CustomTabBarModel("", LocaleKeys.medicalFile.tr()), CustomTabBarModel("", LocaleKeys.request.tr())];
MedicalFileViewModel? medicalVM;
@override
void initState() {
super.initState();
medicalVM = context.read<MedicalFileViewModel>();
}
@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();
AppState appState = getIt.get<AppState>();
return CollapsingListView(
title: "My Medical File".needTranslation,
bottomChild: appState.getAuthenticatedUser()!.isParentUser!
? 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".needTranslation,
onPressed: () {
DialogService dialogService = getIt.get<DialogService>();
dialogService.showAddFamilyFileSheet(
label: "Add Family Member".needTranslation,
message: "Please fill the below field to add a new family member to your profile".needTranslation,
onVerificationPress: () {
medicalVM!.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
});
},
icon: AppAssets.add_icon,
height: 56.h,
fontSize: 16,
borderRadius: 12.h,
fontWeight: FontWeight.w500))
: SizedBox(),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTabBar(
activeBackgroundColor: AppColors.secondaryLightRedColor,
activeTextColor: AppColors.primaryRedColor,
tabs: appState.isChildLoggedIn
? [CustomTabBarModel(null, LocaleKeys.medicalFile.tr())]
: [CustomTabBarModel(null, LocaleKeys.medicalFile.tr()), CustomTabBarModel(null, LocaleKeys.request.tr())],
onTabChange: (index) {
medicalVM!.onFamilyFileTabChange(index);
},
icon: AppAssets.add_icon,
height: 56.h,
fontWeight: FontWeight.w600,
)),
),
SizedBox(height: 25.h),
Selector<MedicalFileViewModel, int>(selector: (_, model) => model.getSelectedFamilyFileTabIndex, builder: (context, selectedIndex, child) => getFamilyTabs(index: selectedIndex)),
SizedBox(height: 20.h),
],
).paddingSymmetrical(20, 0),
);
}
Future<void> showModelSheet() async {
AuthenticationViewModel authVm = getIt.get<AuthenticationViewModel>();
return await 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: () {});
Widget getFamilyTabs({required int index}) {
switch (index) {
case 0:
return FamilyCards(
profiles: medicalVM!.patientFamilyFiles,
onSelect: (FamilyFileResponseModelLists profile) {
medicalVM!.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber);
},
onRemove: (FamilyFileResponseModelLists profile) {
medicalVM!.removeFileFromFamilyMembers(id: profile.id);
},
isLeftAligned: true,
isShowDetails: true,
isShowRemoveButton: true,
);
case 1:
return FamilyCards(
profiles: medicalVM!.pendingFamilyFiles,
isRequestDesign: true,
onSelect: (FamilyFileResponseModelLists profile) {
medicalVM!.acceptRejectFileFromFamilyMembers(id: profile.id, status: 3);
},
onRemove: (FamilyFileResponseModelLists profile) {
medicalVM!.acceptRejectFileFromFamilyMembers(id: profile.id, status: 4);
},
isShowDetails: true,
);
default:
return SizedBox.shrink();
}
}
}

@ -10,6 +10,8 @@ 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/services/dialog_service.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/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
@ -17,10 +19,23 @@ 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 Function(FamilyFileResponseModelLists) onRemove;
final bool isShowDetails;
final bool isBottomSheet;
final bool isRequestDesign;
final bool isLeftAligned;
final bool isShowRemoveButton;
const FamilyCards({super.key, required this.profiles, required this.onSelect, this.isShowDetails = false, this.isBottomSheet = false});
const FamilyCards(
{super.key,
required this.profiles,
required this.onSelect,
required this.onRemove,
this.isShowDetails = false,
this.isBottomSheet = false,
this.isRequestDesign = false,
this.isLeftAligned = false,
this.isShowRemoveButton = false});
@override
State<FamilyCards> createState() => _FamilyCardsState();
@ -31,79 +46,294 @@ class _FamilyCardsState extends State<FamilyCards> {
@override
Widget build(BuildContext context) {
return GridView.builder(
DialogService dialogService = getIt.get<DialogService>();
if (widget.isRequestDesign) {
return Column(
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.alertSquare),
SizedBox(width: 8.h),
"Who can view my medical file ?".needTranslation.toText14(color: AppColors.textColor, isUnderLine: true, weight: FontWeight.w500).onPress(() {
dialogService.showFamilyBottomSheetWithoutHWithChild(
label: "Manage Family".needTranslation,
message: "",
child: manageFamily(),
onOkPressed: () {},
);
}),
SizedBox(width: 4.h),
Utils.buildSvgWithAssets(icon: AppAssets.arrowRight),
],
),
SizedBox(height: 24.h),
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: widget.profiles.where((profile) => profile.isRequestFromMySide ?? false).length,
itemBuilder: (context, index) {
final mySideProfiles = widget.profiles.where((profile) => profile.isRequestFromMySide ?? false).toList();
FamilyFileResponseModelLists profile = mySideProfiles[index];
return Container(
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Opacity(
opacity: 1.0,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
CustomChipWidget(
height: 30.h,
chipType: ChipTypeEnum.alert,
backgroundColor: profile.status == FamilyFileEnum.pending.toInt
? AppColors.alertLightColor.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.rejected.toInt
? AppColors.primaryRedColor.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.active.toInt
? AppColors.lightGreenColor
: AppColors.lightGrayBGColor,
chipText: profile.statusDescription ?? "N/A",
iconAsset: null,
isShowBorder: false,
borderRadius: 8.h,
textColor: profile.status == FamilyFileEnum.pending.toInt
? AppColors.alertLightColor
: profile.status == FamilyFileEnum.rejected.toInt
? AppColors.primaryRedColor
: profile.status == FamilyFileEnum.active.toInt
? AppColors.textGreenColor
: AppColors.alertColor),
SizedBox(height: 8.h),
Wrap(alignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, runAlignment: WrapAlignment.start, spacing: 0.h, children: [
(profile.patientName ?? "").toText14(isBold: false, isCenter: false, maxlines: 1, weight: FontWeight.w600),
(getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false))
.toText12(isBold: false, isCenter: false, maxLine: 1, fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
]),
SizedBox(height: 8.h),
CustomChipWidget(
height: 30.h,
chipType: ChipTypeEnum.alert,
backgroundColor: AppColors.lightGrayBGColor,
chipText: "Medical File: ${profile.responseId ?? "N/A"}",
iconAsset: null,
isShowBorder: false,
borderRadius: 8.h,
textColor: AppColors.textColor),
],
),
),
);
},
),
SizedBox(height: 20.h),
],
);
} else {
return GridView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: widget.profiles.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.h,
mainAxisSpacing: 10.h,
childAspectRatio: widget.isShowDetails ? 0.56.h : 0.65.h,
),
padding: EdgeInsets.only(bottom: 20.h),
itemBuilder: (context, index) {
final profile = widget.profiles[index];
final isActive = (profile.responseId == appState.getAuthenticatedUser()?.patientId);
final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false;
final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID);
return Container(
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Opacity(
opacity: isActive || profile.status == FamilyFileEnum.pending.toInt || !canSwitch ? 0.4 : 1.0, // Fade all content if active
child: Stack(
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Utils.buildImgWithAssets(
icon: profile.gender == null
? AppAssets.dummy_user
: 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").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600),
SizedBox(height: 8.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(
height: 4.h,
),
Spacer(),
CustomButton(
height: 40.h,
onPressed: () {
if (canSwitch) widget.onSelect(profile);
},
text: isActive ? "Active".needTranslation : "Switch".needTranslation,
backgroundColor: isActive || !canSwitch ? Colors.grey.shade200 : AppColors.secondaryLightRedColor,
borderColor: isActive || !canSwitch ? Colors.grey.shade200 : AppColors.secondaryLightRedColor,
textColor: isActive || !canSwitch ? AppColors.greyTextColor : AppColors.primaryRedColor,
fontSize: 13.h,
icon: isActive ? AppAssets.activeCheck : AppAssets.switch_user,
iconColor: isActive || !canSwitch ? (isActive ? null : AppColors.greyTextColor) : AppColors.primaryRedColor,
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0),
).paddingOnly(top: 0, bottom: 0),
],
),
if (widget.isShowRemoveButton) ...[
Positioned(
top: 0,
right: 0,
child: Utils.buildSvgWithAssets(icon: AppAssets.deleteIcon).onPress(() {
if (!isActive) widget.onRemove(profile);
}),
),
],
],
),
),
);
},
);
}
}
Widget manageFamily() {
NavigationService navigationService = getIt<NavigationService>();
return ListView.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,
),
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsetsGeometry.zero,
itemCount: widget.profiles.where((profile) => !(profile.isRequestFromMySide ?? false)).length,
itemBuilder: (context, index) {
final profile = widget.profiles[index];
final isActive = (profile.responseId == appState.getAuthenticatedUser()?.patientId);
final otherProfiles = widget.profiles.where((profile) => !(profile.isRequestFromMySide ?? false)).toList();
FamilyFileResponseModelLists profile = otherProfiles[index];
return Container(
margin: EdgeInsets.only(bottom: 12.h),
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
opacity: 1.0,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
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),
CustomChipWidget(
height: 30.h,
chipType: ChipTypeEnum.alert,
backgroundColor: profile.status == FamilyFileEnum.pending.toInt
? AppColors.alertLightColor.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.rejected.toInt
? AppColors.primaryRedColor.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.active.toInt
? AppColors.lightGreenColor
: AppColors.lightGrayBGColor,
chipText: profile.statusDescription ?? "N/A",
iconAsset: null,
isShowBorder: false,
borderRadius: 8.h,
textColor: profile.status == FamilyFileEnum.pending.toInt
? AppColors.alertLightColor
: profile.status == FamilyFileEnum.rejected.toInt
? AppColors.primaryRedColor
: profile.status == FamilyFileEnum.active.toInt
? AppColors.textGreenColor
: AppColors.alertColor,
),
SizedBox(height: 8.h),
Wrap(
alignment: WrapAlignment.start,
children: [
(profile.patientName ?? "").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600),
(getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)).toText14(
isBold: false,
isCenter: true,
maxlines: 1,
weight: FontWeight.w500,
color: AppColors.greyTextColor,
),
],
),
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),
height: 30.h,
chipType: ChipTypeEnum.alert,
backgroundColor: AppColors.lightGrayBGColor,
chipText: "Medical File: ${profile.patientId ?? "N/A".needTranslation}",
iconAsset: null,
isShowBorder: false,
borderRadius: 8.h,
textColor: AppColors.textColor,
),
SizedBox(height: 16.h),
Row(
children: [
profile.status == FamilyFileEnum.active.toInt
? SizedBox()
: Expanded(
child: CustomButton(
height: 40.h,
text: LocaleKeys.confirm.tr(),
onPressed: () {
navigationService.pop();
widget.onSelect(profile);
},
backgroundColor: AppColors.lightGreenButtonColor,
borderColor: AppColors.lightGreenButtonColor,
textColor: AppColors.textGreenColor,
icon: null,
),
),
profile.status == FamilyFileEnum.active.toInt ? SizedBox() : SizedBox(width: 8.h),
Expanded(
child: CustomButton(
height: 40.h,
text: profile.status == FamilyFileEnum.active.toInt ? LocaleKeys.removeMember.tr() : LocaleKeys.cancel.tr(),
onPressed: () {
navigationService.pop();
widget.onRemove(profile);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
icon: null,
iconColor: AppColors.primaryRedColor,
),
),
],
),
],
),
),
@ -111,4 +341,31 @@ class _FamilyCardsState extends State<FamilyCards> {
},
);
}
String getStatusTextByRequest(FamilyFileEnum status, bool isRequestFromMySide) {
switch (status) {
case FamilyFileEnum.active:
if (isRequestFromMySide) {
return "${status.displayName} your request to be your family member".needTranslation;
} else {
return "can view your file".needTranslation;
}
case FamilyFileEnum.pending:
if (isRequestFromMySide) {
return "has a request ${status.displayName} to be your family member".needTranslation;
} else {
return "wants to add you as their family member".needTranslation;
}
case FamilyFileEnum.rejected:
if (isRequestFromMySide) {
return "${status.displayName} your request to be your family member".needTranslation;
} else {
return "${status.displayName} your family member request".needTranslation;
}
case FamilyFileEnum.inactive:
return "Inactive".needTranslation;
default:
return "N/A".needTranslation;
}
}
}

@ -1,27 +1,31 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/widget/family_cards.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/widgets/common_bottom_sheet.dart';
class MyFamilySheet {
static Future<void> show(BuildContext context, List<FamilyFileResponseModelLists> familyLists, Function(FamilyFileResponseModelLists) onSelect) async {
return await showCommonBottomSheetWithoutHeight(
NavigationService navigationService = getIt<NavigationService>();
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),
'Please select a profile'.needTranslation.toText21(isBold: true),
'switch from the below list of medical file'.needTranslation.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
navigationService.pop();
onSelect(profile);
},
onRemove: (profile) {},
isBottomSheet: true),
callBackFunc: () {},
);

@ -1,3 +1,6 @@
import 'dart:convert';
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_swiper_view/flutter_swiper_view.dart';
@ -5,16 +8,22 @@ 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/date_util.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/int_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/features/habib_wallet/habib_wallet_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/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/app_language_change.dart';
@ -50,8 +59,6 @@ class _ProfileSettingsState extends State<ProfileSettings> {
int length = 3;
final SwiperController _controller = SwiperController();
int _index = 0;
@override
Widget build(BuildContext context) {
return CollapsingListView(
@ -61,27 +68,41 @@ class _ProfileSettingsState extends State<ProfileSettings> {
child: SingleChildScrollView(
padding: EdgeInsets.only(top: 24, bottom: 24),
physics: NeverScrollableScrollPhysics(),
child: Consumer<ProfileSettingsViewModel>(
builder: (context, model, child) {
child: Consumer2<ProfileSettingsViewModel, MedicalFileViewModel>(
builder: (context, profileVm, medicalVm, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Swiper(
itemCount: length,
itemCount: medicalVm.patientFamilyFiles.length,
layout: SwiperLayout.STACK,
loop: true,
itemWidth: MediaQuery.of(context).size.width - 42,
indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right,
controller: _controller,
itemHeight: 210 + 16,
itemHeight: 200.h,
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 FamilyCardWidget(isRootUser: true).paddingOnly(right: 16);
return FamilyCardWidget(
profile: medicalVm.patientFamilyFiles[index],
onAddFamilyMemberPress: () {
DialogService dialogService = getIt.get<DialogService>();
dialogService.showAddFamilyFileSheet(
label: "Add Family Member".needTranslation,
message: "Please fill the below field to add a new family member to your profile".needTranslation,
onVerificationPress: () {
medicalVm.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
});
},
onFamilySwitchPress: (FamilyFileResponseModelLists profile) {
medicalVm.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber);
},
).paddingOnly(right: 16);
},
),
GridView(
@ -230,115 +251,300 @@ class _ProfileSettingsState extends State<ProfileSettings> {
}
class FamilyCardWidget extends StatelessWidget {
FamilyCardWidget({this.isRootUser = true, Key? key}) : super(key: key);
bool isRootUser;
late AppState appState;
final Function() onAddFamilyMemberPress;
final Function(FamilyFileResponseModelLists member) onFamilySwitchPress;
final FamilyFileResponseModelLists profile;
FamilyCardWidget({super.key, required this.onAddFamilyMemberPress, required this.profile, required this.onFamilySwitchPress(FamilyFileResponseModelLists member)});
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
AppState appState = getIt.get<AppState>();
final isActive = (profile.responseId == appState.getAuthenticatedUser()?.patientId);
final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false;
final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID);
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: Column(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [
Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h),
Image.asset(profile.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 0.h,
mainAxisSize: MainAxisSize.min,
children: [
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}"
.toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1),
"${profile.patientName}".toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1),
AppCustomChipWidget(
icon: AppAssets.file_icon,
labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}",
labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${profile.responseId}",
iconSize: 12,
),
],
).expanded,
Icon(Icons.qr_code, size: 56)
],
).expanded,
),
SizedBox(height: 4.h),
SizedBox(
width: double.infinity,
child: Wrap(
alignment: WrapAlignment.start,
spacing: 8.h,
spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"),
AppCustomChipWidget(labelText: "${profile.age} Years Old".needTranslation),
AppCustomChipWidget(
icon: AppAssets.blood_icon,
labelText: "${LocaleKeys.bloodType.tr(context: context)}: ${appState.getUserBloodGroup}",
iconColor: AppColors.primaryRedColor,
),
Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
return AppCustomChipWidget(
icon: insuranceVM.isInsuranceLoading
? AppAssets.cancel_circle_icon
: (DateTime.now().isAfter(
DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
))
? AppAssets.cancel_circle_icon
: AppAssets.insurance_active_icon,
labelText: insuranceVM.isInsuranceLoading
? "Insurance"
: (DateTime.now().isAfter(
DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
)
? "Insurance Expired".needTranslation
: "Insurance Active".needTranslation),
iconColor: insuranceVM.isInsuranceLoading
? AppColors.primaryRedColor
: (DateTime.now().isAfter(
DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
))
? AppColors.primaryRedColor
: AppColors.successColor,
iconSize: 14,
backgroundColor: insuranceVM.isInsuranceLoading
? AppColors.primaryRedColor
: (DateTime.now().isAfter(
DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
))
? AppColors.primaryRedColor.withValues(alpha: 0.15)
: AppColors.successColor.withValues(alpha: 0.15),
).toShimmer2(isShow: insuranceVM.isInsuranceLoading);
}),
icon: AppAssets.blood_icon,
labelPadding: EdgeInsetsDirectional.only(start: -6.h, end: 8.h),
labelText: "Blood: ${appState.getAuthenticatedUser()!.bloodGroup ?? "."}",
iconColor: AppColors.primaryRedColor),
Selector<InsuranceViewModel, ({bool isEmpty, int? patientID, bool isLoading, String? cardValidTo})>(
selector: (context, insuranceVM) => (
isEmpty: insuranceVM.patientInsuranceList.isEmpty,
patientID: insuranceVM.patientInsuranceList.isNotEmpty ? insuranceVM.patientInsuranceList.first.patientID : null,
isLoading: insuranceVM.isInsuranceLoading,
cardValidTo: insuranceVM.patientInsuranceList.isNotEmpty ? insuranceVM.patientInsuranceList.first.cardValidTo : null
),
builder: (context, data, child) {
if (data.isEmpty) {
return const SizedBox();
} else if (profile.responseId != data.patientID) {
return SizedBox();
}
final isLoading = data.isLoading;
final isExpired = !isLoading && DateTime.now().isAfter(DateUtil.convertStringToDate(data.cardValidTo));
final String icon;
final String labelText;
final Color iconColor;
final Color backgroundColor;
if (isLoading) {
icon = AppAssets.cancel_circle_icon;
labelText = "Insurance";
iconColor = AppColors.primaryRedColor;
backgroundColor = AppColors.primaryRedColor;
} else if (isExpired) {
icon = AppAssets.cancel_circle_icon;
labelText = "Insurance Expired".needTranslation;
iconColor = AppColors.primaryRedColor;
backgroundColor = AppColors.primaryRedColor.withValues(alpha: 0.15);
} else {
icon = AppAssets.insurance_active_icon;
labelText = "Insurance Active".needTranslation;
iconColor = AppColors.successColor;
backgroundColor = AppColors.successColor.withValues(alpha: 0.15);
}
return AppCustomChipWidget(
icon: icon,
labelText: labelText,
iconColor: iconColor,
iconSize: 12,
backgroundColor: backgroundColor,
labelPadding: EdgeInsetsDirectional.only(start: -6.h, end: 8.h),
// padding: EdgeInsets.zero,
).toShimmer2(isShow: isLoading);
},
)
// Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
// if (insuranceVM.patientInsuranceList.isEmpty) {
// return const SizedBox();
// } else if (profile.responseId != insuranceVM.patientInsuranceList.first.patientID) {
// return SizedBox();
// }
//
// final isLoading = insuranceVM.isInsuranceLoading;
// final isExpired = !isLoading && DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo));
//
// final String icon;
// final String labelText;
// final Color iconColor;
// final Color backgroundColor;
//
// if (isLoading) {
// icon = AppAssets.cancel_circle_icon;
// labelText = "Insurance";
// iconColor = AppColors.primaryRedColor;
// backgroundColor = AppColors.primaryRedColor;
// } else if (isExpired) {
// icon = AppAssets.cancel_circle_icon;
// labelText = "Insurance Expired".needTranslation;
// iconColor = AppColors.primaryRedColor;
// backgroundColor = AppColors.primaryRedColor.withValues(alpha: 0.15);
// } else {
// icon = AppAssets.insurance_active_icon;
// labelText = "Insurance Active".needTranslation;
// iconColor = AppColors.successColor;
// backgroundColor = AppColors.successColor.withValues(alpha: 0.15);
// }
//
// return AppCustomChipWidget(
// icon: icon,
// labelText: labelText,
// iconColor: iconColor,
// iconSize: 12,
// backgroundColor: backgroundColor,
// // padding: EdgeInsets.zero,
// ).toShimmer2(isShow: isLoading);
// })
// Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
// return insuranceVM.patientInsuranceList. isNotEmpty ? AppCustomChipWidget(
// icon: insuranceVM.isInsuranceLoading
// ? AppAssets.cancel_circle_icon
// : (DateTime.now().isAfter(
// DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
// ))
// ? AppAssets.cancel_circle_icon
// : AppAssets.insurance_active_icon,
// labelText: insuranceVM.isInsuranceLoading
// ? "Insurance"
// : (DateTime.now().isAfter(
// DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
// )
// ? "Insurance Expired".needTranslation
// : "Insurance Active".needTranslation),
// iconColor: insuranceVM.isInsuranceLoading
// ? AppColors.primaryRedColor
// : (DateTime.now().isAfter(
// DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
// ))
// ? AppColors.primaryRedColor
// : AppColors.successColor,
// iconSize: 14,
// backgroundColor: insuranceVM.isInsuranceLoading
// ? AppColors.primaryRedColor
// : (DateTime.now().isAfter(
// DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
// ))
// ? AppColors.primaryRedColor.withValues(alpha: 0.15)
// : AppColors.successColor.withValues(alpha: 0.15),
// ).toShimmer2(isShow: insuranceVM.isInsuranceLoading) : SizedBox();
// }),
],
),
),
],
).paddingOnly(top: 16, right: 16, left: 16, bottom: 12).expanded,
).paddingOnly(top: 16.h, right: 16.h, left: 16.h, bottom: 12.h).expanded,
1.divider,
//TODO: Add family file switch logic here
isRootUser
? CustomButton(icon: AppAssets.add_family, text: "Add a new family member".needTranslation, height: 40.h, fontSize: 14, onPressed: () {})
.paddingOnly(top: 12, right: 16, left: 16, bottom: 16)
: CustomButton(
icon: AppAssets.add_family,
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
iconColor: AppColors.primaryRedColor,
text: "Switch to this medical file".needTranslation,
height: 40.h,
fontSize: 14,
onPressed: () {})
.paddingOnly(top: 12, right: 16, left: 16, bottom: 16),
_buildActionButton(appState),
// if (appState.getAuthenticatedUser()!.isParentUser ?? false) ...[
// if (member!.responseId != appState.getAuthenticatedUser()!.patientId) ...[
// CustomButton(
// icon: AppAssets.switch_user,
// text: "Switch Family File".needTranslation,
// onPressed: () {
// onFamilySwitchPress(member!);
// },
// ).paddingOnly(top: 12, right: 16, left: 16, bottom: 16),
// ] else
// ...[
// CustomButton(
// icon: AppAssets.add_family,
// text: "Add a new family member".needTranslation,
// onPressed: () {
// onAddFamilyMemberPress();
// },
// ).paddingOnly(top: 12, right: 16, left: 16, bottom: 16),
// ]
// ] else
// ...[
// if (appState.getSuperUserID != null && appState.getSuperUserID == member!.responseId) ...[
// CustomButton(
// icon: AppAssets.switch_user,
// text: "Switch Back To Family File".needTranslation,
// onPressed: () {
// onFamilySwitchPress(member!);
// },
// ).paddingOnly(top: 12, right: 16, left: 16, bottom: 16),
// ] else
// ...[
// CustomButton(
// icon: AppAssets.switch_user,
// text: "Disabled".needTranslation,
// backgroundColor: Colors.grey.shade200,
// borderColor: Colors.grey.shade200,
// textColor: AppColors.greyTextColor,
// onPressed: () {},
// iconColor: AppColors.greyTextColor,
// ).paddingOnly(top: 12, right: 16, left: 16, bottom: 16),
// ]
// ]
],
),
);
}
Widget _buildActionButton(AppState appState) {
final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false;
final int? currentUserId = appState.getAuthenticatedUser()?.patientId;
final int? superUserId = appState.getSuperUserID;
if (isParentUser) {
return _buildParentUserButton(currentUserId);
} else {
return _buildNonParentUserButton(superUserId);
}
}
Widget _buildParentUserButton(int? currentUserId) {
final canSwitch = profile.responseId != currentUserId;
return CustomButton(
icon: canSwitch ? AppAssets.switch_user : AppAssets.add_family,
text: canSwitch ? "Switch Family File".needTranslation : "Add a new family member".needTranslation,
onPressed: canSwitch ? () => onFamilySwitchPress(profile) : onAddFamilyMemberPress,
backgroundColor: canSwitch ? AppColors.secondaryLightRedColor : AppColors.primaryRedColor,
borderColor: canSwitch ? AppColors.secondaryLightRedColor : AppColors.primaryRedColor,
textColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor,
iconColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor,
height: 40.h,
fontSize: 14,
).paddingOnly(top: 12, right: 16, left: 16, bottom: 16);
}
Widget _buildNonParentUserButton(int? superUserId) {
final canSwitchBack = superUserId != null && superUserId == profile.responseId;
return CustomButton(
icon: AppAssets.switch_user,
text: canSwitchBack ? "Switch Back To Family File".needTranslation : "Switch".needTranslation,
backgroundColor: canSwitchBack ? AppColors.primaryRedColor : Colors.grey.shade200,
borderColor: canSwitchBack ? AppColors.primaryRedColor : Colors.grey.shade200,
textColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor,
iconColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor,
onPressed: canSwitchBack ? () => onFamilySwitchPress(profile) : () {},
height: 40.h,
fontSize: 14,
).paddingOnly(top: 12, right: 16, left: 16, bottom: 16);
}
// //TODO: Add family file switch logic here
// isRootUser
// ? CustomButton(icon: AppAssets.add_family, text: "Add a new family member".needTranslation, height: 40.h, fontSize: 14, onPressed: () {})
// .paddingOnly(top: 12, right: 16, left: 16, bottom: 16)
// : CustomButton(
// icon: AppAssets.add_family,
// backgroundColor: AppColors.secondaryLightRedColor,
// borderColor: AppColors.secondaryLightRedColor,
// textColor: AppColors.primaryRedColor,
// iconColor: AppColors.primaryRedColor,
// text: "Switch to this medical file".needTranslation,
// height: 40.h,
// fontSize: 14,
// onPressed: () {})
// .paddingOnly(top: 12, right: 16, left: 16, bottom: 16),
//
}

@ -4,6 +4,7 @@ import 'package:hmg_patient_app_new/presentation/authentication/register.dart';
import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart';
import 'package:hmg_patient_app_new/presentation/home/landing_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/splashPage.dart';
class AppRoutes {
@ -12,6 +13,7 @@ class AppRoutes {
static const String register = '/register';
static const String registerStepTwo = '/registerStepTwo';
static const String landingScreen = '/landingScreen';
static const String medicalFilePage = '/medicalFilePage';
static Map<String, WidgetBuilder> get routes => {
initialRoute: (context) => SplashPage(),
@ -19,5 +21,6 @@ class AppRoutes {
landingScreen: (context) => LandingNavigation(),
register: (context) => RegisterNew(),
registerStepTwo: (context) => RegisterNewStep2(),
medicalFilePage: (context) => MedicalFilePage(),
};
}

@ -4,12 +4,15 @@ import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/route_extensions.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/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/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/bottomsheet/exception_bottom_sheet.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/family_files/family_file_add_widget.dart';
abstract class DialogService {
Future<void> showErrorBottomSheet({String title = "", required String message, Function()? onOkPressed, Function()? onCancelPressed});
@ -18,7 +21,14 @@ abstract class DialogService {
Future<void> showCommonBottomSheetWithoutH({String? label, required String message, required Function() onOkPressed, Function()? onCancelPressed});
Future<void> showFamilyBottomSheetWithoutH(
{String? label, required String message, required Function(FamilyFileResponseModelLists response) onSwitchPress, required List<FamilyFileResponseModelLists> profiles});
Future<void> showFamilyBottomSheetWithoutHWithChild({String? label, required String message, Widget? child, required Function() onOkPressed, Function()? onCancelPressed});
Future<void> showPhoneNumberPickerSheet({String? label, String? message, required Function() onSMSPress, required Function() onWhatsappPress});
Future<void> showAddFamilyFileSheet({String? label, String? message, required Function() onVerificationPress});
// TODO : Need to be Fixed showPhoneNumberPickerSheet ( From Login ADn Signup Bottom Sheet Move Here
}
@ -93,6 +103,36 @@ class DialogServiceImp implements DialogService {
title: label ?? "", child: exceptionBottomSheetWidget(context: context, message: message, onOkPressed: onOkPressed, onCancelPressed: onCancelPressed), callBackFunc: () {});
}
@override
Future<void> showFamilyBottomSheetWithoutH(
{String? label, required String message, required Function(FamilyFileResponseModelLists response) onSwitchPress, required List<FamilyFileResponseModelLists> profiles}) async {
final context = navigationService.navigatorKey.currentContext;
if (context == null) return;
showCommonBottomSheetWithoutHeight(context,
title: label ?? "",
child: FamilyCards(
profiles: profiles,
onSelect: (FamilyFileResponseModelLists profile) {
onSwitchPress(profile);
},
onRemove: (FamilyFileResponseModelLists profile) {},
isShowDetails: false,
),
callBackFunc: () {});
}
@override
Future<void> showFamilyBottomSheetWithoutHWithChild({String? label, required String message, Widget? child, required Function() onOkPressed, Function()? onCancelPressed}) async {
final context = navigationService.navigatorKey.currentContext;
if (context == null) return;
showCommonBottomSheetWithoutHeight(
context,
title: label ?? "",
child: child ?? SizedBox(),
callBackFunc: () {},
);
}
@override
Future<void> showPhoneNumberPickerSheet({String? label, String? message, required Function() onSMSPress, required Function() onWhatsappPress}) async {
final context = navigationService.navigatorKey.currentContext;
@ -100,6 +140,18 @@ class DialogServiceImp implements DialogService {
showCommonBottomSheetWithoutHeight(context,
title: label ?? "", child: showPhoneNumberPickerWidget(context: context, message: message, onSMSPress: onSMSPress, onWhatsappPress: onWhatsappPress), callBackFunc: () {});
}
@override
Future<void> showAddFamilyFileSheet({String? label, String? message, required Function() onVerificationPress}) async {
final context = navigationService.navigatorKey.currentContext;
if (context == null) return;
showCommonBottomSheetWithoutHeight(context,
title: label ?? "",
child: FamilyFileAddWidget(() {
onVerificationPress();
}, message ?? ""),
callBackFunc: () {});
}
}
Widget exceptionBottomSheetWidget({required BuildContext context, required String message, required Function() onOkPressed, Function()? onCancelPressed}) {
@ -216,3 +268,12 @@ Widget showPhoneNumberPickerWidget({required BuildContext context, String? messa
// );
});
}
// Widget familyMemberAddWidget() {
// AuthenticationViewModel authVm = getIt.get<AuthenticationViewModel>();
// return showCommonBottomSheetWithoutHeight(context,
// title: "Add Family Member".needTranslation,
// useSafeArea: true,
// child:
// callBackFunc: () {});
// }

@ -34,9 +34,9 @@ class NavigationService {
navigatorKey.currentState?.pushReplacementNamed(routeName);
}
Future<T?> pushToOtpScreen<T>({required String phoneNumber, required Function(int code) checkActivationCode, required Function(String phoneNumber) onResendOTPPressed}) {
Future<T?> pushToOtpScreen<T>({required String phoneNumber, required Function(int code) checkActivationCode, required Function(String phoneNumber) onResendOTPPressed, bool isFormFamilyFile = false}) {
return navigatorKey.currentState!.push(
MaterialPageRoute(builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed)),
MaterialPageRoute(builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed, isFormFamilyFile : isFormFamilyFile)),
);
}

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

@ -23,28 +23,30 @@ class CustomButton extends StatelessWidget {
final double? width;
final double iconSize;
final TextOverflow? textOverflow;
final BorderSide? borderSide;
CustomButton({
Key? key,
required this.text,
required this.onPressed,
this.backgroundColor = const Color(0xFFED1C2B),
this.borderColor = const Color(0xFFED1C2B),
this.textColor = Colors.white,
this.borderRadius = 12,
this.borderWidth = 2,
this.padding = const EdgeInsets.fromLTRB(8, 10, 8, 10),
this.fontSize = 16,
this.fontFamily,
this.fontWeight = FontWeight.w500,
this.isDisabled = false,
this.icon,
this.iconColor = Colors.white,
this.height = 56,
this.width,
this.iconSize = 24,
this.textOverflow,
}) : super(key: key);
CustomButton(
{Key? key,
required this.text,
required this.onPressed,
this.backgroundColor = const Color(0xFFED1C2B),
this.borderColor = const Color(0xFFED1C2B),
this.textColor = Colors.white,
this.borderRadius = 12,
this.borderWidth = 2,
this.padding = const EdgeInsets.fromLTRB(8, 10, 8, 10),
this.fontSize = 16,
this.fontFamily,
this.fontWeight = FontWeight.w500,
this.isDisabled = false,
this.icon,
this.iconColor = Colors.white,
this.height = 56,
this.width,
this.iconSize = 24,
this.textOverflow,
this.borderSide})
: super(key: key);
@override
Widget build(BuildContext context) {
@ -57,17 +59,15 @@ class CustomButton extends StatelessWidget {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: isDisabled ? Colors.transparent : backgroundColor,
borderRadius: borderRadius,
side: BorderSide(
width: borderWidth.h,
color: isDisabled ? borderColor.withOpacity(0.5) : borderColor,
)),
customBorder: BorderRadius.circular(borderRadius),
side: borderSide ?? BorderSide(width: borderWidth.h, color: isDisabled ? borderColor.withValues(alpha: 0.5) : borderColor)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (icon != null)
Padding(
padding: const EdgeInsets.only(right: 8.0, left: 8.0),
padding: EdgeInsets.only(right: 8.h, left: 8.h),
child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconSize, height: iconSize),
),
Padding(
@ -85,17 +85,6 @@ class CustomButton extends StatelessWidget {
),
],
),
)
// .toSmoothContainer(
// smoothness: 1,
// side: BorderSide(width: borderWidth, color: backgroundColor),
// borderRadius: BorderRadius.circular(borderRadius * 1.2),
// foregroundDecoration: BoxDecoration(
// color: isDisabled ? backgroundColor.withOpacity(0.5) : Colors.transparent,
// borderRadius: BorderRadius.circular(borderRadius),
// ),
// ),
);
));
}
}

@ -24,7 +24,8 @@ class AppCustomChipWidget extends StatelessWidget {
this.deleteIconColor = AppColors.textColor,
this.deleteIconHasColor = false,
this.padding = EdgeInsets.zero,
this.labelPadding ,
this.onChipTap,
this.labelPadding,
});
final String? labelText;
@ -42,57 +43,61 @@ class AppCustomChipWidget extends StatelessWidget {
final OutlinedBorder? shape;
final EdgeInsets? padding;
final EdgeInsetsDirectional? labelPadding;
final void Function()? onChipTap;
@override
Widget build(BuildContext context) {
return ChipTheme(
data: ChipThemeData(
padding: EdgeInsets.all(0.0),
shape: SmoothRectangleBorder(
side: BorderSide(
width: 10.0,
color: Colors.transparent, // Crucially, set color to transparent
style: BorderStyle.none,
return GestureDetector(
onTap: onChipTap,
child: ChipTheme(
data: ChipThemeData(
padding: EdgeInsets.all(0.0),
shape: SmoothRectangleBorder(
side: BorderSide(
width: 10.0,
color: Colors.transparent, // Crucially, set color to transparent
style: BorderStyle.none,
),
borderRadius: BorderRadius.circular(8.0), // Apply a border radius of 16.0
),
borderRadius: BorderRadius.circular(8.0), // Apply a border radius of 16.0
),
),
child: icon.isNotEmpty
? Chip(
avatar: icon.isNotEmpty ? Utils.buildSvgWithAssets(icon: icon, width: iconSize.h, height: iconSize.h, iconColor: iconHasColor ? iconColor : null, fit: BoxFit.contain) : SizedBox.shrink(),
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor),
// padding: EdgeInsets.all(0.0),
padding: padding,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
labelPadding: labelPadding??EdgeInsetsDirectional.only(start: 0.h, end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h),
backgroundColor: backgroundColor,
shape: shape ??
SmoothRectangleBorder(
borderRadius: BorderRadius.circular(8 ?? 0),
smoothness: 10,
side: BorderSide(color: AppColors.transparent, width: 1.5),
),
deleteIcon: deleteIcon?.isNotEmpty == true
? Utils.buildSvgWithAssets(icon: deleteIcon!, width: deleteIconSize!.width!.h, height: deleteIconSize!.height.h, iconColor: deleteIconHasColor ? deleteIconColor : null)
: null,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
)
: Chip(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor),
padding: EdgeInsets.all(0.0),
backgroundColor: backgroundColor,
shape: shape ?? SmoothRectangleBorder(
borderRadius: BorderRadius.circular(8 ?? 0),
smoothness: 10,
side: BorderSide(color: AppColors.transparent, width: 1.5),
child: icon.isNotEmpty
? Chip(
avatar: icon.isNotEmpty ? Utils.buildSvgWithAssets(icon: icon, width: iconSize.h, height: iconSize.h, iconColor: iconHasColor ? iconColor : null, fit: BoxFit.contain) : SizedBox.shrink(),
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor),
padding: padding,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 0.h, end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h),
backgroundColor: backgroundColor,
shape: shape ??
SmoothRectangleBorder(
borderRadius: BorderRadius.circular(8 ?? 0),
smoothness: 10,
side: BorderSide(color: AppColors.transparent, width: 1.5),
),
deleteIcon: deleteIcon?.isNotEmpty == true
? Utils.buildSvgWithAssets(icon: deleteIcon!, width: deleteIconSize!.width!.h, height: deleteIconSize!.height.h, iconColor: deleteIconHasColor ? deleteIconColor : null)
: null,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
)
: Chip(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor),
padding: EdgeInsets.all(0.0),
backgroundColor: backgroundColor,
shape: shape ??
SmoothRectangleBorder(
borderRadius: BorderRadius.circular(8 ?? 0),
smoothness: 10,
side: BorderSide(color: AppColors.transparent, width: 1.5),
),
labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: deleteIcon?.isNotEmpty == true ? -2.h : 8.h),
deleteIcon: deleteIcon?.isNotEmpty == true
? Utils.buildSvgWithAssets(icon: deleteIcon!, width: deleteIconSize!.width.h, height: deleteIconSize!.height.h, iconColor: deleteIconHasColor ? deleteIconColor : null)
: null,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
),
labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: deleteIcon?.isNotEmpty == true ? -2.h : 8.h),
deleteIcon: deleteIcon?.isNotEmpty == true
? Utils.buildSvgWithAssets(icon: deleteIcon!, width: deleteIconSize!.width.h, height: deleteIconSize!.height.h, iconColor: deleteIconHasColor ? deleteIconColor : null)
: null,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
),
),
);
}
}

@ -17,6 +17,7 @@ class CustomChipWidget extends StatelessWidget {
final Color? textColor;
final Color? borderColor;
final bool isShowBorder;
final double? height;
const CustomChipWidget({
super.key,
@ -31,6 +32,7 @@ class CustomChipWidget extends StatelessWidget {
this.textColor,
this.borderColor,
this.isShowBorder = false,
this.height,
});
@override
@ -39,6 +41,7 @@ class CustomChipWidget extends StatelessWidget {
final hasOnTap = onTap != null || hasIcon;
return Container(
height: height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadius),
color: isSelected ? chipType.color : backgroundColor ?? chipType.backgroundColor,

@ -104,55 +104,137 @@ class ButtonSheetContent extends StatelessWidget {
}
}
Future<void> showCommonBottomSheetWithoutHeight(
BuildContext context, {
required Widget child,
required VoidCallback callBackFunc,
String title = "",
bool isCloseButtonVisible = true,
bool isFullScreen = true,
bool isDismissible = true,
Widget? titleWidget,
}) async {
void showCommonBottomSheetWithoutHeight(
BuildContext context, {
required Widget child,
required VoidCallback callBackFunc,
String title = "",
bool isCloseButtonVisible = true,
bool isFullScreen = true,
bool isDismissible = true,
Widget? titleWidget,
bool useSafeArea = false,
}) {
showModalBottomSheet<String>(
sheetAnimationStyle: AnimationStyle(
duration: Duration(milliseconds: 500), // Custom animation duration
reverseDuration: Duration(milliseconds: 300), // Custom reverse animation duration
),
context: context,
isScrollControlled: true,
showDragHandle: false,
isDismissible: isDismissible,
backgroundColor: AppColors.bottomSheetBgColor,
builder: (BuildContext context) {
return SafeArea(
top: false,
left: false,
right: false,
child: isCloseButtonVisible
? Container(
padding: EdgeInsets.only(left: 24, top: 24, right: 24, bottom: 12),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.bottomSheetBgColor, borderRadius: 24.h),
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 16.h,
sheetAnimationStyle: AnimationStyle(
duration: Duration(milliseconds: 500),
reverseDuration: Duration(milliseconds: 300),
),
context: context,
isScrollControlled: true,
showDragHandle: false,
isDismissible: isDismissible,
backgroundColor: AppColors.bottomSheetBgColor,
useSafeArea: useSafeArea,
builder: (BuildContext context) {
return SafeArea(
top: false,
left: false,
right: false,
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: SingleChildScrollView(
physics: ClampingScrollPhysics(),
child: isCloseButtonVisible
? Container(
padding: EdgeInsets.only(
left: 24,
top: 24,
right: 24,
bottom: 12,
),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.bottomSheetBgColor,
borderRadius: 24.h,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
titleWidget ?? Expanded(child: title.toText20(weight: FontWeight.w600)),
Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() {
Navigator.of(context).pop();
}),
],
),
child,
titleWidget ??
Expanded(
child: title.toText20(weight: FontWeight.w600),
),
Utils.buildSvgWithAssets(
icon: AppAssets.close_bottom_sheet_icon,
iconColor: Color(0xff2B353E),
).onPress(() {
Navigator.of(context).pop();
}),
],
))
: child,
);
}).then((value) {
),
SizedBox(height: 16.h),
child,
],
),
)
: child,
),
),
);
},
).then((value) {
callBackFunc();
});
}
// void showCommonBottomSheetWithoutHeight(
// BuildContext context, {
// required Widget child,
// required VoidCallback callBackFunc,
// String title = "",
// bool isCloseButtonVisible = true,
// bool isFullScreen = true,
// bool isDismissible = true,
// Widget? titleWidget,
// bool useSafeArea = false,
//
// }) {
// showModalBottomSheet<String>(
// sheetAnimationStyle: AnimationStyle(
// duration: Duration(milliseconds: 500), // Custom animation duration
// reverseDuration: Duration(milliseconds: 300), // Custom reverse animation duration
// ),
// context: context,
// isScrollControlled: true,
// showDragHandle: false,
// isDismissible: isDismissible,
// backgroundColor: AppColors.bottomSheetBgColor,
// useSafeArea: useSafeArea,
// builder: (BuildContext context) {
// return SafeArea(
// top: false,
// left: false,
// right: false,
// child: isCloseButtonVisible
// ? Container(
// padding: EdgeInsets.only(left: 24, top: 24, right: 24, bottom: 12),
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.bottomSheetBgColor, borderRadius: 24.h),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// spacing: 16.h,
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// titleWidget ?? Expanded(child: title.toText20(weight: FontWeight.w600)),
// Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() {
// Navigator.of(context).pop();
// }),
// ],
// ),
// child,
// ],
// ))
// : child,
// );
// }).then((value) {
// callBackFunc();
// });
// }

@ -0,0 +1,105 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_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/medical_file_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/dropdown/country_dropdown_widget.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
class FamilyFileAddWidget extends StatelessWidget {
final Function()? onVerificationPress;
final String message;
const FamilyFileAddWidget(this.onVerificationPress, this.message, {super.key});
@override
Widget build(BuildContext context) {
AuthenticationViewModel authVm = getIt.get<AuthenticationViewModel>();
MedicalFileViewModel? medicalVM = getIt.get<MedicalFileViewModel>();
// TODO: implement build
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
message.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,
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: "",
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.h),
],
),
),
SizedBox(height: 20.h),
CustomButton(
text: "Verify the member".needTranslation,
onPressed: () {
FocusScope.of(context).unfocus();
if (ValidationUtils.isValidatedIdAndPhoneWithCountryValidation(
nationalId: authVm.nationalIdController.text,
selectedCountry: authVm.selectedCountrySignup,
phoneNumber: authVm.phoneNumberController.text,
onOkPress: () {
Navigator.of(context).pop();
},
)) {
// authVm.addFamilyMember(otpTypeEnum: OTPTypeEnum.sms, isExcludedUser: true);
if (onVerificationPress != null) {
onVerificationPress!();
}
}
},
icon: AppAssets.add_icon,
height: 56.h,
fontWeight: FontWeight.w600),
SizedBox(height: 20.h),
],
);
}
}
Loading…
Cancel
Save