diff --git a/assets/images/svg/all_medications_icon.svg b/assets/images/svg/all_medications_icon.svg new file mode 100644 index 0000000..3d3449b --- /dev/null +++ b/assets/images/svg/all_medications_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/allergy_info_icon.svg b/assets/images/svg/allergy_info_icon.svg new file mode 100644 index 0000000..fc962cc --- /dev/null +++ b/assets/images/svg/allergy_info_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/search_by_clinic_icon.svg b/assets/images/svg/search_by_clinic_icon.svg new file mode 100644 index 0000000..ce00f8c --- /dev/null +++ b/assets/images/svg/search_by_clinic_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/search_by_doctor_icon.svg b/assets/images/svg/search_by_doctor_icon.svg new file mode 100644 index 0000000..2ca5eb9 --- /dev/null +++ b/assets/images/svg/search_by_doctor_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/search_by_region_icon.svg b/assets/images/svg/search_by_region_icon.svg new file mode 100644 index 0000000..00abe12 --- /dev/null +++ b/assets/images/svg/search_by_region_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/vaccine_info_icon.svg b/assets/images/svg/vaccine_info_icon.svg new file mode 100644 index 0000000..d8d22b2 --- /dev/null +++ b/assets/images/svg/vaccine_info_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 933afa4..bbfa68c 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -811,5 +811,8 @@ "allSet": "جاهز! الآن يمكنك تسجيل الدخول باستخدام Face ID / Biometric أو البصمة", "enableQuickLogin":"تمكين تسجيل الدخول السريع", "enableMsg":"تمكين تسجيل الدخول السريع سيسمح بالتحقق من خلال Face ID / Biometric الخاص بجهازك الحالي", - "notNow": "ليس الآن" + "notNow": "ليس الآن", + "pendingActivation": "في انتظار التنشيط", + "awaitingApproval": "انتظر القبول", + "ready": "جاهز" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 0b29886..e054238 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -807,5 +807,8 @@ "allSet": "All Set! Now you can login with Face ID or Biometric", "enableQuickLogin": "Enable Quick Login", "enableMsg": "Enabling the quick login will verify through your existing device Face ID / Biometric", - "notNow": "Not Now" + "notNow": "Not Now", + "pendingActivation": "Pending Activation", + "awaitingApproval": "Awaiting Approval", + "ready": "Ready" } \ No newline at end of file diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 42f10f4..bad2e4c 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -726,7 +726,7 @@ const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_In class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 23945d0..106a5ad 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -100,6 +100,12 @@ class AppAssets { static const String forward_chevron_icon = '$svgBasePath/forward_chevron_icon.svg'; static const String logout = '$svgBasePath/logout.svg'; static const String alarm_clock_icon = '$svgBasePath/alarm_clock_icon.svg'; + static const String all_medications_icon = '$svgBasePath/all_medications_icon.svg'; + static const String allergy_info_icon = '$svgBasePath/allergy_info_icon.svg'; + static const String vaccine_info_icon = '$svgBasePath/vaccine_info_icon.svg'; + 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'; //bottom navigation// static const String homeBottom = '$svgBasePath/home_bottom.svg'; diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 0edec4e..f86ec97 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -6,13 +6,16 @@ import 'package:hmg_patient_app_new/core/location_util.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/common/common_repo.dart'; -import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_repo.dart'; -import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_view_model.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart'; @@ -88,6 +91,7 @@ class AppDependencies { getIt.registerLazySingleton(() => PayfortRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => LocalAuthService(loggerService: getIt(), localAuth: getIt())); getIt.registerLazySingleton(() => HabibWalletRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton(() => MedicalFileRepoImp(loggerService: getIt(), apiClient: getIt())); // ViewModels // Global/shared VMs → LazySingleton @@ -141,6 +145,27 @@ class AppDependencies { ), ); + getIt.registerLazySingleton( + () => HabibWalletViewModel( + habibWalletRepo: getIt(), + errorHandlerService: getIt(), + ), + ); + + getIt.registerLazySingleton( + () => MedicalFileViewModel( + medicalFileRepo: getIt(), + errorHandlerService: getIt(), + ), + ); + + getIt.registerLazySingleton( + () => BookAppointmentsViewModel( + bookAppointmentsRepo: getIt(), + errorHandlerService: getIt(), + ), + ); + getIt.registerLazySingleton( () => AuthenticationViewModel( authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()), diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index a330879..e228591 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -1,5 +1,7 @@ import 'dart:convert'; import 'dart:developer'; +import 'dart:io'; +import 'dart:typed_data'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:crypto/crypto.dart' as crypto; @@ -21,6 +23,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/dialogs/confirm_dialog.dart'; import 'package:hmg_patient_app_new/widgets/loading_dialog.dart'; import 'package:lottie/lottie.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:math' as dartMath; @@ -339,7 +342,8 @@ class Utils { ).center; } - static bool isVidaPlusProject(AppState appState, int projectID) { + static bool isVidaPlusProject(int projectID) { + AppState appState = getIt.get(); bool isVidaPlus = false; for (var element in appState.vidaPlusProjectList) { if (element.projectID == projectID) { @@ -629,4 +633,13 @@ class Utils { static String getAdvancePaymentTransID(int projectID, int fileNumber) { return '$projectID-$fileNumber-${DateTime.now().millisecondsSinceEpoch}'; } + + static Future createFileFromString(String encodedStr, String ext) async { + Uint8List bytes = base64.decode(encodedStr); + String dir = (await getApplicationDocumentsDirectory()).path; + File file = File("$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + "." + ext); + await file.writeAsBytes(bytes); + return file.path; + } + } diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index e69de29..8f096dd 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; + +class BookAppointmentsViewModel extends ChangeNotifier { + int selectedTabIndex = 0; + + BookAppointmentsRepo bookAppointmentsRepo; + ErrorHandlerService errorHandlerService; + + BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService}); + + void onTabChanged(int index) { + selectedTabIndex = index; + notifyListeners(); + } +} diff --git a/lib/features/habib_wallet/models/habib_wallet_repo.dart b/lib/features/habib_wallet/habib_wallet_repo.dart similarity index 100% rename from lib/features/habib_wallet/models/habib_wallet_repo.dart rename to lib/features/habib_wallet/habib_wallet_repo.dart diff --git a/lib/features/habib_wallet/models/habib_wallet_view_model.dart b/lib/features/habib_wallet/habib_wallet_view_model.dart similarity index 93% rename from lib/features/habib_wallet/models/habib_wallet_view_model.dart rename to lib/features/habib_wallet/habib_wallet_view_model.dart index 64c6ddf..8e4fc26 100644 --- a/lib/features/habib_wallet/models/habib_wallet_view_model.dart +++ b/lib/features/habib_wallet/habib_wallet_view_model.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_repo.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; class HabibWalletViewModel extends ChangeNotifier { diff --git a/lib/features/medical_file/medical_file_repo.dart b/lib/features/medical_file/medical_file_repo.dart new file mode 100644 index 0000000..affe564 --- /dev/null +++ b/lib/features/medical_file/medical_file_repo.dart @@ -0,0 +1,270 @@ +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/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +import '../authentication/models/resp_models/authenticated_user_resp_model.dart'; + +abstract class MedicalFileRepo { + Future>>> getPatientVaccinesList(); + + Future>>> getPatientSickLeavesList(); + + Future>> getPatientSickLeavePDF(PatientSickLeavesResponseModel patientSickLeavesResponseModel, AuthenticatedUser authenticatedUser); + + Future>>> getPatientMedicalReportsList(); + + Future>> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser); +} + +class MedicalFileRepoImp implements MedicalFileRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + MedicalFileRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future>>> getPatientVaccinesList() async { + Map mapDevice = {"To": "0", "From": "0"}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_VACCINES, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['List_DoneVaccines']; + // if (list == null || list.isEmpty) { + // throw Exception("lab list is empty"); + // } + + final vaccinesList = list.map((item) => PatientVaccineResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: vaccinesList, + ); + } 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>>> getPatientSickLeavesList() async { + Map mapDevice = {}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_PATIENT_SICK_LEAVE_STATUS, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['List_SickLeave']; + // if (list == null || list.isEmpty) { + // throw Exception("lab list is empty"); + // } + + final vaccinesList = list.map((item) => PatientSickLeavesResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: vaccinesList, + ); + } 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> getPatientSickLeavePDF(PatientSickLeavesResponseModel patientSickLeavesResponseModel, AuthenticatedUser authenticatedUser) async { + Map mapDevice = { + "RequestNo": patientSickLeavesResponseModel.requestNo, + "To": authenticatedUser.emailAddress, + "DateofBirth": authenticatedUser.dateofBirth, + "PatientIditificationNum": authenticatedUser.patientIdentificationNo, + "PatientMobileNumber": authenticatedUser.mobileNumber, + "PatientName": "${authenticatedUser.firstName!} ${authenticatedUser.lastName!}", + "ProjectName": patientSickLeavesResponseModel.projectName, + "DoctorName": patientSickLeavesResponseModel.doctorName, + "ProjectID": patientSickLeavesResponseModel.projectID, + "SetupID": patientSickLeavesResponseModel.setupID, + "IsDownload": true, + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + SendSickLeaveEmail, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + // final list = response['List_SickLeave']; + // if (list == null || list.isEmpty) { + // throw Exception("lab list is empty"); + // } + + // final vaccinesList = list.map((item) => PatientSickLeavesResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response["Base64Data"], + ); + } 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>>> getPatientMedicalReportsList() async { + Map mapDevice = { + "IsReport": true, + "EncounterType": 1, + "RequestType": 1, + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + REPORTS, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['GetPatientMedicalStatus']; + // if (list == null || list.isEmpty) { + // throw Exception("lab list is empty"); + // } + + final vaccinesList = list.map((item) => PatientMedicalReportResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: vaccinesList, + ); + } 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> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser) async { + Map mapDevice = { + "SetupID": patientMedicalReportResponseModel.setupId, + "PrintDate": patientMedicalReportResponseModel.requestDate!, + "ProcedureID": "05005009", + "Reporttype": "MEDICAL REPORT", + "stamp": patientMedicalReportResponseModel.requestDate!, + "To": authenticatedUser.emailAddress, + "DateofBirth": authenticatedUser.dateofBirth, + "PatientIditificationNum": authenticatedUser.patientIdentificationNo, + "PatientMobileNumber": authenticatedUser.mobileNumber, + "PatientName": "${authenticatedUser.firstName!} ${authenticatedUser.lastName!}", + "ProjectName": patientMedicalReportResponseModel.projectName, + "ClinicName": patientMedicalReportResponseModel.clinicDescription, + "ProjectID": patientMedicalReportResponseModel.projectID, + "InvoiceNo": Utils.isVidaPlusProject(patientMedicalReportResponseModel.projectID!) ? patientMedicalReportResponseModel.invoiceNoVP : patientMedicalReportResponseModel.invoiceNo, + "InvoiceNo_VP": Utils.isVidaPlusProject(patientMedicalReportResponseModel.projectID!) ? patientMedicalReportResponseModel.invoiceNoVP : patientMedicalReportResponseModel.invoiceNo, + "PrintedByName": "${authenticatedUser.firstName!} ${authenticatedUser.lastName!}", + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + GET_MEDICAL_REPORT_PDF, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + // final list = response['List_SickLeave']; + // if (list == null || list.isEmpty) { + // throw Exception("lab list is empty"); + // } + + // final vaccinesList = list.map((item) => PatientSickLeavesResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response["MedicalReportBase64"], + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } +} diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart index 8c00fe4..73167bb 100644 --- a/lib/features/medical_file/medical_file_view_model.dart +++ b/lib/features/medical_file/medical_file_view_model.dart @@ -1,12 +1,220 @@ import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; class MedicalFileViewModel extends ChangeNotifier { - int selectedTabIndex = 0; + bool isPatientVaccineListLoading = false; + bool isPatientSickLeaveListLoading = false; + bool isPatientSickLeavePDFLoading = false; + bool isPatientMedicalReportsListLoading = false; + + MedicalFileRepo medicalFileRepo; + ErrorHandlerService errorHandlerService; + + List patientVaccineList = []; + List patientSickLeaveList = []; + + List patientMedicalReportList = []; + + List patientMedicalReportRequestedList = []; + List patientMedicalReportReadyList = []; + List patientMedicalReportCancelledList = []; + + String patientSickLeavePDFBase64 = ""; + String patientMedicalReportPDFBase64 = ""; + + int selectedMedicalReportsTabIndex = 0; + + MedicalFileViewModel({required this.medicalFileRepo, required this.errorHandlerService}); + + initMedicalFileProvider() { + isPatientVaccineListLoading = true; + isPatientMedicalReportsListLoading = true; + notifyListeners(); + } + + void onMedicalReportTabChange(int index) { + selectedMedicalReportsTabIndex = index; + if (index == 0) { + patientMedicalReportList = patientMedicalReportRequestedList; + } else if (index == 1) { + patientMedicalReportList = patientMedicalReportReadyList; + } else if (index == 2) { + patientMedicalReportList = patientMedicalReportCancelledList; + } + notifyListeners(); + } + + setIsPatientVaccineListLoading(bool isLoading) { + isPatientVaccineListLoading = isLoading; + notifyListeners(); + } + + setIsPatientSickLeavePDFLoading(bool isLoading) { + isPatientSickLeavePDFLoading = isLoading; + notifyListeners(); + } + + setIsPatientSickLeaveListLoading(bool val) { + if (val) { + patientSickLeaveList.clear(); + patientSickLeavePDFBase64 = ""; + } + isPatientSickLeaveListLoading = val; + notifyListeners(); + } + + setIsPatientMedicalReportsLoading(bool val) { + if (val) { + patientMedicalReportList.clear(); + patientMedicalReportPDFBase64 = ""; + } + isPatientMedicalReportsListLoading = val; + notifyListeners(); + } void onTabChanged(int index) { selectedTabIndex = index; notifyListeners(); } + Future getPatientVaccinesList({Function(dynamic)? onSuccess, Function(String)? onError}) async { + patientVaccineList.clear(); + final result = await medicalFileRepo.getPatientVaccinesList(); + + result.fold( + (failure) async => await errorHandlerService.handleError( + failure: failure, + onOkPressed: () { + onError!(failure.message); + }, + ), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientVaccineList = apiResponse.data!; + isPatientVaccineListLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future getPatientSickLeaveList({Function(dynamic)? onSuccess, Function(String)? onError}) async { + patientSickLeaveList.clear(); + final result = await medicalFileRepo.getPatientSickLeavesList(); + + result.fold( + (failure) async => await errorHandlerService.handleError( + failure: failure, + onOkPressed: () { + onError!(failure.message); + }, + ), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientSickLeaveList = apiResponse.data!; + isPatientSickLeaveListLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future getPatientSickLeavePDF(PatientSickLeavesResponseModel patientSickLeavesResponseModel, AuthenticatedUser authenticatedUser, + {Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await medicalFileRepo.getPatientSickLeavePDF(patientSickLeavesResponseModel, authenticatedUser); + + result.fold( + (failure) async => await errorHandlerService.handleError( + failure: failure, + onOkPressed: () { + onError!(failure.message); + }, + ), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientSickLeavePDFBase64 = apiResponse.data!; + isPatientSickLeaveListLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future getPatientMedicalReportList({Function(dynamic)? onSuccess, Function(String)? onError}) async { + patientMedicalReportList.clear(); + final result = await medicalFileRepo.getPatientMedicalReportsList(); + + result.fold( + (failure) async => await errorHandlerService.handleError( + failure: failure, + onOkPressed: () { + onError!(failure.message); + }, + ), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientMedicalReportList = apiResponse.data!; + if (patientMedicalReportList.isNotEmpty) { + patientMedicalReportRequestedList = patientMedicalReportList.where((element) => element.status == 1).toList(); + patientMedicalReportReadyList = patientMedicalReportList.where((element) => element.status == 2).toList(); + patientMedicalReportCancelledList = patientMedicalReportList.where((element) => element.status == 4).toList(); + } + onMedicalReportTabChange(0); + isPatientMedicalReportsListLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser, + {Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await medicalFileRepo.getPatientMedicalReportPDF(patientMedicalReportResponseModel, authenticatedUser); + + result.fold( + (failure) async => await errorHandlerService.handleError( + failure: failure, + onOkPressed: () { + onError!(failure.message); + }, + ), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientMedicalReportPDFBase64 = apiResponse.data!; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } } diff --git a/lib/features/medical_file/models/patient_medical_response_model.dart b/lib/features/medical_file/models/patient_medical_response_model.dart new file mode 100644 index 0000000..52785af --- /dev/null +++ b/lib/features/medical_file/models/patient_medical_response_model.dart @@ -0,0 +1,192 @@ +class PatientMedicalReportResponseModel { + int? status; + String? encounterDate; + int? projectID; + int? invoiceNo; + int? encounterNo; + String? procedureId; + int? requestType; + String? setupId; + int? patientID; + int? doctorID; + int? clinicID; + String? requestDate; + bool? isRead; + dynamic isReadOn; + num? actualDoctorRate; + String? admissionDate; + int? admissionNumber; + String? appointmentDate; + int? appointmentNO; + String? appointmentTime; + String? clinicDescription; + dynamic clinicDescriptionN; + num? decimalDoctorRate; + String? docName; + dynamic docNameN; + String? doctorImageURL; + String? doctorName; + dynamic doctorNameN; + num? doctorRate; + num? doctorStarsRate; + int? invoiceNoVP; + dynamic invoiceType; + bool? isDoctorAllowVedioCall; + bool? isExecludeDoctor; + bool? isInOutPatient; + String? isInOutPatientDescription; + String? isInOutPatientDescriptionN; + int? noOfPatientsRate; + String? projectName; + dynamic projectNameN; + int? sourceID; + dynamic sourceName; + dynamic sourceNameN; + String? statusDesc; + dynamic strAppointmentDate; + + PatientMedicalReportResponseModel( + {this.status, + this.encounterDate, + this.projectID, + this.invoiceNo, + this.encounterNo, + this.procedureId, + this.requestType, + this.setupId, + this.patientID, + this.doctorID, + this.clinicID, + this.requestDate, + this.isRead, + this.isReadOn, + this.actualDoctorRate, + this.admissionDate, + this.admissionNumber, + this.appointmentDate, + this.appointmentNO, + this.appointmentTime, + this.clinicDescription, + this.clinicDescriptionN, + this.decimalDoctorRate, + this.docName, + this.docNameN, + this.doctorImageURL, + this.doctorName, + this.doctorNameN, + this.doctorRate, + this.doctorStarsRate, + this.invoiceNoVP, + this.invoiceType, + this.isDoctorAllowVedioCall, + this.isExecludeDoctor, + this.isInOutPatient, + this.isInOutPatientDescription, + this.isInOutPatientDescriptionN, + this.noOfPatientsRate, + this.projectName, + this.projectNameN, + this.sourceID, + this.sourceName, + this.sourceNameN, + this.statusDesc, + this.strAppointmentDate}); + + PatientMedicalReportResponseModel.fromJson(Map json) { + status = json['Status']; + encounterDate = json['EncounterDate']; + projectID = json['ProjectID']; + invoiceNo = json['InvoiceNo']; + encounterNo = json['EncounterNo']; + procedureId = json['ProcedureId']; + requestType = json['RequestType']; + setupId = json['SetupId']; + patientID = json['PatientID']; + doctorID = json['DoctorID']; + clinicID = json['ClinicID']; + requestDate = json['RequestDate']; + isRead = json['IsRead']; + isReadOn = json['IsReadOn']; + actualDoctorRate = json['ActualDoctorRate']; + admissionDate = json['AdmissionDate']; + admissionNumber = json['AdmissionNumber']; + appointmentDate = json['AppointmentDate']; + appointmentNO = json['AppointmentNO']; + appointmentTime = json['AppointmentTime']; + clinicDescription = json['ClinicDescription']; + clinicDescriptionN = json['ClinicDescriptionN']; + decimalDoctorRate = json['DecimalDoctorRate']; + docName = json['DocName']; + docNameN = json['DocNameN']; + doctorImageURL = json['DoctorImageURL']; + doctorName = json['DoctorName']; + doctorNameN = json['DoctorNameN']; + doctorRate = json['DoctorRate']; + doctorStarsRate = json['DoctorStarsRate']; + invoiceNoVP = json['InvoiceNo_VP']; + invoiceType = json['InvoiceType']; + isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall']; + isExecludeDoctor = json['IsExecludeDoctor']; + isInOutPatient = json['IsInOutPatient']; + isInOutPatientDescription = json['IsInOutPatientDescription']; + isInOutPatientDescriptionN = json['IsInOutPatientDescriptionN']; + noOfPatientsRate = json['NoOfPatientsRate']; + projectName = json['ProjectName']; + projectNameN = json['ProjectNameN']; + sourceID = json['SourceID']; + sourceName = json['SourceName']; + sourceNameN = json['SourceNameN']; + statusDesc = json['StatusDesc']; + strAppointmentDate = json['StrAppointmentDate']; + } + + Map toJson() { + final Map data = new Map(); + data['Status'] = this.status; + data['EncounterDate'] = this.encounterDate; + data['ProjectID'] = this.projectID; + data['InvoiceNo'] = this.invoiceNo; + data['EncounterNo'] = this.encounterNo; + data['ProcedureId'] = this.procedureId; + data['RequestType'] = this.requestType; + data['SetupId'] = this.setupId; + data['PatientID'] = this.patientID; + data['DoctorID'] = this.doctorID; + data['ClinicID'] = this.clinicID; + data['RequestDate'] = this.requestDate; + data['IsRead'] = this.isRead; + data['IsReadOn'] = this.isReadOn; + data['ActualDoctorRate'] = this.actualDoctorRate; + data['AdmissionDate'] = this.admissionDate; + data['AdmissionNumber'] = this.admissionNumber; + data['AppointmentDate'] = this.appointmentDate; + data['AppointmentNO'] = this.appointmentNO; + data['AppointmentTime'] = this.appointmentTime; + data['ClinicDescription'] = this.clinicDescription; + data['ClinicDescriptionN'] = this.clinicDescriptionN; + data['DecimalDoctorRate'] = this.decimalDoctorRate; + data['DocName'] = this.docName; + data['DocNameN'] = this.docNameN; + data['DoctorImageURL'] = this.doctorImageURL; + data['DoctorName'] = this.doctorName; + data['DoctorNameN'] = this.doctorNameN; + data['DoctorRate'] = this.doctorRate; + data['DoctorStarsRate'] = this.doctorStarsRate; + data['InvoiceNo_VP'] = this.invoiceNoVP; + data['InvoiceType'] = this.invoiceType; + data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall; + data['IsExecludeDoctor'] = this.isExecludeDoctor; + data['IsInOutPatient'] = this.isInOutPatient; + data['IsInOutPatientDescription'] = this.isInOutPatientDescription; + data['IsInOutPatientDescriptionN'] = this.isInOutPatientDescriptionN; + data['NoOfPatientsRate'] = this.noOfPatientsRate; + data['ProjectName'] = this.projectName; + data['ProjectNameN'] = this.projectNameN; + data['SourceID'] = this.sourceID; + data['SourceName'] = this.sourceName; + data['SourceNameN'] = this.sourceNameN; + data['StatusDesc'] = this.statusDesc; + data['StrAppointmentDate'] = this.strAppointmentDate; + return data; + } +} diff --git a/lib/features/medical_file/models/patient_sickleave_response_model.dart b/lib/features/medical_file/models/patient_sickleave_response_model.dart new file mode 100644 index 0000000..b0381a8 --- /dev/null +++ b/lib/features/medical_file/models/patient_sickleave_response_model.dart @@ -0,0 +1,176 @@ +class PatientSickLeavesResponseModel { + String? setupID; + int? projectID; + int? patientID; + int? patientType; + int? clinicID; + int? doctorID; + int? requestNo; + String? requestDate; + int? sickLeaveDays; + int? appointmentNo; + int? admissionNo; + dynamic reportDate; + num? actualDoctorRate; + String? appointmentDate; + String? clinicName; + double? decimalDoctorRate; + String? doctorImageURL; + String? doctorName; + num? doctorRate; + num? doctorStarsRate; + String? doctorTitle; + int? employeeID; + String? endDate; + int? gender; + String? genderDescription; + bool? isActiveDoctorProfile; + bool? isDoctorAllowVedioCall; + bool? isExecludeDoctor; + bool? isInOutPatient; + String? isInOutPatientDescription; + String? isInOutPatientDescriptionN; + bool? isLiveCareAppointment; + dynamic medicalDirectorApprovedStatus; + int? noOfPatientsRate; + dynamic patientName; + String? projectName; + String? qR; + List? speciality; + String? startDate; + int? status; + String? strRequestDate; + + PatientSickLeavesResponseModel( + {this.setupID, + this.projectID, + this.patientID, + this.patientType, + this.clinicID, + this.doctorID, + this.requestNo, + this.requestDate, + this.sickLeaveDays, + this.appointmentNo, + this.admissionNo, + this.reportDate, + this.actualDoctorRate, + this.appointmentDate, + this.clinicName, + this.decimalDoctorRate, + this.doctorImageURL, + this.doctorName, + this.doctorRate, + this.doctorStarsRate, + this.doctorTitle, + this.employeeID, + this.endDate, + this.gender, + this.genderDescription, + this.isActiveDoctorProfile, + this.isDoctorAllowVedioCall, + this.isExecludeDoctor, + this.isInOutPatient, + this.isInOutPatientDescription, + this.isInOutPatientDescriptionN, + this.isLiveCareAppointment, + this.medicalDirectorApprovedStatus, + this.noOfPatientsRate, + this.patientName, + this.projectName, + this.qR, + this.speciality, + this.startDate, + this.status, + this.strRequestDate}); + + PatientSickLeavesResponseModel.fromJson(Map json) { + setupID = json['SetupID']; + projectID = json['ProjectID']; + patientID = json['PatientID']; + patientType = json['PatientType']; + clinicID = json['ClinicID']; + doctorID = json['DoctorID']; + requestNo = json['RequestNo']; + requestDate = json['RequestDate']; + sickLeaveDays = json['SickLeaveDays']; + appointmentNo = json['AppointmentNo']; + admissionNo = json['AdmissionNo']; + reportDate = json['ReportDate']; + actualDoctorRate = json['ActualDoctorRate']; + appointmentDate = json['AppointmentDate']; + clinicName = json['ClinicName']; + decimalDoctorRate = json['DecimalDoctorRate']; + doctorImageURL = json['DoctorImageURL']; + doctorName = json['DoctorName']; + doctorRate = json['DoctorRate']; + doctorStarsRate = json['DoctorStarsRate']; + doctorTitle = json['DoctorTitle']; + employeeID = json['EmployeeID']; + endDate = json['EndDate']; + gender = json['Gender']; + genderDescription = json['GenderDescription']; + isActiveDoctorProfile = json['IsActiveDoctorProfile']; + isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall']; + isExecludeDoctor = json['IsExecludeDoctor']; + isInOutPatient = json['IsInOutPatient']; + isInOutPatientDescription = json['IsInOutPatientDescription']; + isInOutPatientDescriptionN = json['IsInOutPatientDescriptionN']; + isLiveCareAppointment = json['IsLiveCareAppointment']; + medicalDirectorApprovedStatus = json['MedicalDirectorApprovedStatus']; + noOfPatientsRate = json['NoOfPatientsRate']; + patientName = json['PatientName']; + projectName = json['ProjectName']; + qR = json['QR']; + speciality = json['Speciality'].cast(); + startDate = json['StartDate']; + status = json['Status']; + strRequestDate = json['StrRequestDate']; + } + + Map toJson() { + final Map data = new Map(); + data['SetupID'] = this.setupID; + data['ProjectID'] = this.projectID; + data['PatientID'] = this.patientID; + data['PatientType'] = this.patientType; + data['ClinicID'] = this.clinicID; + data['DoctorID'] = this.doctorID; + data['RequestNo'] = this.requestNo; + data['RequestDate'] = this.requestDate; + data['SickLeaveDays'] = this.sickLeaveDays; + data['AppointmentNo'] = this.appointmentNo; + data['AdmissionNo'] = this.admissionNo; + data['ReportDate'] = this.reportDate; + data['ActualDoctorRate'] = this.actualDoctorRate; + data['AppointmentDate'] = this.appointmentDate; + data['ClinicName'] = this.clinicName; + data['DecimalDoctorRate'] = this.decimalDoctorRate; + data['DoctorImageURL'] = this.doctorImageURL; + data['DoctorName'] = this.doctorName; + data['DoctorRate'] = this.doctorRate; + data['DoctorStarsRate'] = this.doctorStarsRate; + data['DoctorTitle'] = this.doctorTitle; + data['EmployeeID'] = this.employeeID; + data['EndDate'] = this.endDate; + data['Gender'] = this.gender; + data['GenderDescription'] = this.genderDescription; + data['IsActiveDoctorProfile'] = this.isActiveDoctorProfile; + data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall; + data['IsExecludeDoctor'] = this.isExecludeDoctor; + data['IsInOutPatient'] = this.isInOutPatient; + data['IsInOutPatientDescription'] = this.isInOutPatientDescription; + data['IsInOutPatientDescriptionN'] = this.isInOutPatientDescriptionN; + data['IsLiveCareAppointment'] = this.isLiveCareAppointment; + data['MedicalDirectorApprovedStatus'] = this.medicalDirectorApprovedStatus; + data['NoOfPatientsRate'] = this.noOfPatientsRate; + data['PatientName'] = this.patientName; + data['ProjectName'] = this.projectName; + data['QR'] = this.qR; + data['Speciality'] = this.speciality; + data['StartDate'] = this.startDate; + data['Status'] = this.status; + data['StrRequestDate'] = this.strRequestDate; + return data; + } +} diff --git a/lib/features/medical_file/models/patient_vaccine_response_model.dart b/lib/features/medical_file/models/patient_vaccine_response_model.dart new file mode 100644 index 0000000..ff27b06 --- /dev/null +++ b/lib/features/medical_file/models/patient_vaccine_response_model.dart @@ -0,0 +1,160 @@ +class PatientVaccineResponseModel { + String? setupID; + int? projectID; + int? patientID; + int? invoiceNo; + String? procedureID; + String? vaccineName; + Null? vaccineNameN; + String? invoiceDate; + int? doctorID; + int? clinicID; + String? firstName; + String? middleName; + String? lastName; + Null? firstNameN; + Null? middleNameN; + Null? lastNameN; + String? dateofBirth; + int? actualDoctorRate; + String? age; + String? clinicName; + Null? decimalDoctorRate; + Null? doctorImageURL; + String? doctorName; + int? doctorRate; + int? doctorStarsRate; + String? doctorTitle; + int? gender; + Null? genderDescription; + Null? invoiceNoVP; + bool? isActiveDoctorProfile; + bool? isDoctorAllowVedioCall; + bool? isExecludeDoctor; + int? noOfPatientsRate; + String? patientName; + String? projectName; + String? qR; + String? vaccinationDate; + + PatientVaccineResponseModel( + {this.setupID, + this.projectID, + this.patientID, + this.invoiceNo, + this.procedureID, + this.vaccineName, + this.vaccineNameN, + this.invoiceDate, + this.doctorID, + this.clinicID, + this.firstName, + this.middleName, + this.lastName, + this.firstNameN, + this.middleNameN, + this.lastNameN, + this.dateofBirth, + this.actualDoctorRate, + this.age, + this.clinicName, + this.decimalDoctorRate, + this.doctorImageURL, + this.doctorName, + this.doctorRate, + this.doctorStarsRate, + this.doctorTitle, + this.gender, + this.genderDescription, + this.invoiceNoVP, + this.isActiveDoctorProfile, + this.isDoctorAllowVedioCall, + this.isExecludeDoctor, + this.noOfPatientsRate, + this.patientName, + this.projectName, + this.qR, + this.vaccinationDate}); + + PatientVaccineResponseModel.fromJson(Map json) { + setupID = json['SetupID']; + projectID = json['ProjectID']; + patientID = json['PatientID']; + invoiceNo = json['InvoiceNo']; + procedureID = json['ProcedureID']; + vaccineName = json['VaccineName']; + vaccineNameN = json['VaccineNameN']; + invoiceDate = json['InvoiceDate']; + doctorID = json['DoctorID']; + clinicID = json['ClinicID']; + firstName = json['FirstName']; + middleName = json['MiddleName']; + lastName = json['LastName']; + firstNameN = json['FirstNameN']; + middleNameN = json['MiddleNameN']; + lastNameN = json['LastNameN']; + dateofBirth = json['DateofBirth']; + actualDoctorRate = json['ActualDoctorRate']; + age = json['Age']; + clinicName = json['ClinicName']; + decimalDoctorRate = json['DecimalDoctorRate']; + doctorImageURL = json['DoctorImageURL']; + doctorName = json['DoctorName']; + doctorRate = json['DoctorRate']; + doctorStarsRate = json['DoctorStarsRate']; + doctorTitle = json['DoctorTitle']; + gender = json['Gender']; + genderDescription = json['GenderDescription']; + invoiceNoVP = json['InvoiceNo_VP']; + isActiveDoctorProfile = json['IsActiveDoctorProfile']; + isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall']; + isExecludeDoctor = json['IsExecludeDoctor']; + noOfPatientsRate = json['NoOfPatientsRate']; + patientName = json['PatientName']; + projectName = json['ProjectName']; + qR = json['QR']; + vaccinationDate = json['VaccinationDate']; + } + + Map toJson() { + final Map data = new Map(); + data['SetupID'] = this.setupID; + data['ProjectID'] = this.projectID; + data['PatientID'] = this.patientID; + data['InvoiceNo'] = this.invoiceNo; + data['ProcedureID'] = this.procedureID; + data['VaccineName'] = this.vaccineName; + data['VaccineNameN'] = this.vaccineNameN; + data['InvoiceDate'] = this.invoiceDate; + data['DoctorID'] = this.doctorID; + data['ClinicID'] = this.clinicID; + data['FirstName'] = this.firstName; + data['MiddleName'] = this.middleName; + data['LastName'] = this.lastName; + data['FirstNameN'] = this.firstNameN; + data['MiddleNameN'] = this.middleNameN; + data['LastNameN'] = this.lastNameN; + data['DateofBirth'] = this.dateofBirth; + data['ActualDoctorRate'] = this.actualDoctorRate; + data['Age'] = this.age; + data['ClinicName'] = this.clinicName; + data['DecimalDoctorRate'] = this.decimalDoctorRate; + data['DoctorImageURL'] = this.doctorImageURL; + data['DoctorName'] = this.doctorName; + data['DoctorRate'] = this.doctorRate; + data['DoctorStarsRate'] = this.doctorStarsRate; + data['DoctorTitle'] = this.doctorTitle; + data['Gender'] = this.gender; + data['GenderDescription'] = this.genderDescription; + data['InvoiceNo_VP'] = this.invoiceNoVP; + data['IsActiveDoctorProfile'] = this.isActiveDoctorProfile; + data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall; + data['IsExecludeDoctor'] = this.isExecludeDoctor; + data['NoOfPatientsRate'] = this.noOfPatientsRate; + data['PatientName'] = this.patientName; + data['ProjectName'] = this.projectName; + data['QR'] = this.qR; + data['VaccinationDate'] = this.vaccinationDate; + return data; + } +} diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index a5528cf..2035f30 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -34,6 +34,8 @@ abstract class MyAppointmentsRepo { {required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, required String scannedCode, required int checkInType}); Future>>> getPatientAppointmentsForTimeLine(); + + Future>>> getPatientDoctorsList(); } class MyAppointmentsRepoImp implements MyAppointmentsRepo { @@ -444,4 +446,49 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> getPatientDoctorsList() async { + Map mapDevice = { + "Top": 0, + "beforeDays": 0, + "exludType": 4, + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_MY_DOCTOR, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['PatientDoctorAppointmentResultList']; + // if (list == null || list.isEmpty) { + // throw Exception("Appointments list is empty"); + // } + + final appointmentsList = list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: appointmentsList, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index d3121af..c6d3499 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -13,6 +13,7 @@ class MyAppointmentsViewModel extends ChangeNotifier { bool isMyAppointmentsLoading = false; bool isAppointmentPatientShareLoading = false; bool isTimeLineAppointmentsLoading = false; + bool isPatientMyDoctorsLoading = false; List patientAppointmentsHistoryList = []; @@ -21,6 +22,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { List patientTimelineAppointmentsList = []; + List patientMyDoctorsList = []; + PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel; MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService}); @@ -35,9 +38,11 @@ class MyAppointmentsViewModel extends ChangeNotifier { patientUpcomingAppointmentsHistoryList.clear(); patientArrivedAppointmentsHistoryList.clear(); patientTimelineAppointmentsList.clear(); + patientMyDoctorsList.clear(); isMyAppointmentsLoading = true; isAppointmentPatientShareLoading = true; isTimeLineAppointmentsLoading = true; + isPatientMyDoctorsLoading = true; notifyListeners(); } @@ -56,6 +61,11 @@ class MyAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + setIsPatientMyDoctorsLoading(bool val) { + isPatientMyDoctorsLoading = val; + notifyListeners(); + } + setAppointmentReminder(bool value, PatientAppointmentHistoryResponseModel item) { int index = patientAppointmentsHistoryList.indexOf(item); if (index != -1) { @@ -263,4 +273,24 @@ class MyAppointmentsViewModel extends ChangeNotifier { }, ); } + + Future getPatientMyDoctors({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.getPatientDoctorsList(); + + 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) { + patientMyDoctorsList = apiResponse.data!; + isPatientMyDoctorsLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index dc71850..8b7f021 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -780,6 +780,9 @@ abstract class LocaleKeys { static const resultsPending = 'resultsPending'; static const resultsAvailable = 'resultsAvailable'; static const viewReport = 'viewReport'; + static const checkAvailability = 'checkAvailability'; + static const readInstructions = 'readInstructions'; + static const searchLabReport = 'searchLabReport'; static const prescriptionDeliveryError = 'prescriptionDeliveryError'; static const receiveOtpToast = 'receiveOtpToast'; static const enterPhoneNumber = 'enterPhoneNumber'; @@ -802,12 +805,13 @@ abstract class LocaleKeys { static const loginByOTP = 'loginByOTP'; static const guest = 'guest'; static const switchAccount = 'switchAccount'; - static const checkAvailability = 'checkAvailability'; - static const readInstructions = 'readInstructions'; - static const searchLabReport = 'searchLabReport'; - static const lastloginBy = 'lastloginBy'; - static const allSet ='allSet'; + static const lastLoginBy = 'lastLoginBy'; + static const allSet = 'allSet'; static const enableQuickLogin = 'enableQuickLogin'; static const enableMsg = 'enableMsg'; static const notNow = 'notNow'; + static const pendingActivation = 'pendingActivation'; + static const awaitingApproval = 'awaitingApproval'; + static const ready = 'ready'; + } diff --git a/lib/main.dart b/lib/main.dart index 2739a6b..901c529 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,7 +9,8 @@ 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/utils.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; -import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_view_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; @@ -96,7 +97,10 @@ void main() async { ), ), ChangeNotifierProvider( - create: (_) => MedicalFileViewModel(), + create: (_) => MedicalFileViewModel( + medicalFileRepo: getIt(), + errorHandlerService: getIt(), + ), ), ChangeNotifierProvider( create: (_) => MyAppointmentsViewModel( @@ -116,6 +120,12 @@ void main() async { errorHandlerService: getIt(), ), ), + ChangeNotifierProvider( + create: (_) => BookAppointmentsViewModel( + bookAppointmentsRepo: getIt(), + errorHandlerService: getIt(), + ), + ), ChangeNotifierProvider( create: (_) => AuthenticationViewModel( authenticationRepo: getIt(), diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index baa2af3..a617647 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -370,7 +370,7 @@ class _AppointmentPaymentPageState extends State { onSuccess: (value) async { print(value); await myAppointmentsViewModel.addAdvanceNumberRequest( - advanceNumber: Utils.isVidaPlusProject(appState, widget.patientAppointmentHistoryResponseModel.projectID) + advanceNumber: Utils.isVidaPlusProject(widget.patientAppointmentHistoryResponseModel.projectID) ? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString() : value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(), paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, diff --git a/lib/presentation/appointments/my_doctors_page.dart b/lib/presentation/appointments/my_doctors_page.dart new file mode 100644 index 0000000..89b120c --- /dev/null +++ b/lib/presentation/appointments/my_doctors_page.dart @@ -0,0 +1,160 @@ +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/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/my_appointments/my_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:provider/provider.dart'; + +import '../../widgets/chip/app_custom_chip_widget.dart'; + +class MyDoctorsPage extends StatelessWidget { + MyDoctorsPage({super.key}); + + late MyAppointmentsViewModel myAppointmentsViewModel; + + @override + Widget build(BuildContext context) { + myAppointmentsViewModel = Provider.of(context, listen: false); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: CollapsingListView( + title: LocaleKeys.myDoctor.tr(context: context), + child: SingleChildScrollView( + child: Consumer(builder: (context, myAppointmentsVM, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 16.h), + ListView.separated( + scrollDirection: Axis.vertical, + itemCount: myAppointmentsVM.isPatientMyDoctorsLoading ? 5 : myAppointmentsVM.patientMyDoctorsList.length, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.only(left: 24.h, right: 24.h), + itemBuilder: (context, index) { + return myAppointmentsVM.isPatientMyDoctorsLoading + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(14.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.network( + "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + width: 63.h, + height: 63.h, + fit: BoxFit.fill, + ).circle(100).toShimmer2(isShow: true), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h), + AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h), + ], + ), + ], + ), + ), + ], + ), + ], + ), + ), + ) + : AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(14.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.network( + myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!, + width: 63.h, + height: 63.h, + fit: BoxFit.fill, + ).circle(100).toShimmer2(isShow: false), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (myAppointmentsVM.patientMyDoctorsList[index].doctorName).toString().toText16(isBold: true).toShimmer2(isShow: false), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget(labelText: myAppointmentsVM.patientMyDoctorsList[index].clinicName).toShimmer2(isShow: false, width: 16.h), + AppCustomChipWidget(labelText: myAppointmentsVM.patientMyDoctorsList[index].projectName).toShimmer2(isShow: false, width: 16.h), + ], + ), + ], + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "".toText16(), + Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor), + ], + ), + ], + ), + ), + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ), + SizedBox(height: 60.h), + ], + ); + }), + ), + ), + ); + } +} diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart index edfc1e7..d711402 100644 --- a/lib/presentation/authentication/saved_login_screen.dart +++ b/lib/presentation/authentication/saved_login_screen.dart @@ -84,7 +84,7 @@ class _SavedLogin extends State { children: [ // Last login info - ("${LocaleKeys.lastloginBy.tr()} ${loginType.displayName}").toText14(isBold: true, color: AppColors.greyTextColor), + ("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}").toText14(isBold: true, color: AppColors.greyTextColor), (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null ? DateUtil.getFormattedDate(DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), "d MMMM, y 'at' HH:mm") : '--') diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart new file mode 100644 index 0000000..0f9a325 --- /dev/null +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -0,0 +1,159 @@ +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/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'; +import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; +import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; +import 'package:provider/provider.dart'; + +class BookAppointmentPage extends StatefulWidget { + const BookAppointmentPage({super.key}); + + @override + State createState() => _BookAppointmentPageState(); +} + +class _BookAppointmentPageState extends State { + late AppState appState; + + @override + Widget build(BuildContext context) { + appState = getIt.get(); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: CollapsingListView( + title: LocaleKeys.bookAppo.tr(context: context), + child: SingleChildScrollView( + child: Consumer(builder: (context, bookAppointmentsVM, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 16.h), + CustomTabBar( + activeTextColor: Color(0xffED1C2B), + activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), + tabs: [ + CustomTabBarModel(null, "General".needTranslation), + CustomTabBarModel(null, "LiveCare".needTranslation), + ], + onTabChange: (index) { + bookAppointmentsVM.onTabChanged(index); + }, + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 24.h), + getSelectedTabData(bookAppointmentsVM.selectedTabIndex), + ], + ); + }), + ), + ), + ); + } + + Widget getSelectedTabData(int index) { + switch (index) { + case 0: + return Column( + children: [ + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: false, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Search By Clinic".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h), + ], + ).onPress(() { + Navigator.of(context).push( + FadePage( + page: SelectClinicPage(), + ), + ); + }), + SizedBox(height: 16.h), + Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Search By Doctor".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "Tap to select".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h), + ], + ).onPress(() {}), + SizedBox(height: 16.h), + Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Search By Region".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "Central Region".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h), + ], + ).onPress(() {}), + ], + ), + ), + ), + ], + ).paddingSymmetrical(24.h, 0.h); + default: + SizedBox.shrink(); + } + return Container(); + } +} diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart new file mode 100644 index 0000000..496f7d5 --- /dev/null +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -0,0 +1,29 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.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'; + +class SelectClinicPage extends StatefulWidget { + const SelectClinicPage({super.key}); + + @override + State createState() => _SelectClinicPageState(); +} + +class _SelectClinicPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: CollapsingListView( + title: LocaleKeys.selectClinic.tr(context: context), + child: SingleChildScrollView( + child: Column( + children: [], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/habib_wallet/habib_wallet_page.dart b/lib/presentation/habib_wallet/habib_wallet_page.dart index 632b11d..1d24d05 100644 --- a/lib/presentation/habib_wallet/habib_wallet_page.dart +++ b/lib/presentation/habib_wallet/habib_wallet_page.dart @@ -7,7 +7,7 @@ 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/habib_wallet/models/habib_wallet_view_model.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart'; import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index b63b404..26e5147 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -12,7 +12,7 @@ import 'package:hmg_patient_app_new/extensions/int_extensions.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/authentication/authentication_view_model.dart'; -import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_view_model.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; @@ -64,6 +64,7 @@ class _LandingPageState extends State { habibWalletVM.getPatientBalanceAmount(); myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.getPatientAppointments(true, false); + myAppointmentsViewModel.getPatientMyDoctors(); prescriptionsViewModel.initPrescriptionsViewModel(); } }); diff --git a/lib/presentation/home/navigation_screen.dart b/lib/presentation/home/navigation_screen.dart index 152bbd2..bdce393 100644 --- a/lib/presentation/home/navigation_screen.dart +++ b/lib/presentation/home/navigation_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/home/landing_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; import 'package:hmg_patient_app_new/widgets/bottom_navigation/bottom_navigation.dart'; @@ -23,7 +24,7 @@ class _LandingNavigationState extends State { children: [ const LandingPage(), MedicalFilePage(), - const LandingPage(), + const BookAppointmentPage(), const LandingPage(), const LandingPage(), ], diff --git a/lib/presentation/home/widgets/habib_wallet_card.dart b/lib/presentation/home/widgets/habib_wallet_card.dart index 9bb53f5..3509000 100644 --- a/lib/presentation/home/widgets/habib_wallet_card.dart +++ b/lib/presentation/home/widgets/habib_wallet_card.dart @@ -4,7 +4,7 @@ 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/habib_wallet/models/habib_wallet_view_model.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; diff --git a/lib/presentation/home/widgets/small_service_card.dart b/lib/presentation/home/widgets/small_service_card.dart index f988e18..6e1c588 100644 --- a/lib/presentation/home/widgets/small_service_card.dart +++ b/lib/presentation/home/widgets/small_service_card.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_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/presentation/appointments/my_doctors_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; @@ -85,6 +87,22 @@ class SmallServiceCard extends StatelessWidget { ), ); break; + + case "my_doctors": + Navigator.of(context).push( + FadePage( + page: MyDoctorsPage(), + ), + ); + break; + + case "sick_leaves": + Navigator.of(context).push( + FadePage( + page: PatientSickleavesListPage(), + ), + ); + break; default: // Handle unknown service break; diff --git a/lib/presentation/lab/collapsing_list_view.dart b/lib/presentation/lab/collapsing_list_view.dart index c7f1540..f84872d 100644 --- a/lib/presentation/lab/collapsing_list_view.dart +++ b/lib/presentation/lab/collapsing_list_view.dart @@ -21,8 +21,9 @@ class CollapsingListView extends StatelessWidget { VoidCallback? history; Widget? bottomChild; bool isClose; + bool isLeading; - CollapsingListView({required this.title, this.child, this.search, this.isClose = false, this.bottomChild, this.report, this.logout, this.history}); + CollapsingListView({required this.title, this.child, this.search, this.isClose = false, this.bottomChild, this.report, this.logout, this.history, this.isLeading = true}); @override Widget build(BuildContext context) { @@ -33,17 +34,18 @@ class CollapsingListView extends StatelessWidget { CustomScrollView( slivers: [ SliverAppBar( + automaticallyImplyLeading: false, pinned: true, expandedHeight: 100, stretch: true, systemOverlayStyle: SystemUiOverlayStyle(statusBarBrightness: Brightness.light), surfaceTintColor: Colors.transparent, backgroundColor: AppColors.bgScaffoldColor, - leading: IconButton( + leading: isLeading ? IconButton( icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.arrow_back, width: 32.h, height: 32.h), padding: EdgeInsets.only(left: 12), onPressed: () => Navigator.pop(context), - ), + ) : SizedBox.shrink(), flexibleSpace: LayoutBuilder( builder: (context, constraints) { final double maxHeight = 100; diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index 2c6a131..520bfec 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; @@ -57,7 +58,7 @@ class _LabOrdersPageState extends State { } }, child: SingleChildScrollView( - padding: EdgeInsets.all(24), + padding: EdgeInsets.all(24.h), physics: NeverScrollableScrollPhysics(), child: Consumer( builder: (context, model, child) { diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 4860bb1..48193e8 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -13,23 +13,33 @@ 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/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; 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/appointments/my_appointments_page.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart'; +import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart'; import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/medical_reports_page.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/vaccine_list_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/lab_rad_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_sick_leave_card.dart'; +import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.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/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; +import '../prescriptions/prescription_detail_page.dart'; import 'widgets/medical_file_appointment_card.dart'; class MedicalFilePage extends StatefulWidget { @@ -43,6 +53,7 @@ class _MedicalFilePageState extends State { late InsuranceViewModel insuranceViewModel; late AppState appState; late MyAppointmentsViewModel myAppointmentsViewModel; + late MedicalFileViewModel medicalFileViewModel; int currentIndex = 0; @@ -50,6 +61,8 @@ class _MedicalFilePageState extends State { void initState() { scheduleMicrotask(() { insuranceViewModel.initInsuranceProvider(); + medicalFileViewModel.setIsPatientSickLeaveListLoading(true); + medicalFileViewModel.getPatientSickLeaveList(); }); super.initState(); } @@ -58,16 +71,17 @@ class _MedicalFilePageState extends State { Widget build(BuildContext context) { insuranceViewModel = Provider.of(context, listen: false); myAppointmentsViewModel = Provider.of(context, listen: false); + medicalFileViewModel = Provider.of(context, listen: false); appState = getIt.get(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, - body: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.only(top: 80.0), + body: CollapsingListView( + title: LocaleKeys.medicalFile.tr(context: context), + isLeading: false, + child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.medicalFile.tr(context: context).toText22(isBold: true).paddingSymmetrical(24.h, 0.0), SizedBox(height: 16.h), TextInputWidget( labelText: LocaleKeys.search.tr(context: context), @@ -318,116 +332,257 @@ class _MedicalFilePageState extends State { SizedBox(height: 16.h), Consumer(builder: (context, prescriptionVM, child) { return prescriptionVM.isPrescriptionsOrdersLoading - ? const MoviesShimmerWidget() - : Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: Colors.white, - borderRadius: 20.h, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - children: [ - ListView.separated( - itemCount: prescriptionVM.patientPrescriptionOrders.length, - shrinkWrap: true, - padding: const EdgeInsets.only(left: 0, right: 8), - physics: NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 500), - child: SlideAnimation( - verticalOffset: 100.0, - child: FadeInAnimation( - child: Row( - children: [ - Utils.buildSvgWithAssets( - icon: AppAssets.prescription_item_icon, - width: 40.h, - height: 40.h, - ), - SizedBox(width: 8.h), - Row( - mainAxisSize: MainAxisSize.max, + ? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h) + : prescriptionVM.patientPrescriptionOrders.isNotEmpty + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: Colors.white, + borderRadius: 20.h, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + children: [ + ListView.separated( + itemCount: prescriptionVM.patientPrescriptionOrders.length, + shrinkWrap: true, + padding: const EdgeInsets.only(left: 0, right: 8), + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: Row( children: [ - Column( - children: [ - // SizedBox(width: 150.h, child: prescriptionVM.prescriptionDetailsList[index].itemDescription!.toText12(isBold: true, maxLine: 1)), - // SizedBox( - // width: 150.h, - // child: - // "Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}" - // .needTranslation - // .toText10(weight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: -0.4), - // ), - ], - ), - SizedBox(width: 68.h), - Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, - iconColor: AppColors.blackColor, - width: 18.h, - height: 13.h, - fit: BoxFit.contain, + Image.network( + prescriptionVM.patientPrescriptionOrders[index].doctorImageURL!, + width: 63.h, + height: 63.h, + fit: BoxFit.fill, + ).circle(100), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + prescriptionVM.patientPrescriptionOrders[index].doctorName!.toText16(isBold: true), + SizedBox(height: 4.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget(labelText: prescriptionVM.patientPrescriptionOrders[index].clinicDescription!), + AppCustomChipWidget( + icon: AppAssets.doctor_calendar_icon, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescriptionVM.patientPrescriptionOrders[index].appointmentDate), false), + ), + ], + ), + ], + ), ), + SizedBox(width: 40.h), + Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor), ], - ), - ], + ).onPress(() { + prescriptionVM.setPrescriptionsDetailsLoading(); + Navigator.of(context).push( + FadePage( + page: PrescriptionDetailPage(prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]), + ), + ); + }), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ), + SizedBox(height: 16.h), + const Divider(color: AppColors.dividerColor), + SizedBox(height: 16.h), + Row( + children: [ + Expanded( + child: CustomButton( + text: "All Prescriptions".needTranslation, + onPressed: () { + Navigator.of(context).push( + FadePage( + page: PrescriptionsListPage(), + ), + ); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12.h, + height: 40.h, + icon: AppAssets.requests, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, ), ), - ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), - ).onPress(() { - prescriptionVM.setPrescriptionsDetailsLoading(); - // Navigator.of(context).push( - // FadePage( - // page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()), - // ), - // ); - }), - SizedBox(height: 16.h), - const Divider(color: AppColors.dividerColor), - SizedBox(height: 16.h), - Row( + SizedBox(width: 10.h), + Expanded( + child: CustomButton( + text: "All Medications".needTranslation, + onPressed: () {}, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12.h, + height: 40.h, + icon: AppAssets.all_medications_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + ), + ), + ], + ), + ], + ), + ), + ).paddingSymmetrical(24.h, 0.h) + : SizedBox.shrink(); + }), + SizedBox(height: 24.h), + //My Doctor Section + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.myDoctor.tr(context: context).toText18(isBold: true), + Row( + children: [ + LocaleKeys.viewAll.tr().toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], + ).onPress(() { + myAppointmentsViewModel.setIsPatientMyDoctorsLoading(true); + myAppointmentsViewModel.getPatientMyDoctors(); + Navigator.of(context).push( + FadePage( + page: MyDoctorsPage(), + ), + ); + }), + ], + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 16.h), + Consumer(builder: (context, myAppointmentsVM, child) { + return SizedBox( + height: 120.h, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: myAppointmentsVM.isPatientMyDoctorsLoading ? 5 : myAppointmentsVM.patientMyDoctorsList.length, + shrinkWrap: true, + padding: EdgeInsets.only(left: 24.h, right: 24.h), + itemBuilder: (context, index) { + return myAppointmentsVM.isPatientMyDoctorsLoading + ? SizedBox( + width: 80.h, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, children: [ + Image.network( + "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + width: 64.h, + height: 64.h, + fit: BoxFit.fill, + ).circle(100).toShimmer2(isShow: true, radius: 50.h), + SizedBox(height: 8.h), Expanded( - child: CustomButton( - text: "All Prescriptions".needTranslation, - onPressed: () { - // Navigator.of(context) - // .push( - // FadePage( - // page: PrescriptionsListPage(), - // ), - // ) - // .then((val) { - // prescriptionsViewModel.setPrescriptionsDetailsLoading(); - // prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel()); - // }); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12.h, - height: 40.h, - icon: AppAssets.requests, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, - ), + child: ("Dr. John Smith Smith Smith").toString().toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2).toShimmer2(isShow: true), ), ], ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h); + ) + : AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SizedBox( + width: 80.h, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.network( + myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!, + width: 64.h, + height: 64.h, + fit: BoxFit.fill, + ).circle(100).toShimmer2(isShow: false, radius: 50.h), + SizedBox(height: 8.h), + Expanded( + child: (myAppointmentsVM.patientMyDoctorsList[index].doctorName) + .toString() + .toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2) + .toShimmer2(isShow: false), + ), + ], + ), + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h), + ), + ); }), SizedBox(height: 24.h), + "Others".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 16.h), + GridView( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 13, mainAxisSpacing: 13), + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + shrinkWrap: true, + children: [ + MedicalFileCard( + label: "Eye Test Results".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.eye_result_icon, + isLargeText: true, + iconSize: 40.h, + ), + MedicalFileCard( + label: "Allergy Info".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.allergy_info_icon, + isLargeText: true, + iconSize: 40.h, + ), + MedicalFileCard( + label: "Vaccine Info".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.vaccine_info_icon, + isLargeText: true, + iconSize: 40.h, + ).onPress(() { + Navigator.of(context).push( + FadePage( + page: VaccineListPage(), + ), + ); + }), + ], + ).paddingSymmetrical(24.h, 0.0), + SizedBox(height: 24.h), ], ); case 1: @@ -448,17 +603,113 @@ class _MedicalFilePageState extends State { padding: EdgeInsets.only(top: 12), shrinkWrap: true, children: [ - MedicalFileCard(label: "Update Insurance".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon), - MedicalFileCard(label: "Insurance Approvals".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon), - MedicalFileCard(label: "My Invoices List".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon), - MedicalFileCard(label: "Ancillary Orders List".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon), + MedicalFileCard( + label: "Update Insurance".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.eye_result_icon, + isLargeText: false, + iconSize: 36.h) + .onPress(() { + Navigator.of(context).push( + FadePage( + page: InsuranceHomePage(), + ), + ); + }), + MedicalFileCard( + label: "Insurance Approvals".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.eye_result_icon, + isLargeText: false, + iconSize: 36.h), + MedicalFileCard( + label: "My Invoices List".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.eye_result_icon, + isLargeText: false, + iconSize: 36.h), + MedicalFileCard( + label: "Ancillary Orders List".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.eye_result_icon, + isLargeText: false, + iconSize: 36.h), ], ).paddingSymmetrical(24.h, 0.0), SizedBox(height: 16.h), ], ); case 2: - return Container(); + // Requests Tab Data + return Column( + children: [ + Consumer(builder: (context, medicalFileVM, child) { + return medicalFileVM.isPatientSickLeaveListLoading + ? PatientSickLeaveCard( + patientSickLeavesResponseModel: PatientSickLeavesResponseModel(), + isLoading: true, + ).paddingSymmetrical(24.h, 0.0) + : medicalFileVM.patientSickLeaveList.isNotEmpty + ? PatientSickLeaveCard( + patientSickLeavesResponseModel: medicalFileVM.patientSickLeaveList.first, + isLoading: false, + ).paddingSymmetrical(24.h, 0.0) + : SizedBox.shrink(); + }), + SizedBox(height: 16.h), + GridView( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 13, mainAxisSpacing: 13), + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + shrinkWrap: true, + children: [ + MedicalFileCard( + label: LocaleKeys.monthlyReports.tr(context: context), + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.eye_result_icon, + isLargeText: false, + iconSize: 40.h, + ), + MedicalFileCard( + label: "Medical Reports".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.allergy_info_icon, + isLargeText: false, + iconSize: 40.h, + ).onPress(() { + medicalFileViewModel.setIsPatientMedicalReportsLoading(true); + medicalFileViewModel.getPatientMedicalReportList(); + Navigator.of(context).push( + FadePage( + page: MedicalReportsPage(), + ), + ); + }), + MedicalFileCard( + label: "Sick Leave Report".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.vaccine_info_icon, + isLargeText: false, + iconSize: 40.h, + ).onPress(() { + Navigator.of(context).push( + FadePage( + page: PatientSickleavesListPage(), + ), + ); + }), + ], + ).paddingSymmetrical(24.h, 0.0), + SizedBox(height: 24.h), + ], + ); case 3: return Container(); default: diff --git a/lib/presentation/medical_file/medical_reports_page.dart b/lib/presentation/medical_file/medical_reports_page.dart new file mode 100644 index 0000000..ea3c34d --- /dev/null +++ b/lib/presentation/medical_file/medical_reports_page.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; +import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_medical_report_card.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; +import 'package:provider/provider.dart'; + +class MedicalReportsPage extends StatefulWidget { + const MedicalReportsPage({super.key}); + + @override + State createState() => _MedicalReportsPageState(); +} + +class _MedicalReportsPageState extends State { + late MedicalFileViewModel medicalFileViewModel; + + @override + Widget build(BuildContext context) { + medicalFileViewModel = Provider.of(context, listen: false); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: CollapsingListView( + title: "Medical Reports".needTranslation, + child: SingleChildScrollView( + child: Column( + children: [ + SizedBox(height: 16.h), + CustomTabBar( + activeTextColor: Color(0xffED1C2B), + activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), + tabs: [ + CustomTabBarModel(null, "Requested".needTranslation), + CustomTabBarModel(null, "Ready".needTranslation), + CustomTabBarModel(null, "Cancelled".needTranslation), + ], + onTabChange: (index) { + medicalFileViewModel.onMedicalReportTabChange(index); + }, + ).paddingSymmetrical(24.h, 0.h), + Consumer(builder: (context, medicalFileVM, child) { + return ListView.separated( + padding: EdgeInsets.only(top: 24.h), + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: medicalFileViewModel.isPatientMedicalReportsListLoading ? 3 : medicalFileViewModel.patientMedicalReportList.length, + // medicalFileViewModel.patientMedicalReportList.isNotEmpty + // ? medicalFileViewModel.patientMedicalReportList.length + // : 1, + itemBuilder: (context, index) { + return medicalFileViewModel.isPatientMedicalReportsListLoading + ? PatientMedicalReportCard( + patientMedicalReportResponseModel: PatientMedicalReportResponseModel(), + medicalFileViewModel: medicalFileVM, + isLoading: true, + ).paddingSymmetrical(24.h, 0.h) + : 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: PatientMedicalReportCard( + patientMedicalReportResponseModel: medicalFileVM.patientMedicalReportList[index], + medicalFileViewModel: medicalFileVM, + + isLoading: false, + ), + ).paddingSymmetrical(24.h, 0.h), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ); + }), + SizedBox(height: 24.h), + ], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/medical_file/patient_sickleaves_list_page.dart b/lib/presentation/medical_file/patient_sickleaves_list_page.dart new file mode 100644 index 0000000..9950779 --- /dev/null +++ b/lib/presentation/medical_file/patient_sickleaves_list_page.dart @@ -0,0 +1,88 @@ +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/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_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:provider/provider.dart'; + +import 'widgets/patient_sick_leave_card.dart'; + +class PatientSickleavesListPage extends StatefulWidget { + const PatientSickleavesListPage({super.key}); + + @override + State createState() => _PatientSickleavesListPageState(); +} + +class _PatientSickleavesListPageState extends State { + late MedicalFileViewModel medicalFileViewModel; + + @override + void initState() { + scheduleMicrotask(() { + medicalFileViewModel.setIsPatientSickLeaveListLoading(true); + medicalFileViewModel.getPatientSickLeaveList(onError: (error) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + medicalFileViewModel = Provider.of(context, listen: false); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: CollapsingListView( + title: "${LocaleKeys.sick.tr(context: context)} ${LocaleKeys.sickSubtitle.tr(context: context)}", + child: SingleChildScrollView( + child: Consumer(builder: (context, medicalFileVM, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.separated( + scrollDirection: Axis.vertical, + itemCount: medicalFileVM.isPatientSickLeaveListLoading ? 3 : medicalFileVM.patientSickLeaveList.length, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return medicalFileVM.isPatientSickLeaveListLoading + ? PatientSickLeaveCard( + patientSickLeavesResponseModel: PatientSickLeavesResponseModel(), + isLoading: true, + ).paddingSymmetrical(24.h, 0.0) + : medicalFileVM.patientSickLeaveList.isNotEmpty + ? AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: PatientSickLeaveCard( + patientSickLeavesResponseModel: medicalFileVM.patientSickLeaveList.first, + isLoading: false, + ).paddingSymmetrical(24.h, 0.0), + ), + ), + ) + : SizedBox.shrink(); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 8.h), + ), + SizedBox(height: 60.h), + ], + ); + }), + ), + ), + ); + } +} diff --git a/lib/presentation/medical_file/vaccine_list_page.dart b/lib/presentation/medical_file/vaccine_list_page.dart new file mode 100644 index 0000000..0cf48d6 --- /dev/null +++ b/lib/presentation/medical_file/vaccine_list_page.dart @@ -0,0 +1,181 @@ +import 'dart:async'; + +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/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:provider/provider.dart'; + +import '../../widgets/chip/app_custom_chip_widget.dart'; + +class VaccineListPage extends StatefulWidget { + const VaccineListPage({super.key}); + + @override + State createState() => _VaccineListPageState(); +} + +class _VaccineListPageState extends State { + late MedicalFileViewModel medicalFileViewModel; + + @override + void initState() { + scheduleMicrotask(() { + medicalFileViewModel.setIsPatientVaccineListLoading(true); + medicalFileViewModel.getPatientVaccinesList(onError: (error) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + medicalFileViewModel = Provider.of(context, listen: false); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: CollapsingListView( + title: "Vaccine Info".needTranslation, + child: SingleChildScrollView( + child: Consumer(builder: (context, medicalFileVM, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 16.h), + ListView.separated( + scrollDirection: Axis.vertical, + itemCount: medicalFileVM.isPatientVaccineListLoading ? 5 : medicalFileVM.patientVaccineList.length, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.only(left: 24.h, right: 24.h), + itemBuilder: (context, index) { + return medicalFileVM.isPatientVaccineListLoading + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(14.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.network( + "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + width: 63.h, + height: 63.h, + fit: BoxFit.fill, + ).circle(100).toShimmer2(isShow: true), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h), + AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h), + ], + ), + ], + ), + ), + ], + ), + ], + ), + ), + ) + : AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(14.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Image.network( + // medicalFileVM.patientVaccineList[index].doctorImageURL, + // width: 63.h, + // height: 63.h, + // fit: BoxFit.fill, + // ).circle(100).toShimmer2(isShow: false), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (medicalFileVM.patientVaccineList[index].doctorName).toString().toText16(isBold: true).toShimmer2(isShow: false), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget( + icon: AppAssets.doctor_calendar_icon, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(medicalFileVM.patientVaccineList[index].vaccinationDate), false)), + AppCustomChipWidget(labelText: medicalFileVM.patientVaccineList[index].vaccineName).toShimmer2(isShow: false, width: 16.h), + AppCustomChipWidget(labelText: medicalFileVM.patientVaccineList[index].clinicName).toShimmer2(isShow: false, width: 16.h), + AppCustomChipWidget(labelText: medicalFileVM.patientVaccineList[index].projectName).toShimmer2(isShow: false, width: 16.h), + ], + ), + ], + ), + ), + ], + ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // "".toText16(), + // Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor), + // ], + // ), + ], + ), + ), + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ), + SizedBox(height: 60.h), + ], + ); + }), + ), + ), + ); + } +} diff --git a/lib/presentation/medical_file/widgets/medical_file_card.dart b/lib/presentation/medical_file/widgets/medical_file_card.dart index 82f38ec..c4980d9 100644 --- a/lib/presentation/medical_file/widgets/medical_file_card.dart +++ b/lib/presentation/medical_file/widgets/medical_file_card.dart @@ -36,7 +36,7 @@ class MedicalFileCard extends StatelessWidget { children: [ Utils.buildSvgWithAssets(icon: svgIcon, width: iconSize.h, height: iconSize.h, fit: BoxFit.contain), SizedBox(height: 12.h), - isLargeText ? label.toText14(color: textColor, isBold: true) : label.toText11(color: textColor, isBold: true), + isLargeText ? label.toText14(color: textColor, isBold: true, maxlines: 1) : label.toText11(color: textColor, isBold: true, maxLine: 2), ], ), ), diff --git a/lib/presentation/medical_file/widgets/patient_medical_report_card.dart b/lib/presentation/medical_file/widgets/patient_medical_report_card.dart new file mode 100644 index 0000000..eb1730c --- /dev/null +++ b/lib/presentation/medical_file/widgets/patient_medical_report_card.dart @@ -0,0 +1,159 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.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/date_util.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/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_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'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:open_filex/open_filex.dart'; +import 'package:share_plus/share_plus.dart'; + +class PatientMedicalReportCard extends StatelessWidget { + PatientMedicalReportCard({super.key, required this.patientMedicalReportResponseModel, required this.medicalFileViewModel, this.isLoading = false}); + + PatientMedicalReportResponseModel patientMedicalReportResponseModel; + MedicalFileViewModel medicalFileViewModel; + + bool isLoading = true; + + @override + Widget build(BuildContext context) { + AppState _appState = getIt.get(); + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.network( + isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientMedicalReportResponseModel.doctorImageURL!, + width: 63.h, + height: 63.h, + fit: BoxFit.fill, + ).circle(100).toShimmer2(isShow: isLoading), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (isLoading ? "" : patientMedicalReportResponseModel.doctorName!).toText16(isBold: true).toShimmer2(isShow: isLoading), + SizedBox(height: 4.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget(labelText: isLoading ? "" : patientMedicalReportResponseModel.clinicDescription!).toShimmer2(isShow: isLoading), + AppCustomChipWidget(labelText: isLoading ? "" : patientMedicalReportResponseModel.projectName!).toShimmer2(isShow: isLoading), + AppCustomChipWidget( + icon: AppAssets.doctor_calendar_icon, + labelText: isLoading + ? "" + : "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientMedicalReportResponseModel.requestDate), false)}, ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientMedicalReportResponseModel.requestDate), false)}") + .toShimmer2(isShow: isLoading), + AppCustomChipWidget( + icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: isLoading ? "" : "Rating: ${patientMedicalReportResponseModel.decimalDoctorRate}") + .toShimmer2(isShow: isLoading), + ], + ), + ], + ), + ), + ], + ), + patientMedicalReportResponseModel.status == 2 + ? Padding( + padding: EdgeInsets.only(top: 16.h), + child: Row( + children: [ + Expanded( + child: CustomButton( + text: "Share", + onPressed: () { + getMedicalReportPDF(true, context, _appState); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12.h, + height: 40.h, + icon: AppAssets.download_1, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + ).toShimmer2(isShow: isLoading), + ), + SizedBox(width: 16.h), + Expanded( + child: CustomButton( + text: "Download", + onPressed: () async { + getMedicalReportPDF(false, context, _appState); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12.h, + height: 40.h, + icon: AppAssets.download_1, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + ).toShimmer2(isShow: isLoading), + ), + ], + ), + ) + : SizedBox.shrink() + ], + ), + ), + ); + } + + void getMedicalReportPDF(bool isShare, BuildContext context, AppState _appState) async { + LoaderBottomSheet.showLoader(); + await medicalFileViewModel.getPatientMedicalReportPDF(patientMedicalReportResponseModel, _appState.getAuthenticatedUser()!).then((val) async { + LoaderBottomSheet.hideLoader(); + if (medicalFileViewModel.patientMedicalReportPDFBase64.isNotEmpty) { + String path = await Utils.createFileFromString(medicalFileViewModel.patientMedicalReportPDFBase64, "pdf"); + if (isShare) { + Share.shareXFiles([XFile(path)], text: "Medical Report"); + } else { + try { + OpenFilex.open(path); + } catch (ex) { + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: "Cannot open file".needTranslation), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + } + } + }); + } +} diff --git a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart new file mode 100644 index 0000000..438013f --- /dev/null +++ b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart @@ -0,0 +1,191 @@ +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/date_util.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/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.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:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; +import 'package:open_filex/open_filex.dart'; +import 'package:provider/provider.dart'; + +class PatientSickLeaveCard extends StatelessWidget { + PatientSickLeaveCard({super.key, required this.patientSickLeavesResponseModel, this.isLoading = false}); + + late MedicalFileViewModel medicalFileViewModel; + PatientSickLeavesResponseModel patientSickLeavesResponseModel; + bool isLoading; + + @override + Widget build(BuildContext context) { + AppState _appState = getIt.get(); + medicalFileViewModel = Provider.of(context, listen: false); + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24, hasShadow: true), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "${LocaleKeys.sick.tr(context: context)} ${LocaleKeys.sickSubtitle.tr(context: context)}".toText16(isBold: true), + AppCustomChipWidget( + labelText: isLoading ? "" : getStatusText(context), + backgroundColor: getStatusColor().withOpacity(0.15), + textColor: getStatusColor(), + ).toShimmer2(isShow: isLoading, width: 100.h), + ], + ), + SizedBox(height: 16.h), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.network( + isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientSickLeavesResponseModel.doctorImageURL!, + width: 30.h, + height: 30.h, + fit: BoxFit.fill, + ).circle(100).toShimmer2(isShow: isLoading), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (isLoading ? "" : patientSickLeavesResponseModel.doctorName!).toText16(isBold: true).toShimmer2(isShow: isLoading), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget( + icon: AppAssets.doctor_calendar_icon, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientSickLeavesResponseModel.appointmentDate), false), + ).toShimmer2(isShow: isLoading), + AppCustomChipWidget(labelText: isLoading ? "Pending Activation" : patientSickLeavesResponseModel.clinicName!).toShimmer2(isShow: isLoading), + ], + ), + ], + ), + ), + ], + ), + SizedBox(height: 16.h), + Row( + children: [ + isLoading + ? Container().toShimmer2(isShow: true, height: 40.h, width: 100.h, radius: 12.h) + : Expanded( + flex: 6, + child: CustomButton( + text: "Download Report".needTranslation, + onPressed: () async { + LoaderBottomSheet.showLoader(); + await medicalFileViewModel.getPatientSickLeavePDF(patientSickLeavesResponseModel, _appState.getAuthenticatedUser()!).then((val) async { + LoaderBottomSheet.hideLoader(); + if (medicalFileViewModel.patientSickLeavePDFBase64.isNotEmpty) { + String path = await Utils.createFileFromString(medicalFileViewModel.patientSickLeavePDFBase64, "pdf"); + try { + OpenFilex.open(path); + } catch (ex) { + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: "Cannot open file".needTranslation), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + } + }); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + icon: AppAssets.download, + iconColor: AppColors.primaryRedColor, + iconSize: 14.h, + ).toShimmer2(isShow: isLoading), + ), + SizedBox(width: 8.h), + Expanded( + flex: 1, + child: Container( + height: 40.h, + width: 40.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.textColor, + borderRadius: 10.h, + ), + child: Padding( + padding: EdgeInsets.all(10.h), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, + width: 10.h, + height: 10.h, + fit: BoxFit.contain, + ), + ), + ).toShimmer2(isShow: isLoading).onPress(() { + Navigator.of(context).push( + FadePage( + page: PatientSickleavesListPage(), + ), + ); + }), + ), + ], + ), + ], + ), + ), + ); + } + + String getStatusText(BuildContext context) { + String statusText = ""; + if (patientSickLeavesResponseModel.status == 1) { + statusText = LocaleKeys.pendingActivation.tr(context: context); + } else if (patientSickLeavesResponseModel.status == 2) { + statusText = LocaleKeys.ready.tr(context: context); + } else if (patientSickLeavesResponseModel.status == 3) { + statusText = LocaleKeys.awaitingApproval.tr(context: context); + } else { + statusText = ""; + } + return statusText; + } + + Color getStatusColor() { + Color statusColor = Colors.white; + if (patientSickLeavesResponseModel.status == 1) { + statusColor = Color(0xffCC9B14); + } else if (patientSickLeavesResponseModel.status == 2) { + statusColor = Color(0xff359846); + } else if (patientSickLeavesResponseModel.status == 3) { + statusColor = Color(0xffD02127); + } else { + statusColor = Colors.white; + } + return statusColor; + } +} diff --git a/lib/services/error_handler_service.dart b/lib/services/error_handler_service.dart index 59a0205..092cf38 100644 --- a/lib/services/error_handler_service.dart +++ b/lib/services/error_handler_service.dart @@ -47,7 +47,7 @@ class ErrorHandlerServiceImp implements ErrorHandlerService { } else { loggerService.errorLogs("Unhandled failure type: $failure"); - await _showDialog(failure, title: "Error"); + await _showDialog(failure, title: "Error", onOkPressed: onOkPressed); } } diff --git a/pubspec.yaml b/pubspec.yaml index 36326f4..147d7ac 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -76,6 +76,8 @@ dependencies: flutter_nfc_kit: ^3.6.0 barcode_scan2: ^4.5.1 keyboard_actions: ^4.2.0 + path_provider: ^2.0.8 + open_filex: ^4.7.0 dev_dependencies: flutter_test: