doctor favorite in progress.

dev_sultan
Sultan khan 2 days ago
parent f51e37db55
commit d3dfb2e2a5

@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
class ApiConsts { class ApiConsts {
static const maxSmallScreen = 660; 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 // 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 getPatientBloodGroup = "services/PatientVarification.svc/REST/BloodDonation_GetBloodGroupDetails";
static String getPatientBloodAgreement = "Services/PatientVarification.svc/REST/CheckUserAgreementForBloodDonation"; 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 getAiOverViewLabOrders = "Services/Patients.svc/REST/HMGAI_Lab_Analyze_Orders_API";
static String getAiOverViewLabOrder = "Services/Patients.svc/REST/HMGAI_Lab_Analyzer_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 //URL to get doctor free slots
var GET_DOCTOR_FREE_SLOTS = "Services/Doctors.svc/REST/GetDoctorFreeSlots"; 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 //URL to insert appointment
var INSERT_SPECIFIC_APPOINTMENT = "Services/Doctors.svc/REST/InsertSpecificAppointment"; var INSERT_SPECIFIC_APPOINTMENT = "Services/Doctors.svc/REST/InsertSpecificAppointment";

@ -180,6 +180,8 @@ class AppAssets {
static const String ic_rrt_vehicle = '$svgBasePath/ic_rrt_vehicle.svg'; 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_rating_icon = '$svgBasePath/doctor_profile_rating_icon.svg';
static const String doctor_profile_reviews_icon = '$svgBasePath/doctor_profile_reviews_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 waiting_appointment_icon = '$svgBasePath/waitingAppo.svg';
static const String call_for_vitals = '$svgBasePath/call_for_vitals.svg'; static const String call_for_vitals = '$svgBasePath/call_for_vitals.svg';
static const String call_for_doctor = '$svgBasePath/call_for_doctor.svg'; static const String call_for_doctor = '$svgBasePath/call_for_doctor.svg';

@ -45,6 +45,8 @@ abstract class AuthenticationRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> insertPatientDeviceData({required dynamic patientDeviceDataRequest}); Future<Either<Failure, GenericApiModel<dynamic>>> insertPatientDeviceData({required dynamic patientDeviceDataRequest});
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientDeviceData({required dynamic patientDeviceDataRequest}); Future<Either<Failure, GenericApiModel<dynamic>>> getPatientDeviceData({required dynamic patientDeviceDataRequest});
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientBloodType();
} }
class AuthenticationRepoImp implements AuthenticationRepo { class AuthenticationRepoImp implements AuthenticationRepo {
@ -688,4 +690,37 @@ class AuthenticationRepoImp implements AuthenticationRepo {
} }
} }
} }
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientBloodType() async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<dynamic>? 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<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()));
}
}
} }

@ -624,6 +624,9 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState.setAuthenticatedUser(activation.list!.first); _appState.setAuthenticatedUser(activation.list!.first);
_appState.setPrivilegeModelList(activation.list!.first.listPrivilege!); _appState.setPrivilegeModelList(activation.list!.first.listPrivilege!);
_appState.setUserBloodGroup = activation.patientBlodType ?? "N/A"; _appState.setUserBloodGroup = activation.patientBlodType ?? "N/A";
// Fetch patient blood type from new API
await getPatientBloodTypeNew();
} }
// _appState.setUserBloodGroup = (activation.patientBlodType ?? ""); // _appState.setUserBloodGroup = (activation.patientBlodType ?? "");
_appState.setAppAuthToken = activation.authenticationTokenId; _appState.setAppAuthToken = activation.authenticationTokenId;
@ -1150,4 +1153,29 @@ class AuthenticationViewModel extends ChangeNotifier {
_navigationService.pushAndReplace(AppRoutes.landingScreen); _navigationService.pushAndReplace(AppRoutes.landingScreen);
} }
} }
Future<void> 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");
}
}
} }

@ -107,6 +107,12 @@ abstract class BookAppointmentsRepo {
Function(String)? onError}); Function(String)? onError});
Future<Either<Failure, GenericApiModel<AppointmentNearestGateResponseModel>>> getAppointmentNearestGate({required int projectID, required int clinicID}); Future<Either<Failure, GenericApiModel<AppointmentNearestGateResponseModel>>> getAppointmentNearestGate({required int projectID, required int clinicID});
Future<Either<Failure, GenericApiModel<dynamic>>> isFavouriteDoctor(
{required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> 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 { class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@ -1133,4 +1139,86 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel<dynamic>>> isFavouriteDoctor(
{required int patientID, required int projectID, required int clinicID, required int doctorID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapRequest = {"PatientID": patientID, "ProjectID": projectID, "ClinicID": clinicID, "DoctorID": doctorID};
try {
GenericApiModel<dynamic>? 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<dynamic>(
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<Either<Failure, GenericApiModel<dynamic>>> insertFavouriteDoctor(
{required int patientID, required int projectID, required int clinicID, required int doctorID, required bool isActive, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapRequest = {"PatientID": patientID, "ProjectID": projectID, "ClinicID": clinicID, "DoctorID": doctorID, "IsActive": isActive};
try {
GenericApiModel<dynamic>? 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<dynamic>(
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()));
}
}
} }

@ -97,6 +97,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isDoctorRatingDetailsLoading = false; bool isDoctorRatingDetailsLoading = false;
List<DoctorRateDetails> doctorDetailsList = []; List<DoctorRateDetails> doctorDetailsList = [];
bool isFavouriteDoctor = false;
List<FreeSlot> slotsList = []; List<FreeSlot> slotsList = [];
List<TimeSlot> docFreeSlots = []; List<TimeSlot> docFreeSlots = [];
List<TimeSlot> dayEvents = []; List<TimeSlot> dayEvents = [];
@ -625,6 +627,15 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
doctorsProfileResponseModel = apiResponse.data!; doctorsProfileResponseModel = apiResponse.data!;
notifyListeners(); 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) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);
} }
@ -1507,4 +1518,79 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}, },
); );
} }
void toggleFavouriteDoctor() {
isFavouriteDoctor = !isFavouriteDoctor;
notifyListeners();
}
void setIsFavouriteDoctor(bool value) {
isFavouriteDoctor = value;
notifyListeners();
}
Future<void> 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<void> 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);
}
}
},
);
}
} }

@ -21,15 +21,12 @@ import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class DoctorProfilePage extends StatelessWidget { class DoctorProfilePage extends StatelessWidget {
DoctorProfilePage({super.key}); const DoctorProfilePage({super.key});
late AppState appState;
late BookAppointmentsViewModel bookAppointmentsViewModel;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); final bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
appState = getIt.get<AppState>(); final appState = getIt.get<AppState>();
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: Column( body: Column(
@ -37,6 +34,40 @@ class DoctorProfilePage extends StatelessWidget {
Expanded( Expanded(
child: CollapsingListView( child: CollapsingListView(
title: LocaleKeys.doctorProfile.tr(), title: LocaleKeys.doctorProfile.tr(),
trailing: Consumer<BookAppointmentsViewModel>(
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: SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

Loading…
Cancel
Save