diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index d31679c..efdde41 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -720,10 +720,14 @@ const SAVE_SETTING = 'Services/Patients.svc/REST/UpdatePateintInfo'; const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_InsertUpdate'; +//family Files + +const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus'; + class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.preProd; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/features/medical_file/medical_file_repo.dart b/lib/features/medical_file/medical_file_repo.dart index bbb4fcd..69c45f3 100644 --- a/lib/features/medical_file/medical_file_repo.dart +++ b/lib/features/medical_file/medical_file_repo.dart @@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine import 'package:hmg_patient_app_new/services/logger_service.dart'; import '../authentication/models/resp_models/authenticated_user_resp_model.dart'; +import 'models/family_file_response_model.dart'; abstract class MedicalFileRepo { Future>>> getPatientVaccinesList(); @@ -22,6 +23,10 @@ abstract class MedicalFileRepo { Future>>> getPatientMedicalReportsList(); Future>> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser); + + Future>>> getPatientFamilyFiles(); + + } class MedicalFileRepoImp implements MedicalFileRepo { @@ -267,4 +272,45 @@ class MedicalFileRepoImp implements MedicalFileRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> getPatientFamilyFiles() async { + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + FAMILY_FILES, + body: {"Status":3}, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['GetAllSharedRecordsByStatusList']; + // if (list == null || list.isEmpty) { + // throw Exception("lab list is empty"); + // } + + final familyLists = list.map((item) => FamilyFileResponseModelLists.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + 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())); + } + } + } diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart index 73167bb..e640769 100644 --- a/lib/features/medical_file/medical_file_view_model.dart +++ b/lib/features/medical_file/medical_file_view_model.dart @@ -1,9 +1,13 @@ 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/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; class MedicalFileViewModel extends ChangeNotifier { @@ -25,11 +29,14 @@ class MedicalFileViewModel extends ChangeNotifier { List patientMedicalReportReadyList = []; List patientMedicalReportCancelledList = []; + List patientFamilyFiles =[]; + String patientSickLeavePDFBase64 = ""; String patientMedicalReportPDFBase64 = ""; int selectedMedicalReportsTabIndex = 0; - + static final DialogService _dialogService = getIt.get(); + AppState _appState = getIt(); MedicalFileViewModel({required this.medicalFileRepo, required this.errorHandlerService}); initMedicalFileProvider() { @@ -217,4 +224,64 @@ class MedicalFileViewModel extends ChangeNotifier { }, ); } + + + Future getFamilyFiles({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await medicalFileRepo.getPatientFamilyFiles(); + + result.fold( + (failure) async => await errorHandlerService.handleError( + failure: failure, + onOkPressed: () { + onError!(failure.message); + }, + ), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + _dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + + patientFamilyFiles = apiResponse.data!; + patientFamilyFiles.insert(0, + FamilyFileResponseModelLists(patientId: _appState.getAuthenticatedUser()!.patientId, patientName: '${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}', isActive: true, gender:_appState.getAuthenticatedUser()!.gender!, responseId: _appState.getAuthenticatedUser()!.patientId ), + ); + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + + + Future switchFamilyFiles({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await medicalFileRepo.getPatientFamilyFiles(); + + result.fold( + (failure) async => await errorHandlerService.handleError( + failure: failure, + onOkPressed: () { + onError!(failure.message); + }, + ), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + _dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + + patientFamilyFiles = apiResponse.data!; + patientFamilyFiles.insert(0, + FamilyFileResponseModelLists(patientId: _appState.getAuthenticatedUser()!.patientId, patientName: '${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}', isActive: true, gender:_appState.getAuthenticatedUser()!.gender!, responseId: _appState.getAuthenticatedUser()!.patientId ), + ); + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + } diff --git a/lib/features/medical_file/models/family_file_response_model.dart b/lib/features/medical_file/models/family_file_response_model.dart new file mode 100644 index 0000000..82fe3d8 --- /dev/null +++ b/lib/features/medical_file/models/family_file_response_model.dart @@ -0,0 +1,105 @@ +import 'dart:convert'; + +class FamilyFileResponseModelLists { + int? id; + int? patientId; + int? responseId; + dynamic relationshipId; + dynamic relationship; + dynamic relationshipN; + int? regionId; + int? familyRegionId; + int? status; + dynamic isActive; + String? editedOn; + String? createdOn; + int? age; + String? emaiLAddress; + int? gender; + String? genderDescription; + String? genderImage; + String? mobileNumber; + int? patientDataVerified; + String? patientIdenficationNumber; + String? patientName; + String? statusDescription; + + FamilyFileResponseModelLists({ + this.id, + this.patientId, + this.responseId, + this.relationshipId, + this.relationship, + this.relationshipN, + this.regionId, + this.familyRegionId, + this.status, + this.isActive, + this.editedOn, + this.createdOn, + this.age, + this.emaiLAddress, + this.gender, + this.genderDescription, + this.genderImage, + this.mobileNumber, + this.patientDataVerified, + this.patientIdenficationNumber, + this.patientName, + this.statusDescription, + }); + + factory FamilyFileResponseModelLists.fromRawJson(String str) => FamilyFileResponseModelLists.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory FamilyFileResponseModelLists.fromJson(Map json) => FamilyFileResponseModelLists( + id: json["ID"], + patientId: json["PatientID"], + responseId: json["ResponseID"], + relationshipId: json["RelationshipID"], + relationship: json["Relationship"], + relationshipN: json["RelationshipN"], + regionId: json["RegionID"], + familyRegionId: json["FamilyRegionID"], + status: json["Status"], + isActive: json["IsActive"], + editedOn: json["EditedOn"], + createdOn: json["CreatedOn"], + age: json["Age"], + emaiLAddress: json["EmaiLAddress"], + gender: json["Gender"], + genderDescription: json["GenderDescription"], + genderImage: json["GenderImage"], + mobileNumber: json["MobileNumber"], + patientDataVerified: json["PatientDataVerified"], + patientIdenficationNumber: json["PatientIdenficationNumber"], + patientName: json["PatientName"], + statusDescription: json["StatusDescription"], + ); + + Map 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, + }; +} diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 22de759..2812b7a 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -15,6 +15,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/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/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'; @@ -40,6 +41,7 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/my_family/my_family_sheet.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; @@ -139,7 +141,14 @@ class _MedicalFilePageState extends State { iconColor: AppColors.blackColor, iconSize: 12.h, text: "File no: ${appState.getAuthenticatedUser()!.patientId}", - onPressed: () {}, + onPressed: () { + //need to remove from this call from here once the family icons will be added + + MyFamilySheet.show(context,medicalFileViewModel.patientFamilyFiles , (profile){ + + }); + + }, backgroundColor: AppColors.greyColor, borderColor: AppColors.greyColor, textColor: AppColors.blackColor, diff --git a/lib/widgets/my_family/my_Family.dart b/lib/widgets/my_family/my_Family.dart index 912ae0b..43295ae 100644 --- a/lib/widgets/my_family/my_Family.dart +++ b/lib/widgets/my_family/my_Family.dart @@ -1,44 +1,137 @@ import 'package:flutter/material.dart'; + import 'package:flutter_svg/flutter_svg.dart'; + import 'package:hmg_patient_app_new/core/app_assets.dart'; + import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; + import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; + import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; + import 'package:hmg_patient_app_new/theme/colors.dart'; + import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; -class ProfileSelector extends StatelessWidget { - final List> profiles; - final Function(Map) onSelect; - - const ProfileSelector({ - Key? key, - required this.profiles, - required this.onSelect, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: profiles.map((profile) { - return ListTile( - leading: CircleAvatar( - radius: 22, - backgroundImage: profile["GenderImage"] != null && - profile["GenderImage"].toString().isNotEmpty - ? NetworkImage(profile["GenderImage"]) - : AssetImage( - profile["Gender"] == 1 - ? "assets/images/male.png" - : "assets/images/female.png") - as ImageProvider, - ), - title: Text( - profile["PatientName"] ?? "Unknown", - style: const TextStyle(fontWeight: FontWeight.w600), - ), - subtitle: Text( - profile["Relationship"] ?? "Self", - style: const TextStyle(color: Colors.grey), + class ProfileSelector extends StatelessWidget { + final List profiles; + final Function(FamilyFileResponseModelLists) onSelect; + + + const ProfileSelector({ + Key? key, + required this.profiles, + required this.onSelect, + }) : super(key: key); + + + @override + Widget build(BuildContext context) { + final double screenHeight = MediaQuery.of(context).size.height; + int? activeProfileId = Utils.appState.getAuthenticatedUser()?.patientId; + return SizedBox( + height: screenHeight * 0.6, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // const Text( + // "Please select a profile", + // style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + // ), + // const SizedBox(height: 6), + // const Text( + // "Switch from the below list of medical file", + // style: TextStyle(fontSize: 14, color: Colors.grey), + // textAlign: TextAlign.center, + // ), + const SizedBox(height: 20), + + // ✅ GridView inside scroll + GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: profiles.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + childAspectRatio: 0.75, + ), + itemBuilder: (context, index) { + final profile = profiles[index]; + final isActive = + (profile.responseId == activeProfileId); + return Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: isActive ? Colors.grey.shade100 : AppColors.whiteColor, // Lighter background for active + borderRadius: 24, + ), + child: Opacity( + opacity: isActive ? 0.5 : 1.0, // Fade all content if active + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 5), + Container( + height: 80, + width: 78, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + image: AssetImage( + profile.gender == 1 + ? AppAssets.male_img + : AppAssets.femaleImg, + ), + fit: BoxFit.cover, + ), + ), + ), + const SizedBox(height: 8), + (profile.patientName ?? "Unknown").toText14( + isBold: true, + maxlines: 1, + isCenter: true, + ), + const SizedBox(height: 4), + Text( + "Relation: ${profile.relationship ?? "N/A"}", + style: const TextStyle( + fontSize: 12, + color: Colors.grey, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 5), + if (isActive) + CustomButton( + height: 36, + onPressed: (){}, // Disabled + text: 'Active', + backgroundColor: Colors.grey.shade200, + borderColor: Colors.grey.shade200, + textColor: AppColors.greyTextColor, + fontSize: 13, + + ).paddingOnly(top: 8, bottom: 4) + else + CustomButton( + height: 36, + onPressed: () => onSelect(profile), + text: 'Select', + backgroundColor: const Color(0xffFEE9EA), + borderColor: const Color(0xffFEE9EA), + textColor: const Color(0xffED1C2B), + fontSize: 13, + ).paddingOnly(top: 8, bottom: 4), + ], + ), + ), + ); + }, + ), + ], ), - trailing: const Icon(Icons.arrow_forward_ios, size: 16), - onTap: () => onSelect(profile), - ); - }).toList(), - ); + ), + ); + } + } -} diff --git a/lib/widgets/my_family/my_family_sheet.dart b/lib/widgets/my_family/my_family_sheet.dart index 2e0ae15..37ad5af 100644 --- a/lib/widgets/my_family/my_family_sheet.dart +++ b/lib/widgets/my_family/my_family_sheet.dart @@ -1,12 +1,23 @@ import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; import '../common_bottom_sheet.dart'; import 'my_Family.dart'; class MyFamilySheet { - static void show(BuildContext context, List> profiles, Function(Map) onSelect) { + static void show(BuildContext context, List familyLists, Function(FamilyFileResponseModelLists) onSelect) { showCommonBottomSheetWithoutHeight( context, - title: 'Select Profile', - child: ProfileSelector(profiles: profiles, onSelect: (profile) { + titleWidget: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 'Please select a profile'.toText21(isBold: true), + 'switch from the below list of medical file'.toText16(weight: FontWeight.w100, color: AppColors.greyTextColor), + ],), + + child: ProfileSelector(profiles: familyLists, onSelect: (profile) { + Navigator.of(context).pop(); // Close the bottom sheet onSelect(profile); // Call the onSelect callback }), callBackFunc: () {},