From d3dfb2e2a50058fea17b2582fadba7c25355d107 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Wed, 1 Apr 2026 15:37:23 +0300 Subject: [PATCH] doctor favorite in progress. --- lib/core/api_consts.dart | 9 +- lib/core/app_assets.dart | 2 + .../authentication/authentication_repo.dart | 35 ++++++++ .../authentication_view_model.dart | 28 ++++++ .../book_appointments_repo.dart | 88 +++++++++++++++++++ .../book_appointments_view_model.dart | 86 ++++++++++++++++++ .../book_appointment/doctor_profile_page.dart | 43 +++++++-- 7 files changed, 284 insertions(+), 7 deletions(-) diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index a529c740..57c88c13 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart'; 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 @@ -229,6 +229,7 @@ class ApiConsts { static String getPatientBloodGroup = "services/PatientVarification.svc/REST/BloodDonation_GetBloodGroupDetails"; static String getPatientBloodAgreement = "Services/PatientVarification.svc/REST/CheckUserAgreementForBloodDonation"; + static String getPatientBloodTypeNew = "Services/Patients.svc/REST/HIS_GetPatientBloodType_New"; static String getAiOverViewLabOrders = "Services/Patients.svc/REST/HMGAI_Lab_Analyze_Orders_API"; static String getAiOverViewLabOrder = "Services/Patients.svc/REST/HMGAI_Lab_Analyzer_API"; @@ -467,6 +468,12 @@ var GET_DENTAL_DOCTORS_LIST_URL = "Services/Doctors.svc/REST/Dental_DoctorChiefC //URL to get doctor free slots var GET_DOCTOR_FREE_SLOTS = "Services/Doctors.svc/REST/GetDoctorFreeSlots"; +//URL to check if doctor is favorite +var IS_FAVOURITE_DOCTOR = "Services/Patients.svc/REST/Patient_IsFavouriteDoctor"; + +//URL to insert favorite doctor +var INSERT_FAVOURITE_DOCTOR = "Services/Patients.svc/REST/Patient_InsertFavouriteDoctor"; + //URL to insert appointment var INSERT_SPECIFIC_APPOINTMENT = "Services/Doctors.svc/REST/InsertSpecificAppointment"; diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index fdd32814..de192d23 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -180,6 +180,8 @@ class AppAssets { static const String ic_rrt_vehicle = '$svgBasePath/ic_rrt_vehicle.svg'; static const String doctor_profile_rating_icon = '$svgBasePath/doctor_profile_rating_icon.svg'; static const String doctor_profile_reviews_icon = '$svgBasePath/doctor_profile_reviews_icon.svg'; + static const String bookmark_icon = '$svgBasePath/bookmark_icon.svg'; + static const String bookmark_filled_icon = '$svgBasePath/bookmark_filled_icon.svg'; static const String waiting_appointment_icon = '$svgBasePath/waitingAppo.svg'; static const String call_for_vitals = '$svgBasePath/call_for_vitals.svg'; static const String call_for_doctor = '$svgBasePath/call_for_doctor.svg'; diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index f3b14c67..91f304bc 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -45,6 +45,8 @@ abstract class AuthenticationRepo { Future>> insertPatientDeviceData({required dynamic patientDeviceDataRequest}); Future>> getPatientDeviceData({required dynamic patientDeviceDataRequest}); + + Future>> getPatientBloodType(); } class AuthenticationRepoImp implements AuthenticationRepo { @@ -688,4 +690,37 @@ class AuthenticationRepoImp implements AuthenticationRepo { } } } + + @override + Future>> getPatientBloodType() async { + Map requestBody = {}; + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + ApiConsts.getPatientBloodTypeNew, + body: requestBody, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + 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())); + } + } } diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index ce54c69f..276d8ddb 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -624,6 +624,9 @@ class AuthenticationViewModel extends ChangeNotifier { _appState.setAuthenticatedUser(activation.list!.first); _appState.setPrivilegeModelList(activation.list!.first.listPrivilege!); _appState.setUserBloodGroup = activation.patientBlodType ?? "N/A"; + + // Fetch patient blood type from new API + await getPatientBloodTypeNew(); } // _appState.setUserBloodGroup = (activation.patientBlodType ?? ""); _appState.setAppAuthToken = activation.authenticationTokenId; @@ -1150,4 +1153,29 @@ class AuthenticationViewModel extends ChangeNotifier { _navigationService.pushAndReplace(AppRoutes.landingScreen); } } + + Future getPatientBloodTypeNew() async { + try { + final result = await _authenticationRepo.getPatientBloodType(); + + result.fold( + (failure) async { + // Log error but don't show to user, keep existing blood type + log("Failed to fetch blood type: ${failure.message}"); + }, + (apiResponse) { + if (apiResponse.messageStatus == 1 && apiResponse.data != null) { + // Extract blood type from response + String? bloodType = apiResponse.data['GetPatientBloodType']; + if (bloodType != null && bloodType.isNotEmpty) { + _appState.setUserBloodGroup = bloodType; + log("Blood type updated from new API: $bloodType"); + } + } + }, + ); + } catch (e) { + log("Error calling getPatientBloodType: $e"); + } + } } diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart index fc541d2e..b56fa024 100644 --- a/lib/features/book_appointments/book_appointments_repo.dart +++ b/lib/features/book_appointments/book_appointments_repo.dart @@ -107,6 +107,12 @@ abstract class BookAppointmentsRepo { Function(String)? onError}); Future>> getAppointmentNearestGate({required int projectID, required int clinicID}); + + Future>> isFavouriteDoctor( + {required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError}); + + Future>> insertFavouriteDoctor( + {required int patientID, required int projectID, required int clinicID, required int doctorID, required bool isActive, Function(dynamic)? onSuccess, Function(String)? onError}); } class BookAppointmentsRepoImp implements BookAppointmentsRepo { @@ -1133,4 +1139,86 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>> isFavouriteDoctor( + {required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + Map mapRequest = {"PatientID": patientID, "ProjectID": projectID, "ClinicID": clinicID, "DoctorID": doctorID}; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + IS_FAVOURITE_DOCTOR, + body: mapRequest, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + if (onError != null) { + onError(error); + } + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + if (onSuccess != null) { + onSuccess(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>> insertFavouriteDoctor( + {required int patientID, required int projectID, required int clinicID, required int doctorID, required bool isActive, Function(dynamic)? onSuccess, Function(String)? onError}) async { + Map mapRequest = {"PatientID": patientID, "ProjectID": projectID, "ClinicID": clinicID, "DoctorID": doctorID, "IsActive": isActive}; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + INSERT_FAVOURITE_DOCTOR, + body: mapRequest, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + if (onError != null) { + onError(error); + } + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + if (onSuccess != null) { + onSuccess(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())); + } + } } diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index 4dd103b8..aefc0b79 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -97,6 +97,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { bool isDoctorRatingDetailsLoading = false; List doctorDetailsList = []; + bool isFavouriteDoctor = false; + List slotsList = []; List docFreeSlots = []; List dayEvents = []; @@ -625,6 +627,15 @@ class BookAppointmentsViewModel extends ChangeNotifier { } else if (apiResponse.messageStatus == 1) { doctorsProfileResponseModel = apiResponse.data!; notifyListeners(); + + // Check if doctor is favorite after getting profile + checkIsFavouriteDoctor( + patientID: _appState.getAuthenticatedUser()!.patientId!, + projectID: doctorsProfileResponseModel.projectID ?? 0, + clinicID: doctorsProfileResponseModel.clinicID ?? 0, + doctorID: doctorsProfileResponseModel.doctorID ?? 0, + ); + if (onSuccess != null) { onSuccess(apiResponse); } @@ -1507,4 +1518,79 @@ class BookAppointmentsViewModel extends ChangeNotifier { }, ); } + + void toggleFavouriteDoctor() { + isFavouriteDoctor = !isFavouriteDoctor; + notifyListeners(); + } + + void setIsFavouriteDoctor(bool value) { + isFavouriteDoctor = value; + notifyListeners(); + } + + Future checkIsFavouriteDoctor({required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await bookAppointmentsRepo.isFavouriteDoctor( + patientID: patientID, + projectID: projectID, + clinicID: clinicID, + doctorID: doctorID, + onSuccess: onSuccess, + onError: onError, + ); + + result.fold( + (failure) async { + if (onError != null) { + onError(failure.message); + } + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + if (onError != null) { + onError(apiResponse.errorMessage ?? "Failed to check favorite doctor"); + } + } else if (apiResponse.messageStatus == 1) { + // Check the response for IsFavouriteDoctor flag + bool isFavorite = apiResponse.data['IsFavouriteDoctor'] ?? false; + setIsFavouriteDoctor(isFavorite); + if (onSuccess != null) { + onSuccess(apiResponse.data); + } + } + }, + ); + } + + Future insertFavouriteDoctor({required int patientID, required int projectID, required int clinicID, required int doctorID, required bool isActive, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await bookAppointmentsRepo.insertFavouriteDoctor( + patientID: patientID, + projectID: projectID, + clinicID: clinicID, + doctorID: doctorID, + isActive: isActive, + onSuccess: onSuccess, + onError: onError, + ); + + result.fold( + (failure) async { + if (onError != null) { + onError(failure.message); + } + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + if (onError != null) { + onError(apiResponse.errorMessage ?? "Failed to update favorite doctor"); + } + } else if (apiResponse.messageStatus == 1) { + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse.data); + } + } + }, + ); + } } diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart index 549b8b0d..f79aa770 100644 --- a/lib/presentation/book_appointment/doctor_profile_page.dart +++ b/lib/presentation/book_appointment/doctor_profile_page.dart @@ -21,15 +21,12 @@ import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:provider/provider.dart'; class DoctorProfilePage extends StatelessWidget { - DoctorProfilePage({super.key}); - - late AppState appState; - late BookAppointmentsViewModel bookAppointmentsViewModel; + const DoctorProfilePage({super.key}); @override Widget build(BuildContext context) { - bookAppointmentsViewModel = Provider.of(context, listen: false); - appState = getIt.get(); + final bookAppointmentsViewModel = Provider.of(context, listen: false); + final appState = getIt.get(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: Column( @@ -37,6 +34,40 @@ class DoctorProfilePage extends StatelessWidget { Expanded( child: CollapsingListView( title: LocaleKeys.doctorProfile.tr(), + trailing: Consumer( + builder: (context, viewModel, child) { + return SizedBox( + width: 24.h, + height: 24.h, + child: Utils.buildSvgWithAssets( + icon: viewModel.isFavouriteDoctor ? AppAssets.bookmark_filled_icon : AppAssets.bookmark_icon, + width: 24.h, + height: 24.h, + iconColor: viewModel.isFavouriteDoctor ? AppColors.primaryRedColor : AppColors.textColor, + ).onPress(() async { + viewModel.toggleFavouriteDoctor(); + await viewModel.insertFavouriteDoctor( + patientID: appState.getAuthenticatedUser()!.patientId!, + projectID: viewModel.doctorsProfileResponseModel.projectID ?? 0, + clinicID: viewModel.doctorsProfileResponseModel.clinicID ?? 0, + doctorID: viewModel.doctorsProfileResponseModel.doctorID ?? 0, + isActive: viewModel.isFavouriteDoctor, + onSuccess: (response) { + // Successfully added/removed favorite + print( + viewModel.isFavouriteDoctor ? "Doctor added to favorites" : "Doctor removed from favorites", + ); + }, + onError: (error) { + // Revert the state on error + viewModel.toggleFavouriteDoctor(); + Utils.showToast(error); + }, + ); + }), + ); + }, + ), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start,