From a9d085098fcdb2ad1a2088af971ce3ad4fbc0f92 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Sun, 10 May 2026 14:52:40 +0300 Subject: [PATCH] Pharmacy invoice implemented --- lib/core/api/api_client.dart | 2 +- lib/core/api_consts.dart | 5 +- .../get_pharmacy_invoices_response_model.dart | 92 +++++++++ .../my_invoices/my_invoices_repo.dart | 84 ++++++++ .../my_invoices/my_invoices_view_model.dart | 68 +++++++ .../medical_file/medical_file_page.dart | 2 +- .../my_invoices/my_invoices_list.dart | 106 +++++++++- .../widgets/pharmacy_invoice_card.dart | 186 ++++++++++++++++++ 8 files changed, 532 insertions(+), 13 deletions(-) create mode 100644 lib/features/my_invoices/models/get_pharmacy_invoices_response_model.dart create mode 100644 lib/presentation/my_invoices/widgets/pharmacy_invoice_card.dart diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index b5d62899..958deaf2 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -209,7 +209,7 @@ class ApiClientImp implements ApiClient { } body['TokenID'] = "@dm!n"; - // body['PatientID'] = 5787639; + // body['PatientID'] = 1624918; // body['PatientTypeID'] = 1; // body['PatientOutSA'] = 0; // body['SessionID'] = "45786230487560q"; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 301cb31c..96d80118 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.preProd; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT @@ -867,6 +867,7 @@ var GET_DOCTOR_LIST_CALCULATION = "Services/Doctors.svc/REST/GetCallculationDoct // var GET_ALL_APPOINTMENTS_FOR_DENTAL_CLINIC = "Services/Patients.svc/REST/GetDentalAppointments"; var GET_ALL_APPOINTMENTS_FOR_DENTAL_CLINIC = 'Services/Patients.svc/REST/GetAllInvoices'; +var GET_ALL_PHARMACY_INVOICES = 'Services/Patients.svc/REST/GetAllPharmacyInvoices'; var GET_DENTAL_APPOINTMENT_INVOICE = "Services/Patients.svc/REST/HIS_eInvoiceForDentalByAppointmentNo"; var SEND_DENTAL_APPOINTMENT_INVOICE_EMAIL = "Services/Notifications.svc/REST/SendInvoiceForDental"; @@ -990,6 +991,8 @@ const UPLOAD_PROFILE_IMAGE = 'Services/Patients.svc/REST/Patient_InsertProfileIm const DOWNLOAD_INVOICE_PDF = 'Services/Notifications.svc/REST/DownloadInvoiceReport'; +const DOWNLOAD_PHARMACY_INVOICE_PDF = 'Services/Notifications.svc/REST/DownloadInvoiceReport'; + class ApiKeyConstants { static final String googleMapsApiKey = 'AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng'; } diff --git a/lib/features/my_invoices/models/get_pharmacy_invoices_response_model.dart b/lib/features/my_invoices/models/get_pharmacy_invoices_response_model.dart new file mode 100644 index 00000000..2128e4e1 --- /dev/null +++ b/lib/features/my_invoices/models/get_pharmacy_invoices_response_model.dart @@ -0,0 +1,92 @@ +class GetPharmacyInvoicesResponseModel { + String? setupId; + int? projectID; + int? patientID; + int? appointmentNo; + String? appointmentDate; + dynamic appointmentDateN; + int? clinicID; + int? doctorID; + int? invoiceNo; + int? status; + String? arrivedOn; + String? doctorName; + String? doctorNameN; + String? displayInvoiceNo; + String? clinicName; + double? decimalDoctorRate; + String? doctorImageURL; + num? doctorRate; + num? patientNumber; + String? projectName; + + GetPharmacyInvoicesResponseModel( + {this.setupId, + this.projectID, + this.patientID, + this.appointmentNo, + this.appointmentDate, + this.appointmentDateN, + this.clinicID, + this.doctorID, + this.invoiceNo, + this.status, + this.arrivedOn, + this.doctorName, + this.doctorNameN, + this.displayInvoiceNo, + this.clinicName, + this.decimalDoctorRate, + this.doctorImageURL, + this.doctorRate, + this.patientNumber, + this.projectName}); + + GetPharmacyInvoicesResponseModel.fromJson(Map json) { + setupId = json['SetupId']; + projectID = json['ProjectID']; + patientID = json['PatientID']; + appointmentNo = json['AppointmentNo']; + appointmentDate = json['AppointmentDate']; + appointmentDateN = json['AppointmentDateN']; + clinicID = json['ClinicID']; + doctorID = json['DoctorID']; + invoiceNo = json['InvoiceNo']; + status = json['Status']; + arrivedOn = json['ArrivedOn']; + doctorName = json['DoctorName']; + doctorNameN = json['DoctorNameN']; + displayInvoiceNo = json['DisplayInvoiceNo']; + clinicName = json['ClinicName']; + decimalDoctorRate = json['DecimalDoctorRate']; + doctorImageURL = json['DoctorImageURL']; + doctorRate = json['DoctorRate']; + patientNumber = json['PatientNumber']; + projectName = json['ProjectName']; + } + + Map toJson() { + final Map data = new Map(); + data['SetupId'] = this.setupId; + data['ProjectID'] = this.projectID; + data['PatientID'] = this.patientID; + data['AppointmentNo'] = this.appointmentNo; + data['AppointmentDate'] = this.appointmentDate; + data['AppointmentDateN'] = this.appointmentDateN; + data['ClinicID'] = this.clinicID; + data['DoctorID'] = this.doctorID; + data['InvoiceNo'] = this.invoiceNo; + data['Status'] = this.status; + data['ArrivedOn'] = this.arrivedOn; + data['DoctorName'] = this.doctorName; + data['DoctorNameN'] = this.doctorNameN; + data['DisplayInvoiceNo'] = this.displayInvoiceNo; + data['ClinicName'] = this.clinicName; + data['DecimalDoctorRate'] = this.decimalDoctorRate; + data['DoctorImageURL'] = this.doctorImageURL; + data['DoctorRate'] = this.doctorRate; + data['PatientNumber'] = this.patientNumber; + data['ProjectName'] = this.projectName; + return data; + } +} diff --git a/lib/features/my_invoices/my_invoices_repo.dart b/lib/features/my_invoices/my_invoices_repo.dart index 486fdc2a..cee29489 100644 --- a/lib/features/my_invoices/my_invoices_repo.dart +++ b/lib/features/my_invoices/my_invoices_repo.dart @@ -5,16 +5,21 @@ 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/features/my_invoices/models/get_invoice_details_response_model.dart'; import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoices_list_response_model.dart'; +import 'package:hmg_patient_app_new/features/my_invoices/models/get_pharmacy_invoices_response_model.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; abstract class MyInvoicesRepo { Future>>> getAllInvoicesList(); + Future>>> getAllPharmacyInvoices(); + Future>> getInvoiceDetails({required num appointmentNo, required num invoiceNo, required int projectID}); Future>> sendInvoiceEmail({required num appointmentNo, required int projectID}); Future>> downloadInvoice({required String setupId, required int invoiceNo, required int projectID}); + + Future>> downloadPharmacyInvoice({required String setupId, required int invoiceNo, required int projectID}); } class MyInvoicesRepoImp implements MyInvoicesRepo { @@ -61,6 +66,47 @@ class MyInvoicesRepoImp implements MyInvoicesRepo { } } + @override + Future>>> getAllPharmacyInvoices() async { + Map mapDevice = {}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_ALL_PHARMACY_INVOICES, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['List_AllPharmacyInvoices'] ?? response['PharmacyInvoicesList'] ?? []; + + final invoicesList = list + .map((item) => GetPharmacyInvoicesResponseModel.fromJson(item as Map)) + .toList() + .cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: invoicesList, + ); + } 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>> getInvoiceDetails({required num appointmentNo, required num invoiceNo, required int projectID}) async { Map mapDevice = { @@ -178,4 +224,42 @@ class MyInvoicesRepoImp implements MyInvoicesRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future> downloadPharmacyInvoice({required String setupId, required int invoiceNo, required int projectID}) async { + Map mapDevice = { + "SetupID": setupId, + "ProjectID": projectID, + "InvoiceNo": invoiceNo, + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + DOWNLOAD_PHARMACY_INVOICE_PDF, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response["InvoiceReportPDFContent"], + ); + } 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_invoices/my_invoices_view_model.dart b/lib/features/my_invoices/my_invoices_view_model.dart index a5d1e0b1..f39ba65b 100644 --- a/lib/features/my_invoices/my_invoices_view_model.dart +++ b/lib/features/my_invoices/my_invoices_view_model.dart @@ -3,6 +3,7 @@ import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoice_details_response_model.dart'; import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoices_list_response_model.dart'; +import 'package:hmg_patient_app_new/features/my_invoices/models/get_pharmacy_invoices_response_model.dart'; import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_repo.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; @@ -12,6 +13,7 @@ enum InvoiceFilterType { all, hospital, clinic, doctor } class MyInvoicesViewModel extends ChangeNotifier { bool isInvoicesListLoading = false; bool isInvoiceDetailsLoading = false; + bool isPharmacyInvoicesLoading = false; MyInvoicesRepo myInvoicesRepo; ErrorHandlerService errorHandlerService; @@ -19,6 +21,8 @@ class MyInvoicesViewModel extends ChangeNotifier { List allInvoicesList = []; List _originalInvoicesList = []; + List allPharmacyInvoicesList = []; + List _originalPharmacyInvoicesList = []; InvoiceFilterType currentFilter = InvoiceFilterType.all; late GetInvoiceDetailsResponseModel invoiceDetailsResponseModel; @@ -29,6 +33,7 @@ class MyInvoicesViewModel extends ChangeNotifier { String? selectedFilterItem; String? downloadInvoicePDFBase64 = ""; + String? downloadPharmacyInvoicePDFBase64 = ""; int selectedTabIndex = 0; @@ -36,9 +41,12 @@ class MyInvoicesViewModel extends ChangeNotifier { setInvoicesListLoading() { isInvoicesListLoading = true; + isPharmacyInvoicesLoading = true; allInvoicesList.clear(); + allPharmacyInvoicesList.clear(); _originalInvoicesList.clear(); currentFilter = InvoiceFilterType.all; + onTabChanged(0); notifyListeners(); } @@ -79,6 +87,40 @@ class MyInvoicesViewModel extends ChangeNotifier { ); } + Future getAllPharmacyInvoices({Function(dynamic)? onSuccess, Function(String)? onError}) async { + isPharmacyInvoicesLoading = true; + allPharmacyInvoicesList.clear(); + notifyListeners(); + + final result = await myInvoicesRepo.getAllPharmacyInvoices(); + + result.fold( + (failure) async { + isPharmacyInvoicesLoading = false; + notifyListeners(); + if (onError != null) { + onError(failure.message); + } + }, + (apiResponse) { + _originalPharmacyInvoicesList = []; + allPharmacyInvoicesList = []; + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + _originalPharmacyInvoicesList = apiResponse.data ?? []; + _originalPharmacyInvoicesList.sort((a, b) => (b.appointmentDate ?? '').compareTo(a.appointmentDate ?? '')); + allPharmacyInvoicesList = List.from(_originalPharmacyInvoicesList); + isPharmacyInvoicesLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + Future getInvoiceDetails({required num appointmentNo, required num invoiceNo, required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await myInvoicesRepo.getInvoiceDetails(appointmentNo: appointmentNo, invoiceNo: invoiceNo, projectID: projectID); result.fold( @@ -151,6 +193,32 @@ class MyInvoicesViewModel extends ChangeNotifier { ); } + Future downloadPharmacyInvoicePDF({required String setupId, required int invoiceNo, required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myInvoicesRepo.downloadPharmacyInvoice(setupId: setupId, invoiceNo: invoiceNo, projectID: projectID); + + result.fold( + (failure) async { + if (onError != null) { + onError(failure.message); + } + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + if (apiResponse.data != null && apiResponse.data!.isNotEmpty) { + downloadPharmacyInvoicePDFBase64 = apiResponse.data!; + } else { + downloadPharmacyInvoicePDFBase64 = ""; + } + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + void filterInvoices(InvoiceFilterType filterType) { currentFilter = filterType; diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index b60cd8bc..c22e261d 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -584,7 +584,7 @@ class _MedicalFilePageState extends State { ) : Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.bgRedLightColor, + // color: AppColors.bgRedLightColor, borderRadius: 12.r, hasShadow: false, ), diff --git a/lib/presentation/my_invoices/my_invoices_list.dart b/lib/presentation/my_invoices/my_invoices_list.dart index ae191d89..d23639b9 100644 --- a/lib/presentation/my_invoices/my_invoices_list.dart +++ b/lib/presentation/my_invoices/my_invoices_list.dart @@ -5,22 +5,18 @@ 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/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/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; -import 'package:hmg_patient_app_new/presentation/my_invoices/my_invoices_details_page.dart'; import 'package:hmg_patient_app_new/presentation/my_invoices/widgets/invoice_filter_bottom_sheet.dart'; import 'package:hmg_patient_app_new/presentation/my_invoices/widgets/invoice_list_card.dart'; +import 'package:hmg_patient_app_new/presentation/my_invoices/widgets/pharmacy_invoice_card.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; -import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:open_filex/open_filex.dart'; import 'package:provider/provider.dart'; @@ -135,6 +131,10 @@ class _MyInvoicesListState extends State { ], onTabChange: (index) { myInvoicesVM.onTabChanged(index); + if (index == 1 && myInvoicesVM.allPharmacyInvoicesList.isEmpty) { + // Load pharmacy invoices when pharmacy tab is selected and list is empty + myInvoicesVM.getAllPharmacyInvoices(); + } }, ).paddingSymmetrical(24.h, 0.h), myInvoicesVM.selectedTabIndex == 0 ? Column( @@ -148,7 +148,7 @@ class _MyInvoicesListState extends State { myInvoicesViewModel.filterInvoices(InvoiceFilterType.all); }, backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.all ? AppColors.bgRedLightColor : AppColors.whiteColor, - borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.all ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), + borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.all ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), textColor: myInvoicesVM.currentFilter == InvoiceFilterType.all ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, fontWeight: FontWeight.w600, @@ -164,7 +164,7 @@ class _MyInvoicesListState extends State { _showHospitalFilterBottomSheet(context); }, backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.bgRedLightColor : AppColors.whiteColor, - borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), + borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), textColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, fontWeight: FontWeight.w600, @@ -180,7 +180,7 @@ class _MyInvoicesListState extends State { _showClinicFilterBottomSheet(context); }, backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.bgRedLightColor : AppColors.whiteColor, - borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), + borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), textColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, fontWeight: FontWeight.w600, @@ -196,7 +196,7 @@ class _MyInvoicesListState extends State { _showDoctorFilterBottomSheet(context); }, backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.bgRedLightColor : AppColors.whiteColor, - borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), + borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), textColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, fontWeight: FontWeight.w600, @@ -329,7 +329,93 @@ class _MyInvoicesListState extends State { : Utils.getNoDataWidget(context); }).paddingSymmetrical(24.w, 0.h), ], - ) : Container(), + ) + : Column( + children: [ + SizedBox(height: 16.h), + ListView.builder( + itemCount: myInvoicesVM.isPharmacyInvoicesLoading + ? 4 + : myInvoicesVM.allPharmacyInvoicesList.isEmpty + ? 1 + : myInvoicesVM.allPharmacyInvoicesList.length, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: EdgeInsetsGeometry.zero, + itemBuilder: (context, index) { + return myInvoicesVM.isPharmacyInvoicesLoading + ? LabResultItemView(onTap: () {}, labOrder: null, index: index, isLoading: true) + : myInvoicesVM.allPharmacyInvoicesList.isNotEmpty + ? 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, + child: PharmacyInvoiceCard( + pharmacyInvoice: myInvoicesVM.allPharmacyInvoicesList[index], + onTap: () async { + // if (Utils.isVidaPlusProject(myInvoicesVM.allPharmacyInvoicesList[index].projectID ?? 0)) { + // // For Vida+ projects, send email (if API is available) + // showCommonBottomSheetWithoutHeight( + // context, + // child: Utils.getSuccessWidget( + // loadingText: LocaleKeys.comingSoon.tr(context: context), + // ), + // callBackFunc: () {}, + // isFullScreen: false, + // isCloseButtonVisible: true, + // isAutoDismiss: true, + // ); + // } else { + // Download pharmacy invoice PDF + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context)); + myInvoicesViewModel.downloadPharmacyInvoicePDF( + setupId: myInvoicesVM.allPharmacyInvoicesList[index].setupId ?? "", + invoiceNo: myInvoicesVM.allPharmacyInvoicesList[index].invoiceNo ?? 0, + projectID: myInvoicesVM.allPharmacyInvoicesList[index].projectID ?? 0, + onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }, + onSuccess: (value) async { + LoaderBottomSheet.hideLoader(); + if (myInvoicesViewModel.downloadPharmacyInvoicePDFBase64!.isNotEmpty) { + String path = await Utils.createFileFromString(myInvoicesViewModel.downloadPharmacyInvoicePDFBase64!, "pdf"); + try { + OpenFilex.open(path); + } catch (ex) { + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: "Cannot open file"), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + } + }, + ); + // } + }, + ), + ), + ), + ), + ) + : Utils.getNoDataWidget(context); + }).paddingSymmetrical(24.w, 0.h), + ], + ), ], ); }), diff --git a/lib/presentation/my_invoices/widgets/pharmacy_invoice_card.dart b/lib/presentation/my_invoices/widgets/pharmacy_invoice_card.dart new file mode 100644 index 00000000..6d672b38 --- /dev/null +++ b/lib/presentation/my_invoices/widgets/pharmacy_invoice_card.dart @@ -0,0 +1,186 @@ +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/my_invoices/models/get_pharmacy_invoices_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 'dart:ui' as ui; + +class PharmacyInvoiceCard extends StatelessWidget { + final GetPharmacyInvoicesResponseModel pharmacyInvoice; + final VoidCallback onTap; + + const PharmacyInvoiceCard({ + super.key, + required this.pharmacyInvoice, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + final isArabic = getIt().isArabic(); + + return 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: [ + // Type chips + // Wrap( + // alignment: WrapAlignment.start, + // direction: Axis.horizontal, + // spacing: 6.w, + // runSpacing: 6.h, + // children: [ + // AppCustomChipWidget( + // icon: AppAssets.pharmacy_icon, + // iconColor: AppColors.primaryRedColor, + // labelText: LocaleKeys.pharmacy.tr(context: context), + // textColor: AppColors.primaryRedColor, + // ), + // if (pharmacyInvoice.totalAmount != null) + // AppCustomChipWidget( + // labelText: '${pharmacyInvoice.totalAmount!.toStringAsFixed(2)} ${LocaleKeys.sar.tr(context: context)}', + // backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1), + // textColor: AppColors.primaryRedColor, + // isEnglishOnly: true, + // ), + // ], + // ), + SizedBox(height: 16.h), + + // Main content row + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: [ + Image.network( + pharmacyInvoice.doctorImageURL ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png', + width: 63.h, + height: 63.h, + fit: BoxFit.cover, + ).circle(100.r), + Transform.translate( + offset: Offset(0.0, -20.h), + child: Container( + width: 40.w, + height: 40.h, + decoration: BoxDecoration( + color: AppColors.whiteColor, + shape: BoxShape.circle, + border: Border.all( + color: AppColors.scaffoldBgColor, + width: 1.5.w, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, applyThemeColor: false), + SizedBox(height: 2.h), + "${pharmacyInvoice.decimalDoctorRate ?? '0.0'}".toText11(isBold: true, color: AppColors.textColor), + ], + ), + ).circle(100), + ), + ], + ), + SizedBox(width: 16.w), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (getIt().isArabic() + ? (pharmacyInvoice.doctorNameN ?? pharmacyInvoice.doctorName ?? LocaleKeys.doctor.tr(context: context)) + : (pharmacyInvoice.doctorName ?? pharmacyInvoice.doctorNameN ?? LocaleKeys.doctor.tr(context: context)) + ).toText16(isBold: true), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 6.w, + runSpacing: 6.h, + children: [ + AppCustomChipWidget( + labelText: "${LocaleKeys.invoiceNo.tr(context: context)}: ${pharmacyInvoice.invoiceNo ?? '-'}", + labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), + isEnglishOnly: true, + ), + AppCustomChipWidget( + labelText: ((pharmacyInvoice.clinicName ?? LocaleKeys.clinic.tr(context: context)).length > 15 + ? '${(pharmacyInvoice.clinicName ?? LocaleKeys.clinic.tr(context: context)).substring(0, 12)}...' + : (pharmacyInvoice.clinicName ?? LocaleKeys.clinic.tr(context: context))), + labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w), + ), + AppCustomChipWidget( + labelText: pharmacyInvoice.projectName ?? LocaleKeys.hospital.tr(context: context), + labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), + ), + if (pharmacyInvoice.appointmentDate != null) + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), + icon: AppAssets.doctor_calendar_icon, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(pharmacyInvoice.appointmentDate), false), + isEnglishOnly: true, + ), + ), + ], + ), + ], + ), + ), + ], + ), + SizedBox(height: 16.h), + + // Action button + CustomButton( + text: Utils.isVidaPlusProject(pharmacyInvoice.projectID ?? 0) + ? LocaleKeys.sendEmail.tr(context: context) + : LocaleKeys.downloadInvoice.tr(context: context), + onPressed: onTap, + backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1), + borderColor: AppColors.primaryRedColor.withValues(alpha: 0.01), + textColor: AppColors.primaryRedColor, + fontSize: 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + iconSize: 14.h, + ), + ], + ), + ), + ).paddingOnly(bottom: 16.h); + } + + String _formatDate(String? dateString) { + if (dateString == null || dateString.isEmpty) return '-'; + try { + final date = DateUtil.convertStringToDate(dateString); + return DateUtil.formatDateToDate(date, false); + } catch (e) { + return dateString; + } + } +} +