diff --git a/assets/images/svg/doctor_contact_icon.svg b/assets/images/svg/doctor_contact_icon.svg
new file mode 100644
index 0000000..00cba33
--- /dev/null
+++ b/assets/images/svg/doctor_contact_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/doctor_profile_icon.svg b/assets/images/svg/doctor_profile_icon.svg
new file mode 100644
index 0000000..120c3c1
--- /dev/null
+++ b/assets/images/svg/doctor_profile_icon.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/images/svg/free_med_delivery_icon.svg b/assets/images/svg/free_med_delivery_icon.svg
new file mode 100644
index 0000000..626e035
--- /dev/null
+++ b/assets/images/svg/free_med_delivery_icon.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/svg/immediate_service_icon.svg b/assets/images/svg/immediate_service_icon.svg
new file mode 100644
index 0000000..261ac9a
--- /dev/null
+++ b/assets/images/svg/immediate_service_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/livecare_book_icon.svg b/assets/images/svg/livecare_book_icon.svg
new file mode 100644
index 0000000..3d38ac7
--- /dev/null
+++ b/assets/images/svg/livecare_book_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/livecare_clinic_icon.svg b/assets/images/svg/livecare_clinic_icon.svg
new file mode 100644
index 0000000..2b60209
--- /dev/null
+++ b/assets/images/svg/livecare_clinic_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/svg/no_visit_icon.svg b/assets/images/svg/no_visit_icon.svg
new file mode 100644
index 0000000..2c8616a
--- /dev/null
+++ b/assets/images/svg/no_visit_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json
index e054238..df0f391 100644
--- a/assets/langs/en-US.json
+++ b/assets/langs/en-US.json
@@ -754,7 +754,7 @@
"thisItemIsNotAvailable": "This item is not available",
"beforeAfterImages": "Before & After Images",
"clinicAcceptLivecare": "No need to wait or visit You can now get medical consultation via Video call (LiveCare service) in The name of the clinic clinic and the doctor will contact you immediately",
- "livecareModalTop": "This Clinic is Accepting livecare services",
+ "livecareModalTop": "This Clinic is Accepting LiveCare services",
"livecarePoint2": "A Specialized doctor will contact you",
"livecarePoint3": "A Doctor will be able to see your full medical file history",
"livecarePoint4": "Free Medicine Delivery Available",
@@ -811,4 +811,5 @@
"pendingActivation": "Pending Activation",
"awaitingApproval": "Awaiting Approval",
"ready": "Ready"
+
}
\ No newline at end of file
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index 106a5ad..2603fb5 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -106,6 +106,13 @@ class AppAssets {
static const String search_by_clinic_icon = '$svgBasePath/search_by_clinic_icon.svg';
static const String search_by_doctor_icon = '$svgBasePath/search_by_doctor_icon.svg';
static const String search_by_region_icon = '$svgBasePath/search_by_region_icon.svg';
+ static const String livecare_clinic_icon = '$svgBasePath/livecare_clinic_icon.svg';
+ static const String immediate_service_icon = '$svgBasePath/immediate_service_icon.svg';
+ static const String no_visit_icon = '$svgBasePath/no_visit_icon.svg';
+ static const String doctor_contact_icon = '$svgBasePath/doctor_contact_icon.svg';
+ static const String free_med_delivery_icon = '$svgBasePath/free_med_delivery_icon.svg';
+ static const String livecare_book_icon = '$svgBasePath/livecare_book_icon.svg';
+ static const String doctor_profile_icon = '$svgBasePath/doctor_profile_icon.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';
diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart
index f86ec97..191b75a 100644
--- a/lib/core/dependencies.dart
+++ b/lib/core/dependencies.dart
@@ -145,13 +145,6 @@ class AppDependencies {
),
);
- getIt.registerLazySingleton(
- () => HabibWalletViewModel(
- habibWalletRepo: getIt(),
- errorHandlerService: getIt(),
- ),
- );
-
getIt.registerLazySingleton(
() => MedicalFileViewModel(
medicalFileRepo: getIt(),
diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart
index 66847a4..1c38a5e 100644
--- a/lib/extensions/string_extensions.dart
+++ b/lib/extensions/string_extensions.dart
@@ -193,11 +193,11 @@ extension EmailValidator on String {
style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 17.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
- Widget toText18({Color? color, bool isBold = false, bool isCenter = false, int? maxlines}) => Text(
+ Widget toText18({Color? color, FontWeight? weight, bool isBold = false, bool isCenter = false, int? maxlines}) => Text(
maxLines: maxlines,
textAlign: isCenter ? TextAlign.center : null,
this,
- style: TextStyle(fontSize: 18.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
+ style: TextStyle(fontSize: 18.fSize, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4),
);
Widget toText19({Color? color, bool isBold = false}) => Text(
diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart
index 5b0bf10..baa7a09 100644
--- a/lib/features/book_appointments/book_appointments_repo.dart
+++ b/lib/features/book_appointments/book_appointments_repo.dart
@@ -1,10 +1,24 @@
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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/doctor_profile_response_model.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class BookAppointmentsRepo {
- Future> getDoctors();
+ Future>>> getClinics();
+
+ Future> getProjects();
+
+ Future>>> getDoctorsList(int clinicID, int projectID, bool isNearest, int doctorId, String doctorName, {isContinueDentalPlan = false});
+
+ Future>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});
+
+ Future>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});
}
class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@@ -14,35 +28,155 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
BookAppointmentsRepoImp({required this.loggerService, required this.apiClient});
@override
- Future> getDoctors() async {
+ Future>>> getClinics() async {
+ Map mapDevice = {};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_CLINICS_LIST_URL,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final list = response['ListClinicCentralized'];
+ // if (list == null || list.isEmpty) {
+ // throw Exception("lab list is empty");
+ // }
+
+ final clinicsList = list.map((item) => GetClinicsListResponseModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: clinicsList,
+ );
+ } 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> getProjects() {
+ // TODO: implement getProjects
+ throw UnimplementedError();
+ }
+
+ @override
+ Future>>> getDoctorsList(int clinicID, int projectID, bool isNearest, int doctorId, String doctorName,
+ {isContinueDentalPlan = false}) async {
+ Map mapDevice = {
+ "ClinicID": clinicID,
+ "ProjectID": projectID,
+ "DoctorName": doctorName, //!= null ? doctorId : 0,
+ "ContinueDentalPlan": isContinueDentalPlan,
+ "IsSearchAppointmnetByClinicID": isContinueDentalPlan ? false : true,
+ "isDentalAllowedBackend": clinicID == 17 ? true : isContinueDentalPlan,
+ "IsGetNearAppointment": isNearest,
+ if (isNearest) "SelectedDate": DateUtil.convertDateToString(DateTime.now()),
+ "License": true
+ };
+
try {
- // Mock API call with delayed response
- final result = await Future.delayed(
- const Duration(seconds: 2),
- () => {
- 'success': true,
- 'data': [
- {
- 'id': '1',
- 'name': 'Dr. Ahmed Hassan',
- 'specialty': 'Cardiology',
- 'experience': '10 years',
- 'rating': 4.8,
- 'image': 'https://example.com/doctor1.jpg'
- },
- ]
- }
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_DOCTORS_LIST_URL,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final list = response['DoctorList'];
+ // if (list == null || list.isEmpty) {
+ // throw Exception("lab list is empty");
+ // }
+
+ final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: doctorsList,
+ );
+ } 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>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ Map mapDevice = {
+ "ClinicID": clinicID,
+ "ProjectID": projectID,
+ "doctorID": doctorId,
+ "License": true,
+ "IsRegistered": true,
+ };
+
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_DOCTOR_PROFILE,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ onError!(error);
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final doctorProfileList = response['DoctorProfileList'];
+ if (doctorProfileList == null || doctorProfileList.isEmpty) {
+ // throw Exception("lab list is empty");
+ onError!("Doctor profile not found");
+ }
+
+ final doctorsList = DoctorsProfileResponseModel.fromJson(doctorProfileList[0]);
- if (result != null && result is Map && result['success'] != null && result['success'] != false) {
- return Right(result);
- } else {
- loggerService.errorLogs(result.toString());
- return Left(ServerFailure(result.toString()));
- }
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: doctorsList,
+ );
+ } 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) {
- loggerService.errorLogs(e.toString());
- return Left(ServerFailure(e.toString()));
+ return Left(UnknownFailure(e.toString()));
}
}
+
+ @override
+ Future>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}) {
+ throw UnimplementedError();
+ }
}
diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart
index 8f096dd..dbe1439 100644
--- a/lib/features/book_appointments/book_appointments_view_model.dart
+++ b/lib/features/book_appointments/book_appointments_view_model.dart
@@ -1,17 +1,159 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/doctor_profile_response_model.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class BookAppointmentsViewModel extends ChangeNotifier {
int selectedTabIndex = 0;
+ bool isClinicsListLoading = false;
+ bool isDoctorsListLoading = false;
+ bool isDoctorProfileLoading = false;
+
+ List clinicsList = [];
+ List _filteredClinicsList = [];
+
+ List get filteredClinicsList => _filteredClinicsList;
+
+ List doctorsList = [];
+
+ GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel();
+ DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel();
+
+ late DoctorsProfileResponseModel doctorsProfileResponseModel;
+
BookAppointmentsRepo bookAppointmentsRepo;
ErrorHandlerService errorHandlerService;
BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService});
+ void initializeFilteredList() {
+ _filteredClinicsList = List.from(clinicsList);
+ notifyListeners();
+ }
+
+ void filterClinics(String? query) {
+ if (query!.isEmpty) {
+ _filteredClinicsList = List.from(clinicsList);
+ } else {
+ _filteredClinicsList = clinicsList.where((clinic) => clinic.clinicDescription?.toLowerCase().contains(query!.toLowerCase()) ?? false).toList();
+ }
+ notifyListeners();
+ }
+
+ initBookAppointmentViewModel() {
+ isClinicsListLoading = true;
+ isDoctorsListLoading = true;
+ isDoctorProfileLoading = true;
+ clinicsList.clear();
+ doctorsList.clear();
+ notifyListeners();
+ }
+
+ setIsDoctorsListLoading(bool value) {
+ if (value) {
+ doctorsList.clear();
+ }
+ isDoctorsListLoading = value;
+ notifyListeners();
+ }
+
+ setIsClinicsListLoading(bool value) {
+ if (value) {
+ clinicsList.clear();
+ }
+ isClinicsListLoading = value;
+ notifyListeners();
+ }
+
+ setSelectedClinic(GetClinicsListResponseModel clinic) {
+ selectedClinic = clinic;
+ notifyListeners();
+ }
+
+ setSelectedDoctor(DoctorsListResponseModel doctor) {
+ selectedDoctor = doctor;
+ notifyListeners();
+ }
+
+ setIsDoctorProfileLoading(bool value) {
+ isDoctorProfileLoading = value;
+ notifyListeners();
+ }
+
+ setDoctorsProfile(DoctorsProfileResponseModel profile) {
+ doctorsProfileResponseModel = profile;
+ notifyListeners();
+ }
+
void onTabChanged(int index) {
selectedTabIndex = index;
notifyListeners();
}
+
+ Future getClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ final result = await bookAppointmentsRepo.getClinics();
+
+ result.fold(
+ (failure) async => await errorHandlerService.handleError(failure: failure),
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ clinicsList = apiResponse.data!;
+ isClinicsListLoading = false;
+ initializeFilteredList();
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
+ //TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
+ Future getDoctorsList(
+ {int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID!, 0, isNearest, doctorId, doctorName);
+
+ result.fold(
+ (failure) async => await errorHandlerService.handleError(failure: failure),
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ doctorsList = apiResponse.data!;
+ isDoctorsListLoading = false;
+ initializeFilteredList();
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
+ Future getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ final result = await bookAppointmentsRepo.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError);
+
+ result.fold(
+ (failure) async {},
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ onError!(apiResponse.errorMessage ?? "Unknown error occurred");
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ doctorsProfileResponseModel = apiResponse.data!;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
}
diff --git a/lib/features/book_appointments/models/doctor_profile_response_model.dart b/lib/features/book_appointments/models/doctor_profile_response_model.dart
new file mode 100644
index 0000000..67e6ffa
--- /dev/null
+++ b/lib/features/book_appointments/models/doctor_profile_response_model.dart
@@ -0,0 +1,196 @@
+class DoctorsProfileResponseModel {
+ int? doctorID;
+ String? doctorName;
+ dynamic doctorNameN;
+ int? clinicID;
+ String? clinicDescription;
+ dynamic clinicDescriptionN;
+ dynamic licenseExpiry;
+ int? employmentType;
+ dynamic setupID;
+ int? projectID;
+ String? projectName;
+ String? nationalityID;
+ String? nationalityName;
+ dynamic nationalityNameN;
+ int? gender;
+ String? genderDescription;
+ dynamic genderDescriptionN;
+ dynamic doctorTitle;
+ dynamic projectNameN;
+ bool? isAllowWaitList;
+ String? titleDescription;
+ dynamic titleDescriptionN;
+ dynamic isRegistered;
+ dynamic isDoctorDummy;
+ bool? isActive;
+ dynamic isDoctorAppointmentDisplayed;
+ bool? doctorClinicActive;
+ dynamic isbookingAllowed;
+ String? doctorCases;
+ dynamic doctorPicture;
+ String? doctorProfileInfo;
+ List? specialty;
+ num? actualDoctorRate;
+ String? consultationFee;
+ double? decimalDoctorRate;
+ String? doctorImageURL;
+ String? doctorMobileNumber;
+ num? doctorRate;
+ num? doctorStarsRate;
+ String? doctorTitleForProfile;
+ bool? isAppointmentAllowed;
+ bool? isDoctorHasPrePostImages;
+ String? nationalityFlagURL;
+ num? noOfPatientsRate;
+ String? qR;
+ int? serviceID;
+
+ DoctorsProfileResponseModel(
+ {this.doctorID,
+ this.doctorName,
+ this.doctorNameN,
+ this.clinicID,
+ this.clinicDescription,
+ this.clinicDescriptionN,
+ this.licenseExpiry,
+ this.employmentType,
+ this.setupID,
+ this.projectID,
+ this.projectName,
+ this.nationalityID,
+ this.nationalityName,
+ this.nationalityNameN,
+ this.gender,
+ this.genderDescription,
+ this.genderDescriptionN,
+ this.doctorTitle,
+ this.projectNameN,
+ this.isAllowWaitList,
+ this.titleDescription,
+ this.titleDescriptionN,
+ this.isRegistered,
+ this.isDoctorDummy,
+ this.isActive,
+ this.isDoctorAppointmentDisplayed,
+ this.doctorClinicActive,
+ this.isbookingAllowed,
+ this.doctorCases,
+ this.doctorPicture,
+ this.doctorProfileInfo,
+ this.specialty,
+ this.actualDoctorRate,
+ this.consultationFee,
+ this.decimalDoctorRate,
+ this.doctorImageURL,
+ this.doctorMobileNumber,
+ this.doctorRate,
+ this.doctorStarsRate,
+ this.doctorTitleForProfile,
+ this.isAppointmentAllowed,
+ this.isDoctorHasPrePostImages,
+ this.nationalityFlagURL,
+ this.noOfPatientsRate,
+ this.qR,
+ this.serviceID});
+
+ DoctorsProfileResponseModel.fromJson(Map json) {
+ doctorID = json['DoctorID'];
+ doctorName = json['DoctorName'];
+ doctorNameN = json['DoctorNameN'];
+ clinicID = json['ClinicID'];
+ clinicDescription = json['ClinicDescription'];
+ clinicDescriptionN = json['ClinicDescriptionN'];
+ licenseExpiry = json['LicenseExpiry'];
+ employmentType = json['EmploymentType'];
+ setupID = json['SetupID'];
+ projectID = json['ProjectID'];
+ projectName = json['ProjectName'];
+ nationalityID = json['NationalityID'];
+ nationalityName = json['NationalityName'];
+ nationalityNameN = json['NationalityNameN'];
+ gender = json['Gender'];
+ genderDescription = json['Gender_Description'];
+ genderDescriptionN = json['Gender_DescriptionN'];
+ doctorTitle = json['DoctorTitle'];
+ projectNameN = json['ProjectNameN'];
+ isAllowWaitList = json['IsAllowWaitList'];
+ titleDescription = json['Title_Description'];
+ titleDescriptionN = json['Title_DescriptionN'];
+ isRegistered = json['IsRegistered'];
+ isDoctorDummy = json['IsDoctorDummy'];
+ isActive = json['IsActive'];
+ isDoctorAppointmentDisplayed = json['IsDoctorAppointmentDisplayed'];
+ doctorClinicActive = json['DoctorClinicActive'];
+ isbookingAllowed = json['IsbookingAllowed'];
+ doctorCases = json['DoctorCases'];
+ doctorPicture = json['DoctorPicture'];
+ doctorProfileInfo = json['DoctorProfileInfo'];
+ specialty = json['Specialty'].cast();
+ actualDoctorRate = json['ActualDoctorRate'];
+ consultationFee = json['ConsultationFee'];
+ decimalDoctorRate = json['DecimalDoctorRate'];
+ doctorImageURL = json['DoctorImageURL'];
+ doctorMobileNumber = json['DoctorMobileNumber'];
+ doctorRate = json['DoctorRate'];
+ doctorStarsRate = json['DoctorStarsRate'];
+ doctorTitleForProfile = json['DoctorTitleForProfile'];
+ isAppointmentAllowed = json['IsAppointmentAllowed'];
+ isDoctorHasPrePostImages = json['IsDoctorHasPrePostImages'];
+ nationalityFlagURL = json['NationalityFlagURL'];
+ noOfPatientsRate = json['NoOfPatientsRate'];
+ qR = json['QR'];
+ serviceID = json['ServiceID'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['DoctorID'] = this.doctorID;
+ data['DoctorName'] = this.doctorName;
+ data['DoctorNameN'] = this.doctorNameN;
+ data['ClinicID'] = this.clinicID;
+ data['ClinicDescription'] = this.clinicDescription;
+ data['ClinicDescriptionN'] = this.clinicDescriptionN;
+ data['LicenseExpiry'] = this.licenseExpiry;
+ data['EmploymentType'] = this.employmentType;
+ data['SetupID'] = this.setupID;
+ data['ProjectID'] = this.projectID;
+ data['ProjectName'] = this.projectName;
+ data['NationalityID'] = this.nationalityID;
+ data['NationalityName'] = this.nationalityName;
+ data['NationalityNameN'] = this.nationalityNameN;
+ data['Gender'] = this.gender;
+ data['Gender_Description'] = this.genderDescription;
+ data['Gender_DescriptionN'] = this.genderDescriptionN;
+ data['DoctorTitle'] = this.doctorTitle;
+ data['ProjectNameN'] = this.projectNameN;
+ data['IsAllowWaitList'] = this.isAllowWaitList;
+ data['Title_Description'] = this.titleDescription;
+ data['Title_DescriptionN'] = this.titleDescriptionN;
+ data['IsRegistered'] = this.isRegistered;
+ data['IsDoctorDummy'] = this.isDoctorDummy;
+ data['IsActive'] = this.isActive;
+ data['IsDoctorAppointmentDisplayed'] = this.isDoctorAppointmentDisplayed;
+ data['DoctorClinicActive'] = this.doctorClinicActive;
+ data['IsbookingAllowed'] = this.isbookingAllowed;
+ data['DoctorCases'] = this.doctorCases;
+ data['DoctorPicture'] = this.doctorPicture;
+ data['DoctorProfileInfo'] = this.doctorProfileInfo;
+ data['Specialty'] = this.specialty;
+ data['ActualDoctorRate'] = this.actualDoctorRate;
+ data['ConsultationFee'] = this.consultationFee;
+ data['DecimalDoctorRate'] = this.decimalDoctorRate;
+ data['DoctorImageURL'] = this.doctorImageURL;
+ data['DoctorMobileNumber'] = this.doctorMobileNumber;
+ data['DoctorRate'] = this.doctorRate;
+ data['DoctorStarsRate'] = this.doctorStarsRate;
+ data['DoctorTitleForProfile'] = this.doctorTitleForProfile;
+ data['IsAppointmentAllowed'] = this.isAppointmentAllowed;
+ data['IsDoctorHasPrePostImages'] = this.isDoctorHasPrePostImages;
+ data['NationalityFlagURL'] = this.nationalityFlagURL;
+ data['NoOfPatientsRate'] = this.noOfPatientsRate;
+ data['QR'] = this.qR;
+ data['ServiceID'] = this.serviceID;
+ return data;
+ }
+}
diff --git a/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart b/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart
new file mode 100644
index 0000000..4cd03a3
--- /dev/null
+++ b/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart
@@ -0,0 +1,252 @@
+class DoctorsListResponseModel {
+ int? clinicID;
+ String? clinicName;
+ String? clinicNameN;
+ String? doctorTitle;
+ int? iD;
+ String? name;
+ int? projectID;
+ String? projectName;
+ num? actualDoctorRate;
+ int? clinicRoomNo;
+ dynamic date;
+ dynamic dayName;
+ num? decimalDoctorRate;
+ dynamic doctorAvailability;
+ int? doctorID;
+ String? doctorImageURL;
+ String? doctorMobileNumber;
+ dynamic doctorProfile;
+ dynamic doctorProfileInfo;
+ num? doctorRate;
+ num? doctorStarsRate;
+ int? employmentType;
+ int? gender;
+ String? genderDescription;
+ int? hISRegionId;
+ bool? isActive;
+ bool? isAllowWaitList;
+ bool? isAppointmentAllowed;
+ bool? isDoctorAllowVedioCall;
+ bool? isDoctorDummy;
+ bool? isDoctorHasPrePostImages;
+ bool? isHMC;
+ bool? isHmg;
+ bool? isLiveCare;
+ String? latitude;
+ String? longitude;
+ String? nationalityFlagURL;
+ String? nationalityID;
+ String? nationalityName;
+ dynamic nearestFreeSlot;
+ int? noOfFreeSlotsAvailable;
+ num? noOfPatientsRate;
+ dynamic originalClinicID;
+ int? personRate;
+ num? projectDistanceInKiloMeters;
+ String? projectNameBottom;
+ String? projectNameTop;
+ String? qR;
+ dynamic qRString;
+ num? rateNumber;
+ String? regionName;
+ dynamic regionNameN;
+ dynamic serviceID;
+ String? setupID;
+ List? speciality;
+ List? specialityN;
+ int? transactionType;
+ int? virtualEmploymentType;
+ dynamic workingHours;
+ dynamic vida3Id;
+
+ DoctorsListResponseModel(
+ {this.clinicID,
+ this.clinicName,
+ this.clinicNameN,
+ this.doctorTitle,
+ this.iD,
+ this.name,
+ this.projectID,
+ this.projectName,
+ this.actualDoctorRate,
+ this.clinicRoomNo,
+ this.date,
+ this.dayName,
+ this.decimalDoctorRate,
+ this.doctorAvailability,
+ this.doctorID,
+ this.doctorImageURL,
+ this.doctorMobileNumber,
+ this.doctorProfile,
+ this.doctorProfileInfo,
+ this.doctorRate,
+ this.doctorStarsRate,
+ this.employmentType,
+ this.gender,
+ this.genderDescription,
+ this.hISRegionId,
+ this.isActive,
+ this.isAllowWaitList,
+ this.isAppointmentAllowed,
+ this.isDoctorAllowVedioCall,
+ this.isDoctorDummy,
+ this.isDoctorHasPrePostImages,
+ this.isHMC,
+ this.isHmg,
+ this.isLiveCare,
+ this.latitude,
+ this.longitude,
+ this.nationalityFlagURL,
+ this.nationalityID,
+ this.nationalityName,
+ this.nearestFreeSlot,
+ this.noOfFreeSlotsAvailable,
+ this.noOfPatientsRate,
+ this.originalClinicID,
+ this.personRate,
+ this.projectDistanceInKiloMeters,
+ this.projectNameBottom,
+ this.projectNameTop,
+ this.qR,
+ this.qRString,
+ this.rateNumber,
+ this.regionName,
+ this.regionNameN,
+ this.serviceID,
+ this.setupID,
+ this.speciality,
+ this.specialityN,
+ this.transactionType,
+ this.virtualEmploymentType,
+ this.workingHours,
+ this.vida3Id});
+
+ DoctorsListResponseModel.fromJson(Map json) {
+ clinicID = json['ClinicID'];
+ clinicName = json['ClinicName'];
+ clinicNameN = json['ClinicNameN'];
+ doctorTitle = json['DoctorTitle'];
+ iD = json['ID'];
+ name = json['Name'];
+ projectID = json['ProjectID'];
+ projectName = json['ProjectName'];
+ actualDoctorRate = json['ActualDoctorRate'];
+ clinicRoomNo = json['ClinicRoomNo'];
+ date = json['Date'];
+ dayName = json['DayName'];
+ decimalDoctorRate = json['DecimalDoctorRate'];
+ doctorAvailability = json['DoctorAvailability'];
+ doctorID = json['DoctorID'];
+ doctorImageURL = json['DoctorImageURL'];
+ doctorMobileNumber = json['DoctorMobileNumber'];
+ doctorProfile = json['DoctorProfile'];
+ doctorProfileInfo = json['DoctorProfileInfo'];
+ doctorRate = json['DoctorRate'];
+ doctorStarsRate = json['DoctorStarsRate'];
+ employmentType = json['EmploymentType'];
+ gender = json['Gender'];
+ genderDescription = json['GenderDescription'];
+ hISRegionId = json['HISRegionId'];
+ isActive = json['IsActive'];
+ isAllowWaitList = json['IsAllowWaitList'];
+ isAppointmentAllowed = json['IsAppointmentAllowed'];
+ isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall'];
+ isDoctorDummy = json['IsDoctorDummy'];
+ isDoctorHasPrePostImages = json['IsDoctorHasPrePostImages'];
+ isHMC = json['IsHMC'];
+ isHmg = json['IsHmg'];
+ isLiveCare = json['IsLiveCare'];
+ latitude = json['Latitude'];
+ longitude = json['Longitude'];
+ nationalityFlagURL = json['NationalityFlagURL'];
+ nationalityID = json['NationalityID'];
+ nationalityName = json['NationalityName'];
+ nearestFreeSlot = json['NearestFreeSlot'];
+ noOfFreeSlotsAvailable = json['NoOfFreeSlotsAvailable'];
+ noOfPatientsRate = json['NoOfPatientsRate'];
+ originalClinicID = json['OriginalClinicID'];
+ personRate = json['PersonRate'];
+ projectDistanceInKiloMeters = json['ProjectDistanceInKiloMeters'];
+ projectNameBottom = json['ProjectNameBottom'];
+ projectNameTop = json['ProjectNameTop'];
+ qR = json['QR'];
+ qRString = json['QRString'];
+ rateNumber = json['RateNumber'];
+ regionName = json['RegionName'];
+ regionNameN = json['RegionNameN'];
+ serviceID = json['ServiceID'];
+ setupID = json['SetupID'];
+ speciality = json['Speciality'].cast();
+ specialityN = json['SpecialityN'].cast();
+ transactionType = json['TransactionType'];
+ virtualEmploymentType = json['VirtualEmploymentType'];
+ workingHours = json['WorkingHours'];
+ vida3Id = json['vida3Id'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['ClinicID'] = this.clinicID;
+ data['ClinicName'] = this.clinicName;
+ data['ClinicNameN'] = this.clinicNameN;
+ data['DoctorTitle'] = this.doctorTitle;
+ data['ID'] = this.iD;
+ data['Name'] = this.name;
+ data['ProjectID'] = this.projectID;
+ data['ProjectName'] = this.projectName;
+ data['ActualDoctorRate'] = this.actualDoctorRate;
+ data['ClinicRoomNo'] = this.clinicRoomNo;
+ data['Date'] = this.date;
+ data['DayName'] = this.dayName;
+ data['DecimalDoctorRate'] = this.decimalDoctorRate;
+ data['DoctorAvailability'] = this.doctorAvailability;
+ data['DoctorID'] = this.doctorID;
+ data['DoctorImageURL'] = this.doctorImageURL;
+ data['DoctorMobileNumber'] = this.doctorMobileNumber;
+ data['DoctorProfile'] = this.doctorProfile;
+ data['DoctorProfileInfo'] = this.doctorProfileInfo;
+ data['DoctorRate'] = this.doctorRate;
+ data['DoctorStarsRate'] = this.doctorStarsRate;
+ data['EmploymentType'] = this.employmentType;
+ data['Gender'] = this.gender;
+ data['GenderDescription'] = this.genderDescription;
+ data['HISRegionId'] = this.hISRegionId;
+ data['IsActive'] = this.isActive;
+ data['IsAllowWaitList'] = this.isAllowWaitList;
+ data['IsAppointmentAllowed'] = this.isAppointmentAllowed;
+ data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall;
+ data['IsDoctorDummy'] = this.isDoctorDummy;
+ data['IsDoctorHasPrePostImages'] = this.isDoctorHasPrePostImages;
+ data['IsHMC'] = this.isHMC;
+ data['IsHmg'] = this.isHmg;
+ data['IsLiveCare'] = this.isLiveCare;
+ data['Latitude'] = this.latitude;
+ data['Longitude'] = this.longitude;
+ data['NationalityFlagURL'] = this.nationalityFlagURL;
+ data['NationalityID'] = this.nationalityID;
+ data['NationalityName'] = this.nationalityName;
+ data['NearestFreeSlot'] = this.nearestFreeSlot;
+ data['NoOfFreeSlotsAvailable'] = this.noOfFreeSlotsAvailable;
+ data['NoOfPatientsRate'] = this.noOfPatientsRate;
+ data['OriginalClinicID'] = this.originalClinicID;
+ data['PersonRate'] = this.personRate;
+ data['ProjectDistanceInKiloMeters'] = this.projectDistanceInKiloMeters;
+ data['ProjectNameBottom'] = this.projectNameBottom;
+ data['ProjectNameTop'] = this.projectNameTop;
+ data['QR'] = this.qR;
+ data['QRString'] = this.qRString;
+ data['RateNumber'] = this.rateNumber;
+ data['RegionName'] = this.regionName;
+ data['RegionNameN'] = this.regionNameN;
+ data['ServiceID'] = this.serviceID;
+ data['SetupID'] = this.setupID;
+ data['Speciality'] = this.speciality;
+ data['SpecialityN'] = this.specialityN;
+ data['TransactionType'] = this.transactionType;
+ data['VirtualEmploymentType'] = this.virtualEmploymentType;
+ data['WorkingHours'] = this.workingHours;
+ data['vida3Id'] = this.vida3Id;
+ return data;
+ }
+}
diff --git a/lib/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart b/lib/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart
new file mode 100644
index 0000000..0b0fa13
--- /dev/null
+++ b/lib/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart
@@ -0,0 +1,36 @@
+class GetClinicsListResponseModel {
+ int? clinicID;
+ String? clinicDescription;
+ String? clinicDescriptionN;
+ int? age;
+ int? gender;
+ bool? isLiveCareClinicAndOnline;
+ int? liveCareClinicID;
+ int? liveCareServiceID;
+
+ GetClinicsListResponseModel({this.clinicID, this.clinicDescription, this.clinicDescriptionN, this.age, this.gender, this.isLiveCareClinicAndOnline, this.liveCareClinicID, this.liveCareServiceID});
+
+ GetClinicsListResponseModel.fromJson(Map json) {
+ clinicID = json['ClinicID'];
+ clinicDescription = json['ClinicDescription'];
+ clinicDescriptionN = json['ClinicDescriptionN'];
+ age = json['Age'];
+ gender = json['Gender'];
+ isLiveCareClinicAndOnline = json['IsLiveCareClinicAndOnline'];
+ liveCareClinicID = json['LiveCareClinicID'];
+ liveCareServiceID = json['LiveCareServiceID'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['ClinicID'] = this.clinicID;
+ data['ClinicDescription'] = this.clinicDescription;
+ data['ClinicDescriptionN'] = this.clinicDescriptionN;
+ data['Age'] = this.age;
+ data['Gender'] = this.gender;
+ data['IsLiveCareClinicAndOnline'] = this.isLiveCareClinicAndOnline;
+ data['LiveCareClinicID'] = this.liveCareClinicID;
+ data['LiveCareServiceID'] = this.liveCareServiceID;
+ return data;
+ }
+}
diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart
index 0f9a325..6613320 100644
--- a/lib/presentation/book_appointment/book_appointment_page.dart
+++ b/lib/presentation/book_appointment/book_appointment_page.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
@@ -9,6 +11,7 @@ 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/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@@ -25,14 +28,25 @@ class BookAppointmentPage extends StatefulWidget {
class _BookAppointmentPageState extends State {
late AppState appState;
+ late BookAppointmentsViewModel bookAppointmentsViewModel;
+
+ @override
+ void initState() {
+ scheduleMicrotask(() {
+ bookAppointmentsViewModel.initBookAppointmentViewModel();
+ });
+ super.initState();
+ }
@override
Widget build(BuildContext context) {
+ bookAppointmentsViewModel = Provider.of(context, listen: false);
appState = getIt.get();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.bookAppo.tr(context: context),
+ isLeading: false,
child: SingleChildScrollView(
child: Consumer(builder: (context, bookAppointmentsVM, child) {
return Column(
@@ -95,6 +109,7 @@ class _BookAppointmentPageState extends State {
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
).onPress(() {
+ bookAppointmentsViewModel.setIsClinicsListLoading(true);
Navigator.of(context).push(
FadePage(
page: SelectClinicPage(),
@@ -122,7 +137,13 @@ class _BookAppointmentPageState extends State {
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
- ).onPress(() {}),
+ ).onPress(() {
+ Navigator.of(context).push(
+ FadePage(
+ page: SearchDoctorByName(),
+ ),
+ );
+ }),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart
new file mode 100644
index 0000000..0bb1098
--- /dev/null
+++ b/lib/presentation/book_appointment/doctor_profile_page.dart
@@ -0,0 +1,129 @@
+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_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.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/book_appointments/book_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.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/app_custom_chip_widget.dart';
+import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
+import 'package:provider/provider.dart';
+
+class DoctorProfilePage extends StatelessWidget {
+ DoctorProfilePage({super.key});
+
+ late AppState appState;
+ late BookAppointmentsViewModel bookAppointmentsViewModel;
+
+ @override
+ Widget build(BuildContext context) {
+ bookAppointmentsViewModel = Provider.of(context, listen: false);
+ appState = getIt.get();
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: Column(
+ children: [
+ Expanded(
+ child: CollapsingListView(
+ title: "Doctor Profile".needTranslation,
+ child: SingleChildScrollView(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 24.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ Image.network(
+ bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL!,
+ width: 63.h,
+ height: 63.h,
+ fit: BoxFit.fill,
+ ).circle(100),
+ SizedBox(width: 8.h),
+ Column(
+ children: [
+ ("${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorTitleForProfile} ${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName}")
+ .toString()
+ .toText28(isBold: true),
+ (bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.isNotEmpty ? bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.first : "")
+ .toString()
+ .toText18(weight: FontWeight.w500, color: AppColors.primaryRedColor),
+ ],
+ ),
+ ],
+ ),
+ Image.network(
+ bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL!,
+ width: 32.h,
+ height: 32.h,
+ fit: BoxFit.fill,
+ ),
+ ],
+ ),
+ SizedBox(height: 12.h),
+ Wrap(
+ direction: Axis.horizontal,
+ spacing: 3.h,
+ runSpacing: 4.h,
+ children: [
+ AppCustomChipWidget(
+ icon: AppAssets.rating_icon,
+ iconColor: AppColors.ratingColorYellow,
+ labelText: "Rating: ${bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate}".needTranslation,
+ ),
+ AppCustomChipWidget(
+ icon: AppAssets.rating_icon,
+ iconColor: AppColors.ratingColorYellow,
+ labelText: "Reviews: ${bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate}".needTranslation,
+ ),
+ ],
+ ),
+ SizedBox(height: 16.h),
+ Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
+ SizedBox(height: 16.h),
+ "Biography".toText14(weight: FontWeight.w600, color: AppColors.textColor),
+ bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo!.toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor),
+ ],
+ ).paddingSymmetrical(24.h, 0.h),
+ ),
+ ),
+ ),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: true,
+ ),
+ child: CustomButton(
+ text: "View available appointments".needTranslation,
+ onPressed: () {
+ showCommonBottomSheetWithoutHeight(context, title: "".needTranslation, child: Utils.getLoadingWidget(), callBackFunc: () {}, isFullScreen: false);
+ },
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 50.h,
+ icon: AppAssets.calendar,
+ iconColor: AppColors.whiteColor,
+ iconSize: 20.h,
+ ).paddingSymmetrical(24.h, 24.h),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/book_appointment/search_doctor_by_name.dart b/lib/presentation/book_appointment/search_doctor_by_name.dart
new file mode 100644
index 0000000..6180932
--- /dev/null
+++ b/lib/presentation/book_appointment/search_doctor_by_name.dart
@@ -0,0 +1,76 @@
+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_state.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.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/book_appointments/book_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/input_widget.dart';
+
+class SearchDoctorByName extends StatefulWidget {
+ const SearchDoctorByName({super.key});
+
+ @override
+ State createState() => _SearchDoctorByNameState();
+}
+
+class _SearchDoctorByNameState extends State {
+ TextEditingController searchEditingController = TextEditingController();
+
+ FocusNode textFocusNode = FocusNode();
+
+ late AppState appState;
+ late BookAppointmentsViewModel bookAppointmentsViewModel;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: CollapsingListView(
+ title: "Choose Doctor".needTranslation,
+ child: SingleChildScrollView(
+ child: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 24.h),
+ child: Column(
+ children: [
+ SizedBox(height: 16.h),
+ TextInputWidget(
+ labelText: LocaleKeys.search.tr(context: context),
+ hintText: LocaleKeys.doctorName.tr(context: context),
+ controller: searchEditingController,
+ isEnable: true,
+ prefix: null,
+ autoFocus: false,
+ isBorderAllowed: false,
+ keyboardType: TextInputType.text,
+ focusNode: textFocusNode,
+ suffix: searchEditingController.text.isNotEmpty
+ ? GestureDetector(
+ onTap: () {
+ searchEditingController.clear();
+ bookAppointmentsViewModel.filterClinics("");
+ textFocusNode.unfocus();
+ },
+ child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown),
+ )
+ : null,
+ onChange: (value) {
+ bookAppointmentsViewModel.filterClinics(value!);
+ },
+ padding: EdgeInsets.symmetric(
+ vertical: ResponsiveExtension(10).h,
+ horizontal: ResponsiveExtension(15).h,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart
index 496f7d5..214d4ad 100644
--- a/lib/presentation/book_appointment/select_clinic_page.dart
+++ b/lib/presentation/book_appointment/select_clinic_page.dart
@@ -1,8 +1,26 @@
+import 'dart:async';
+
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
+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/utils/size_utils.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/book_appointments/book_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/select_livecare_clinic_page.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic_card.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/input_widget.dart';
+import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
+import 'package:provider/provider.dart';
class SelectClinicPage extends StatefulWidget {
const SelectClinicPage({super.key});
@@ -12,18 +30,127 @@ class SelectClinicPage extends StatefulWidget {
}
class _SelectClinicPageState extends State {
+ TextEditingController searchEditingController = TextEditingController();
+ FocusNode textFocusNode = FocusNode();
+
+ late AppState appState;
+ late BookAppointmentsViewModel bookAppointmentsViewModel;
+
+ @override
+ void initState() {
+ scheduleMicrotask(() {
+ bookAppointmentsViewModel.getClinics();
+ });
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ textFocusNode.dispose();
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
+ bookAppointmentsViewModel = Provider.of(context, listen: false);
+ appState = getIt.get();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.selectClinic.tr(context: context),
child: SingleChildScrollView(
- child: Column(
- children: [],
+ child: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 24.h),
+ child: Consumer(builder: (context, bookAppointmentsVM, child) {
+ return Column(
+ children: [
+ SizedBox(height: 16.h),
+ TextInputWidget(
+ labelText: LocaleKeys.search.tr(context: context),
+ hintText: LocaleKeys.clinicName.tr(context: context),
+ controller: searchEditingController,
+ isEnable: true,
+ prefix: null,
+ autoFocus: false,
+ isBorderAllowed: false,
+ keyboardType: TextInputType.text,
+ focusNode: textFocusNode,
+ suffix: searchEditingController.text.isNotEmpty
+ ? GestureDetector(
+ onTap: () {
+ searchEditingController.clear();
+ bookAppointmentsViewModel.filterClinics("");
+ textFocusNode.unfocus();
+ },
+ child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown),
+ )
+ : null,
+ onChange: (value) {
+ bookAppointmentsViewModel.filterClinics(value!);
+ },
+ padding: EdgeInsets.symmetric(
+ vertical: ResponsiveExtension(10).h,
+ horizontal: ResponsiveExtension(15).h,
+ ),
+ ),
+ ListView.separated(
+ padding: EdgeInsets.only(top: 24.h),
+ shrinkWrap: true,
+ physics: NeverScrollableScrollPhysics(),
+ itemCount: bookAppointmentsVM.isClinicsListLoading ? 5 : bookAppointmentsVM.filteredClinicsList.length,
+ itemBuilder: (context, index) {
+ return bookAppointmentsVM.isClinicsListLoading
+ ? ClinicCard(
+ clinicsListResponseModel: GetClinicsListResponseModel(),
+ isLoading: bookAppointmentsVM.isClinicsListLoading,
+ )
+ : AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 500),
+ child: SlideAnimation(
+ verticalOffset: 100.0,
+ child: FadeInAnimation(
+ child: AnimatedContainer(
+ duration: Duration(milliseconds: 300),
+ curve: Curves.easeInOut,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
+ child: ClinicCard(
+ clinicsListResponseModel: bookAppointmentsVM.filteredClinicsList[index],
+ isLoading: bookAppointmentsVM.isClinicsListLoading,
+ ).onPress(() {
+ onClinicSelected(bookAppointmentsVM.filteredClinicsList[index]);
+ }),
+ ),
+ ),
+ ),
+ );
+ },
+ separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
+ ),
+ ],
+ );
+ }),
),
),
),
);
}
+
+ void onClinicSelected(GetClinicsListResponseModel clinic) {
+ bookAppointmentsViewModel.setSelectedClinic(clinic);
+ bookAppointmentsViewModel.setIsDoctorsListLoading(true);
+ if (clinic.isLiveCareClinicAndOnline ?? false) {
+ Navigator.of(context).push(
+ FadePage(
+ page: SelectLivecareClinicPage(),
+ ),
+ );
+ } else {
+ Navigator.of(context).push(
+ FadePage(
+ page: SelectDoctorPage(),
+ ),
+ );
+ }
+ }
}
diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart
new file mode 100644
index 0000000..0986c29
--- /dev/null
+++ b/lib/presentation/book_appointment/select_doctor_page.dart
@@ -0,0 +1,155 @@
+import 'dart:async';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
+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/utils/size_utils.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/book_appointments/book_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_card.dart';
+import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.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/transitions/fade_page.dart';
+import 'package:provider/provider.dart';
+
+class SelectDoctorPage extends StatefulWidget {
+ SelectDoctorPage({super.key});
+
+ @override
+ State createState() => _SelectDoctorPageState();
+}
+
+class _SelectDoctorPageState extends State {
+ TextEditingController searchEditingController = TextEditingController();
+
+ FocusNode textFocusNode = FocusNode();
+
+ late AppState appState;
+ late BookAppointmentsViewModel bookAppointmentsViewModel;
+
+ @override
+ void initState() {
+ scheduleMicrotask(() {
+ bookAppointmentsViewModel.getDoctorsList();
+ });
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ bookAppointmentsViewModel = Provider.of(context, listen: false);
+ appState = getIt.get();
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: CollapsingListView(
+ title: "Choose Doctor".needTranslation,
+ child: SingleChildScrollView(
+ child: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 24.h),
+ child: Consumer(builder: (context, bookAppointmentsVM, child) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // TODO: Implement doctor filter functionality
+ SizedBox(height: 16.h),
+ TextInputWidget(
+ labelText: LocaleKeys.search.tr(context: context),
+ hintText: LocaleKeys.doctorName.tr(context: context),
+ controller: searchEditingController,
+ isEnable: true,
+ prefix: null,
+ autoFocus: false,
+ isBorderAllowed: false,
+ keyboardType: TextInputType.text,
+ focusNode: textFocusNode,
+ suffix: searchEditingController.text.isNotEmpty
+ ? GestureDetector(
+ onTap: () {
+ searchEditingController.clear();
+ bookAppointmentsViewModel.filterClinics("");
+ textFocusNode.unfocus();
+ },
+ child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown),
+ )
+ : null,
+ onChange: (value) {
+ bookAppointmentsViewModel.filterClinics(value!);
+ },
+ padding: EdgeInsets.symmetric(
+ vertical: ResponsiveExtension(10).h,
+ horizontal: ResponsiveExtension(15).h,
+ ),
+ ),
+ ListView.separated(
+ padding: EdgeInsets.only(top: 24.h),
+ shrinkWrap: true,
+ physics: NeverScrollableScrollPhysics(),
+ itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 : bookAppointmentsVM.doctorsList.length,
+ itemBuilder: (context, index) {
+ return bookAppointmentsVM.isDoctorsListLoading
+ ? DoctorCard(
+ doctorsListResponseModel: DoctorsListResponseModel(),
+ isLoading: true,
+ )
+ : AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 500),
+ child: SlideAnimation(
+ verticalOffset: 100.0,
+ child: FadeInAnimation(
+ child: AnimatedContainer(
+ duration: Duration(milliseconds: 300),
+ curve: Curves.easeInOut,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
+ child: DoctorCard(
+ doctorsListResponseModel: bookAppointmentsVM.doctorsList[index],
+ isLoading: false,
+ ).onPress(() async {
+ bookAppointmentsVM.setSelectedDoctor(bookAppointmentsVM.doctorsList[index]);
+ // bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel());
+ LoaderBottomSheet.showLoader();
+ await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
+ LoaderBottomSheet.hideLoader();
+ Navigator.of(context).push(
+ FadePage(
+ page: DoctorProfilePage(),
+ ),
+ );
+ }, onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: err),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ });
+ }),
+ ),
+ ),
+ ),
+ );
+ },
+ separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
+ ),
+ ],
+ );
+ }),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/book_appointment/select_livecare_clinic_page.dart b/lib/presentation/book_appointment/select_livecare_clinic_page.dart
new file mode 100644
index 0000000..ef682c3
--- /dev/null
+++ b/lib/presentation/book_appointment/select_livecare_clinic_page.dart
@@ -0,0 +1,146 @@
+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/utils/size_utils.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/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart';
+import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.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/transitions/fade_page.dart';
+
+class SelectLivecareClinicPage extends StatelessWidget {
+ const SelectLivecareClinicPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: Column(
+ children: [
+ Expanded(
+ child: CollapsingListView(
+ title: LocaleKeys.livecare.tr(context: context),
+ child: SingleChildScrollView(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 24.h),
+ LocaleKeys.livecareModalTop.tr(context: context).toText18(isBold: true),
+ SizedBox(height: 40.h),
+ Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.immediate_service_icon, width: 58.h, height: 58.h),
+ SizedBox(width: 18.h),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Immediate service".needTranslation.toText18(color: AppColors.textColor, isBold: true),
+ "No need to wait, you will get medical consultation immediately via video call".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
+ ],
+ ),
+ ),
+ ],
+ ),
+ SizedBox(height: 24.h),
+ Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.no_visit_icon, width: 58.h, height: 58.h),
+ SizedBox(width: 18.h),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "No visit required".needTranslation.toText18(color: AppColors.textColor, isBold: true),
+ LocaleKeys.livecarePoint5.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
+ ],
+ ),
+ ),
+ ],
+ ),
+ SizedBox(height: 24.h),
+ Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.doctor_contact_icon, width: 58.h, height: 58.h),
+ SizedBox(width: 18.h),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Doctor will contact".needTranslation.toText18(color: AppColors.textColor, isBold: true),
+ "A specialised doctor will contact you and will be able to view your medical history".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
+ ],
+ ),
+ ),
+ ],
+ ),
+ SizedBox(height: 24.h),
+ Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.free_med_delivery_icon, width: 58.h, height: 58.h),
+ SizedBox(width: 18.h),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Free medicine delivery".needTranslation.toText18(color: AppColors.textColor, isBold: true),
+ "Offers free medicine delivery for the LiveCare appointment".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ],
+ ).paddingSymmetrical(24.h, 0.h),
+ ),
+ ),
+ ),
+ Column(
+ children: [
+ CustomButton(
+ text: "Yes please, I am in a hurry".needTranslation,
+ onPressed: () {},
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 50.h,
+ icon: AppAssets.livecare_book_icon,
+ iconColor: AppColors.whiteColor,
+ iconSize: 18.h,
+ ).paddingSymmetrical(24.h, 0.h),
+ SizedBox(height: 16.h),
+ CustomButton(
+ text: "No, Thanks. I would like a physical visit".needTranslation,
+ onPressed: () {
+ Navigator.of(context).pop();
+ Navigator.of(context).push(
+ FadePage(
+ page: SelectDoctorPage(),
+ ),
+ );
+ },
+ backgroundColor: AppColors.secondaryLightRedColor,
+ borderColor: AppColors.secondaryLightRedColor,
+ textColor: AppColors.primaryRedColor,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 50.h,
+ ).paddingSymmetrical(24.h, 0.h),
+ SizedBox(height: 24.h),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/book_appointment/widgets/clinic_card.dart b/lib/presentation/book_appointment/widgets/clinic_card.dart
new file mode 100644
index 0000000..5cfc624
--- /dev/null
+++ b/lib/presentation/book_appointment/widgets/clinic_card.dart
@@ -0,0 +1,42 @@
+import 'package:flutter/material.dart';
+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/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/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+
+class ClinicCard extends StatelessWidget {
+ ClinicCard({super.key, required this.clinicsListResponseModel, required this.isLoading});
+
+ GetClinicsListResponseModel clinicsListResponseModel;
+ bool isLoading;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ padding: EdgeInsets.all(16.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: false,
+ ),
+ child: Column(
+ children: [
+ Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
+ "".toText16(isBold: true).toShimmer2(isShow: isLoading),
+ (clinicsListResponseModel.isLiveCareClinicAndOnline ?? true)
+ ? Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain).toShimmer2(isShow: isLoading)
+ : SizedBox.shrink(),
+ ]),
+ SizedBox(height: 16.h),
+ Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
+ Expanded(child: (isLoading ? "Cardiology" : clinicsListResponseModel.clinicDescription!).toText16(isBold: true).toShimmer2(isShow: isLoading)),
+ Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading),
+ ]),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/book_appointment/widgets/doctor_card.dart b/lib/presentation/book_appointment/widgets/doctor_card.dart
new file mode 100644
index 0000000..648f5ef
--- /dev/null
+++ b/lib/presentation/book_appointment/widgets/doctor_card.dart
@@ -0,0 +1,121 @@
+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/utils/size_utils.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/book_appointments/models/resp_models/doctors_list_response_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/chip/app_custom_chip_widget.dart';
+
+class DoctorCard extends StatelessWidget {
+ DoctorCard({super.key, required this.doctorsListResponseModel, required this.isLoading});
+
+ DoctorsListResponseModel doctorsListResponseModel;
+ bool isLoading = false;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: false,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(14.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Image.network(
+ isLoading
+ ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png"
+ : doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png",
+ width: 63.h,
+ height: 63.h,
+ fit: BoxFit.fill,
+ ).circle(100).toShimmer2(isShow: isLoading),
+ SizedBox(width: 8.h),
+ Expanded(
+ flex: 9,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ SizedBox(
+ width: MediaQuery.of(context).size.width * 0.49,
+ child: (isLoading ? "Dr John Smith" : "${doctorsListResponseModel.doctorTitle} ${doctorsListResponseModel.name}").toString().toText16(isBold: true, maxlines: 1),
+ ).toShimmer2(isShow: isLoading),
+ Image.network(
+ isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
+ width: 20.h,
+ height: 15.h,
+ fit: BoxFit.fill,
+ ).toShimmer2(isShow: isLoading),
+ ],
+ ),
+ SizedBox(height: 2.h),
+ (isLoading
+ ? "Consultant Cardiologist"
+ : doctorsListResponseModel.speciality!.isNotEmpty
+ ? doctorsListResponseModel.speciality!.first
+ : "")
+ .toString()
+ .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1)
+ .toShimmer2(isShow: isLoading),
+ ],
+ ),
+ ),
+ Expanded(
+ flex: 1,
+ child: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown).toShimmer2(isShow: isLoading),
+ ),
+ ],
+ ),
+ SizedBox(height: 12.h),
+ Wrap(
+ direction: Axis.horizontal,
+ spacing: 3.h,
+ runSpacing: 4.h,
+ children: [
+ AppCustomChipWidget(
+ labelText: "Clinic: ${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}".needTranslation,
+ ).toShimmer2(isShow: isLoading),
+ AppCustomChipWidget(
+ labelText: "Branch: ${isLoading ? "Olaya Hospital" : doctorsListResponseModel.projectName}".needTranslation,
+ ).toShimmer2(isShow: isLoading),
+ AppCustomChipWidget(
+ icon: AppAssets.rating_icon,
+ iconColor: AppColors.ratingColorYellow,
+ labelText: "Rating: ${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".needTranslation,
+ ).toShimmer2(isShow: isLoading),
+ ],
+ ),
+ SizedBox(height: 12.h),
+ CustomButton(
+ text: LocaleKeys.bookAppo.tr(context: context),
+ onPressed: () {},
+ backgroundColor: Color(0xffFEE9EA),
+ borderColor: Color(0xffFEE9EA),
+ textColor: Color(0xffED1C2B),
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 40.h,
+ icon: AppAssets.add_icon,
+ iconColor: AppColors.primaryRedColor,
+ iconSize: 15.h,
+ ).toShimmer2(isShow: isLoading),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart
index 26e5147..50d8032 100644
--- a/lib/presentation/home/landing_page.dart
+++ b/lib/presentation/home/landing_page.dart
@@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/authentication/quick_login.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart';
@@ -89,24 +90,26 @@ class _LandingPageState extends State {
children: [
appState.isAuthenticated
? WelcomeWidget(
- onTap: () {},
- name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'),
- imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
- )
+ onTap: () {},
+ name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'),
+ imageUrl: appState
+ .getAuthenticatedUser()
+ ?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
+ )
: CustomButton(
- text: LocaleKeys.loginOrRegister.tr(context: context),
- onPressed: () async {
- await authVM.onLoginPressed();
- },
- backgroundColor: Color(0xffFEE9EA),
- borderColor: Color(0xffFEE9EA),
- textColor: Color(0xffED1C2B),
- fontSize: 16,
- fontWeight: FontWeight.w500,
- borderRadius: 12,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 50,
- ),
+ text: LocaleKeys.loginOrRegister.tr(context: context),
+ onPressed: () async {
+ await authVM.onLoginPressed();
+ },
+ backgroundColor: Color(0xffFEE9EA),
+ borderColor: Color(0xffFEE9EA),
+ textColor: Color(0xffED1C2B),
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 50,
+ ),
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 20, width: 20).onPress(() {
@@ -141,153 +144,156 @@ class _LandingPageState extends State {
SizedBox(height: 16.h),
appState.isAuthenticated
? Column(
- children: [
- Container(
- width: double.infinity,
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24,
- ),
- child: Padding(
- padding: EdgeInsets.all(12.h),
- child: Column(
- children: [
- Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),
- SizedBox(height: 12.h),
- "You do not have any upcoming appointment. Please book an appointment".toText12(isCenter: true),
- SizedBox(height: 12.h),
- CustomButton(
- text: LocaleKeys.bookAppo.tr(context: context),
- onPressed: () {
- Navigator.of(context).pushReplacement(
- MaterialPageRoute(builder: (BuildContext context) => LandingPage()),
- );
- },
- backgroundColor: Color(0xffFEE9EA),
- borderColor: Color(0xffFEE9EA),
- textColor: Color(0xffED1C2B),
- fontSize: 14,
- fontWeight: FontWeight.w500,
- borderRadius: 12,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 40,
- icon: AppAssets.add_icon,
- iconColor: AppColors.primaryRedColor,
+ children: [
+ Container(
+ width: double.infinity,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(12.h),
+ child: Column(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),
+ SizedBox(height: 12.h),
+ "You do not have any upcoming appointment. Please book an appointment".toText12(isCenter: true),
+ SizedBox(height: 12.h),
+ CustomButton(
+ text: LocaleKeys.bookAppo.tr(context: context),
+ onPressed: () {
+ Navigator.of(context)
+ .push(
+ FadePage(
+ page: BookAppointmentPage(),
),
- ],
- ),
- ),
- ).paddingSymmetrical(24.h, 0.h),
- SizedBox(height: 12.h),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- "Quick Links".toText16(isBold: true),
- Row(
- children: [
- "View medical file".toText12(color: AppColors.primaryRedColor),
- SizedBox(width: 2.h),
- Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
- ],
- ),
- ],
- ).paddingSymmetrical(24.h, 0.h).onPress(() {
- Navigator.of(context).push(
- FadePage(
- page: MedicalFilePage(),
- ),
- );
- }),
- SizedBox(height: 12.h),
- Container(
- height: 127.h,
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24,
+ );
+ },
+ backgroundColor: Color(0xffFEE9EA),
+ borderColor: Color(0xffFEE9EA),
+ textColor: Color(0xffED1C2B),
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 40,
+ icon: AppAssets.add_icon,
+ iconColor: AppColors.primaryRedColor,
),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Column(
- children: [
- Expanded(
- child: ListView.separated(
- scrollDirection: Axis.horizontal,
- itemCount: LandingPageData.getLoggedInServiceCardsList.length,
- shrinkWrap: true,
- padding: const EdgeInsets.only(left: 0, right: 8),
- itemBuilder: (context, index) {
- return AnimationConfiguration.staggeredList(
- position: index,
- duration: const Duration(milliseconds: 1000),
- child: SlideAnimation(
- horizontalOffset: 100.0,
- child: FadeInAnimation(
- child: SmallServiceCard(
- icon: LandingPageData.getLoggedInServiceCardsList[index].icon,
- title: LandingPageData.getLoggedInServiceCardsList[index].title,
- subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle,
- iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor,
- textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor,
- backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor,
- isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold,
- serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName,
- ),
- ),
- ),
- );
- },
- separatorBuilder: (BuildContext cxt, int index) => 0.width,
+ ],
+ ),
+ ),
+ ).paddingSymmetrical(24.h, 0.h),
+ SizedBox(height: 12.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ "Quick Links".toText16(isBold: true),
+ Row(
+ children: [
+ "View medical file".toText12(color: AppColors.primaryRedColor),
+ SizedBox(width: 2.h),
+ Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
+ ],
+ ),
+ ],
+ ).paddingSymmetrical(24.h, 0.h).onPress(() {
+ Navigator.of(context).push(
+ FadePage(
+ page: MedicalFilePage(),
+ ),
+ );
+ }),
+ SizedBox(height: 12.h),
+ Container(
+ height: 127.h,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ children: [
+ Expanded(
+ child: ListView.separated(
+ scrollDirection: Axis.horizontal,
+ itemCount: LandingPageData.getLoggedInServiceCardsList.length,
+ shrinkWrap: true,
+ padding: const EdgeInsets.only(left: 0, right: 8),
+ itemBuilder: (context, index) {
+ return AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 1000),
+ child: SlideAnimation(
+ horizontalOffset: 100.0,
+ child: FadeInAnimation(
+ child: SmallServiceCard(
+ icon: LandingPageData.getLoggedInServiceCardsList[index].icon,
+ title: LandingPageData.getLoggedInServiceCardsList[index].title,
+ subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle,
+ iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor,
+ textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor,
+ backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor,
+ isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold,
+ serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName,
+ ),
+ ),
),
- ),
- ],
+ );
+ },
+ separatorBuilder: (BuildContext cxt, int index) => 0.width,
),
),
- ).paddingSymmetrical(24.h, 0.h),
- ],
- )
- : Container(
- height: 127.h,
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24,
+ ],
),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Column(
- children: [
- Expanded(
- child: ListView.separated(
- scrollDirection: Axis.horizontal,
- itemCount: LandingPageData.getNotLoggedInServiceCardsList.length,
- shrinkWrap: true,
- padding: const EdgeInsets.only(left: 0, right: 8),
- itemBuilder: (context, index) {
- return AnimationConfiguration.staggeredList(
- position: index,
- duration: const Duration(milliseconds: 1000),
- child: SlideAnimation(
- horizontalOffset: 100.0,
- child: FadeInAnimation(
- child: SmallServiceCard(
- icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon,
- title: LandingPageData.getNotLoggedInServiceCardsList[index].title,
- subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle,
- iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor,
- textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor,
- backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor,
- isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold,
- ),
- ),
- ),
- );
- },
- separatorBuilder: (BuildContext cxt, int index) => 0.width,
+ ),
+ ).paddingSymmetrical(24.h, 0.h),
+ ],
+ )
+ : Container(
+ height: 127.h,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ children: [
+ Expanded(
+ child: ListView.separated(
+ scrollDirection: Axis.horizontal,
+ itemCount: LandingPageData.getNotLoggedInServiceCardsList.length,
+ shrinkWrap: true,
+ padding: const EdgeInsets.only(left: 0, right: 8),
+ itemBuilder: (context, index) {
+ return AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 1000),
+ child: SlideAnimation(
+ horizontalOffset: 100.0,
+ child: FadeInAnimation(
+ child: SmallServiceCard(
+ icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon,
+ title: LandingPageData.getNotLoggedInServiceCardsList[index].title,
+ subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle,
+ iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor,
+ textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor,
+ backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor,
+ isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold,
+ ),
+ ),
),
- ),
- ],
+ );
+ },
+ separatorBuilder: (BuildContext cxt, int index) => 0.width,
),
),
- ).paddingSymmetrical(24.h, 0.h),
+ ],
+ ),
+ ),
+ ).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
diff --git a/lib/presentation/home/navigation_screen.dart b/lib/presentation/home/navigation_screen.dart
index bdce393..8566aa8 100644
--- a/lib/presentation/home/navigation_screen.dart
+++ b/lib/presentation/home/navigation_screen.dart
@@ -24,7 +24,7 @@ class _LandingNavigationState extends State {
children: [
const LandingPage(),
MedicalFilePage(),
- const BookAppointmentPage(),
+ BookAppointmentPage(),
const LandingPage(),
const LandingPage(),
],
diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart
index 48193e8..f0e7587 100644
--- a/lib/presentation/medical_file/medical_file_page.dart
+++ b/lib/presentation/medical_file/medical_file_page.dart
@@ -60,9 +60,11 @@ class _MedicalFilePageState extends State {
@override
void initState() {
scheduleMicrotask(() {
- insuranceViewModel.initInsuranceProvider();
- medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
- medicalFileViewModel.getPatientSickLeaveList();
+ if (appState.isAuthenticated) {
+ insuranceViewModel.initInsuranceProvider();
+ medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
+ medicalFileViewModel.getPatientSickLeaveList();
+ }
});
super.initState();
}
diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart
index 44e1327..0fb9223 100644
--- a/lib/widgets/input_widget.dart
+++ b/lib/widgets/input_widget.dart
@@ -40,6 +40,7 @@ class TextInputWidget extends StatelessWidget {
final SelectionTypeEnum? selectionType;
final num? fontSize;
final bool? isWalletAmountInput;
+ final Widget? suffix;
// final List countryList;
// final Function(Country)? onCountryChange;
@@ -69,6 +70,7 @@ class TextInputWidget extends StatelessWidget {
this.selectionType,
this.fontSize = 14,
this.isWalletAmountInput = false,
+ this.suffix,
// this.countryList = const [],
// this.onCountryChange,
});
@@ -238,6 +240,7 @@ class TextInputWidget extends StatelessWidget {
hintText: hintText,
hintStyle: TextStyle(fontSize: 14.fSize, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.2),
prefixIconConstraints: BoxConstraints(minWidth: 45.h),
+ suffixIconConstraints: BoxConstraints(maxHeight: 20.h),
prefixIcon: prefix == null
? null
: Text(
@@ -250,6 +253,12 @@ class TextInputWidget extends StatelessWidget {
letterSpacing: -0.2,
),
),
+ suffixIcon: suffix != null
+ ? Padding(
+ padding: EdgeInsets.only(right: 0.h),
+ child: suffix,
+ )
+ : null,
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
focusedBorder: InputBorder.none,
diff --git a/lib/widgets/loader/bottomsheet_loader.dart b/lib/widgets/loader/bottomsheet_loader.dart
index 57dfa01..aa97fdf 100644
--- a/lib/widgets/loader/bottomsheet_loader.dart
+++ b/lib/widgets/loader/bottomsheet_loader.dart
@@ -1,3 +1,4 @@
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
@@ -17,7 +18,7 @@ class LoaderBottomSheet {
showModalBottomSheet(
context: _navService.navigatorKey.currentContext!,
- isDismissible: ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? true : false,
+ isDismissible: (ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat || kDebugMode) ? true : false,
enableDrag: false,
backgroundColor: Colors.transparent,
builder: (_) {