From 192d617350216d240506b98284e62751ebf473e2 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Wed, 24 Sep 2025 09:33:33 +0300 Subject: [PATCH] family screen & widgets --- lib/core/api_consts.dart | 2 +- lib/core/utils/request_utils.dart | 28 ++-- .../authentication/authentication_repo.dart | 6 +- .../authentication_view_model.dart | 40 +++--- .../common/models/family_file_request.dart | 57 ++++++++ .../medical_file/medical_file_repo.dart | 23 ++-- .../medical_file/medical_file_view_model.dart | 32 ++++- .../medical_file/medical_file_page.dart | 7 + lib/presentation/my_family/my_Family.dart | 4 +- lib/widgets/chip/app_custom_chip_widget.dart | 129 +++++++++--------- 10 files changed, 211 insertions(+), 117 deletions(-) create mode 100644 lib/features/common/models/family_file_request.dart diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 3640cdd..9b013a5 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -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 diff --git a/lib/core/utils/request_utils.dart b/lib/core/utils/request_utils.dart index 4f66819..cd813ed 100644 --- a/lib/core/utils/request_utils.dart +++ b/lib/core/utils/request_utils.dart @@ -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({ @@ -126,6 +127,7 @@ class RequestUtils { required bool isFileNo, dynamic payload, required bool isExcludedUser, + required bool isFormFamilyFile, int? responseID, }) { AppState _appState = getIt.get(); @@ -159,7 +161,7 @@ class RequestUtils { } request.deviceTypeID = request.searchType; - if (isExcludedUser) { + if (isFormFamilyFile) { //INFO: Only for Excluded User Family Member Addition request.isPatientExcluded = isExcludedUser; request.responseID = responseID; @@ -258,8 +260,8 @@ class RequestUtils { }; } - static dynamic getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode}) { - var request = {}; + static Future getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode}) async { + FamilyFileRequest request = FamilyFileRequest(); int? loginType = 0; // Default to National ID if (countryCode == CountryEnum.saudiArabia.countryCode || countryCode == '+966') { @@ -269,18 +271,18 @@ class RequestUtils { } 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["isDentalAllowedBackend"] = false; + request.searchType = loginType; + request.sharedPatientMobileNumber = mobileNo; + request.zipCode = countryCode; + request.isRegister = false; + request.patientStatus = 2; + request.isDentalAllowedBackend = false; return request; } } diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index 89a9404..8038c43 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -17,7 +17,7 @@ abstract class AuthenticationRepo { Future>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq}); - Future>> sendActivationCodeRepo({required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false, bool isExcludedUser = false}); + Future>> sendActivationCodeRepo({required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false, bool isFormFamilyFile = false}); Future>> checkActivationCodeRepo({required dynamic newRequest, required String? activationCode, required bool isRegister, bool isExcludedUser = false}); @@ -131,7 +131,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false, - bool isExcludedUser = false, + bool isFormFamilyFile = false, }) async { int isOutKsa = (sendActivationCodeReq.zipCode == '966' || sendActivationCodeReq.zipCode == '+966') ? 0 : 1; sendActivationCodeReq.patientOutSA = isOutKsa; @@ -142,7 +142,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { Failure? failure; await apiClient.post( - isExcludedUser + isFormFamilyFile ? ApiConsts.sendFamilyFileActivation : isRegister ? ApiConsts.sendActivationCodeRegister diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 7c1252c..558f02d 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -354,25 +354,26 @@ class AuthenticationViewModel extends ChangeNotifier { 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, - isExcludedUser: isExcludedUser, - responseID: responseID, - ); + 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, + isExcludedUser: isExcludedUser, + isFormFamilyFile: isFormFamilyFile, + responseID: responseID); // TODO: GET APP SMS SIGNATURE HERE request.sMSSignature = await getSignature(); @@ -382,7 +383,7 @@ class AuthenticationViewModel extends ChangeNotifier { } final resultEither = - await _authenticationRepo.sendActivationCodeRepo(sendActivationCodeReq: request, isRegister: checkIsUserComingForRegister(request: payload), languageID: 'er', isExcludedUser: isExcludedUser); + await _authenticationRepo.sendActivationCodeRepo(sendActivationCodeReq: request, isRegister: checkIsUserComingForRegister(request: payload), languageID: 'er', isFormFamilyFile: isFormFamilyFile); resultEither.fold( (failure) async => await _errorHandlerService.handleError(failure: failure), @@ -397,9 +398,10 @@ class AuthenticationViewModel extends ChangeNotifier { } else { if (apiResponse.data != null && apiResponse.data['isSMSSent'] == true) { LoaderBottomSheet.hideLoader(); - if (!isComingFromResendOTP) + if (!isComingFromResendOTP) { navigateToOTPScreen( otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber, isComingFromRegister: checkIsUserComingForRegister(request: payload), payload: payload, isExcludedUser: isExcludedUser); + } } else { // TODO: Handle isSMSSent false // navigateToOTPScreen(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber); diff --git a/lib/features/common/models/family_file_request.dart b/lib/features/common/models/family_file_request.dart new file mode 100644 index 0000000..047b9af --- /dev/null +++ b/lib/features/common/models/family_file_request.dart @@ -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 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 toJson() => { + "SharedPatientID": sharedPatientId, + "SharedPatientIdentificationID": sharedPatientIdentificationId, + "SearchType": searchType, + "SharedPatientMobileNumber": sharedPatientMobileNumber, + "zipCode": zipCode, + "isRegister": isRegister, + "PatientStatus": patientStatus, + "isDentalAllowedBackend": isDentalAllowedBackend, + "IsPatientExcluded": isPatientExcluded, + "ReponseID": responseID, + }; +} diff --git a/lib/features/medical_file/medical_file_repo.dart b/lib/features/medical_file/medical_file_repo.dart index 6b83f33..3042aba 100644 --- a/lib/features/medical_file/medical_file_repo.dart +++ b/lib/features/medical_file/medical_file_repo.dart @@ -26,7 +26,7 @@ abstract class MedicalFileRepo { Future>>> getPatientFamilyFiles(); - Future>>> addFamilyFile({required dynamic request}); + Future>> addFamilyFile({required dynamic request}); } class MedicalFileRepoImp implements MedicalFileRepo { @@ -313,9 +313,9 @@ class MedicalFileRepoImp implements MedicalFileRepo { } @override - Future>>> addFamilyFile({dynamic request}) async { + Future>> addFamilyFile({dynamic request}) async { try { - GenericApiModel>? apiResponse; + GenericApiModel? apiResponse; Failure? failure; await apiClient.post( ApiConsts.addFamilyFile, @@ -325,17 +325,12 @@ 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)).toList().cast(); - // - // apiResponse = GenericApiModel>( - // messageStatus: messageStatus, - // statusCode: statusCode, - // errorMessage: null, - // data: familyLists, - // ); + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: response["ShareFamilyFileObj"] ?? null, + ); } catch (e) { failure = DataParsingFailure(e.toString()); } diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart index a724a10..1c3d353 100644 --- a/lib/features/medical_file/medical_file_view_model.dart +++ b/lib/features/medical_file/medical_file_view_model.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; @@ -6,6 +8,7 @@ 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'; @@ -13,6 +16,7 @@ import 'package:hmg_patient_app_new/features/medical_file/models/patient_sicklea import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; class MedicalFileViewModel extends ChangeNotifier { int selectedTabIndex = 0; @@ -298,12 +302,34 @@ class MedicalFileViewModel extends ChangeNotifier { Future addFamilyFile({required OTPTypeEnum otpTypeEnum, required bool isExcludedUser}) async { AuthenticationViewModel authVM = getIt.get(); - final request = + NavigationService navigationService = getIt.get(); + FamilyFileRequest request = await RequestUtils.getAddFamilyRequest(nationalIDorFile: authVM.nationalIdController.text, mobileNo: authVM.phoneNumberController.text, countryCode: authVM.selectedCountrySignup.countryCode); - final resultEither = await medicalFileRepo.addFamilyFile(request: request); + final resultEither = await medicalFileRepo.addFamilyFile(request: request.toJson()); resultEither.fold((failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) async { - print(apiResponse); + if (apiResponse != null && apiResponse.data != null) { + request.isPatientExcluded = apiResponse.data["IsPatientExcluded"]; + request.responseID = apiResponse.data["ReponseID"]; + _dialogService.showExceptionBottomSheet( + message: apiResponse.data['Message'], + onOkPressed: () { + print("=================== On Press Ok =================="); + authVM.sendActivationCode( + otpTypeEnum: otpTypeEnum, + nationalIdOrFileNumber: request.sharedPatientIdentificationId!, + phoneNumber: request.sharedPatientMobileNumber!, + isForRegister: false, + isExcludedUser: apiResponse.data['IsPatientExcluded'], + responseID: apiResponse.data["ReponseID"], + isFormFamilyFile: true); + + // insertFamilyData(payload: apiResponse.data![0]['ShareFamilyFileObj'], isExcludedPatient: apiResponse.data![0]['ShareFamilyFileObj']['IsPatientExcluded']); + }, + onCancelPressed: () { + navigationService.pop(); + }); + } }); } } diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 9776e5b..3786382 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -143,6 +143,13 @@ class _MedicalFilePageState extends State { 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, diff --git a/lib/presentation/my_family/my_Family.dart b/lib/presentation/my_family/my_Family.dart index 4eefe7a..7ebd069 100644 --- a/lib/presentation/my_family/my_Family.dart +++ b/lib/presentation/my_family/my_Family.dart @@ -28,10 +28,10 @@ class FamilyMedicalScreen extends StatefulWidget { final Function(FamilyFileResponseModelLists) onSelect; const FamilyMedicalScreen({ - Key? key, + super.key, required this.profiles, required this.onSelect, - }) : super(key: key); + }); @override State createState() => _FamilyMedicalScreenState(); diff --git a/lib/widgets/chip/app_custom_chip_widget.dart b/lib/widgets/chip/app_custom_chip_widget.dart index b29f6e6..099062e 100644 --- a/lib/widgets/chip/app_custom_chip_widget.dart +++ b/lib/widgets/chip/app_custom_chip_widget.dart @@ -24,6 +24,7 @@ class AppCustomChipWidget extends StatelessWidget { this.deleteIconColor = AppColors.textColor, this.deleteIconHasColor = false, this.padding = EdgeInsets.zero, + this.onChipTap }); final String? labelText; @@ -40,74 +41,78 @@ class AppCustomChipWidget extends StatelessWidget { final bool deleteIconHasColor; final OutlinedBorder? shape; final EdgeInsets? padding; + final void Function()? onChipTap; @override Widget build(BuildContext context) { - return ChipTheme( - data: ChipThemeData( - padding: EdgeInsets.all(0.0), - shape: SmoothRectangleBorder( - side: BorderSide( - width: 0.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: 0.0, + color: Colors.transparent, // Crucially, set color to transparent + style: BorderStyle.none, + ), + borderRadius: BorderRadius.circular(10.0), // Apply a border radius of 16.0 ), - borderRadius: BorderRadius.circular(10.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) + : SizedBox.shrink(), + label: richText ?? + labelText!.toText10( + weight: FontWeight.w500, + letterSpacing: -0.64, + color: textColor), + // padding: EdgeInsets.all(0.0), + padding: padding, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + labelPadding: EdgeInsets.only( + left: -4.h, + right: deleteIcon?.isNotEmpty == true ? 2.h : 8.h), + backgroundColor: backgroundColor, + shape: shape, + 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.64, + color: textColor), + padding: EdgeInsets.all(0.0), + backgroundColor: backgroundColor, + shape: shape, + labelPadding: EdgeInsets.only( + left: 8.h, + right: 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, + ), ), - child: icon.isNotEmpty - ? Chip( - avatar: icon.isNotEmpty - ? Utils.buildSvgWithAssets( - icon: icon, - width: iconSize.h, - height: iconSize.h, - iconColor: iconHasColor ? iconColor : null) - : SizedBox.shrink(), - label: richText ?? - labelText!.toText10( - weight: FontWeight.w500, - letterSpacing: -0.64, - color: textColor), - // padding: EdgeInsets.all(0.0), - padding: padding, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - labelPadding: EdgeInsets.only( - left: -4.h, - right: deleteIcon?.isNotEmpty == true ? 2.h : 8.h), - backgroundColor: backgroundColor, - shape: shape, - 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.64, - color: textColor), - padding: EdgeInsets.all(0.0), - backgroundColor: backgroundColor, - shape: shape, - labelPadding: EdgeInsets.only( - left: 8.h, - right: 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, - ), ); } }