Merge remote-tracking branch 'origin/master' into reminer

reminer
tahaalam 21 hours ago
commit a8138ba231

@ -752,6 +752,11 @@
"erConsultation": "تتيح لك هذه الخدمة إجراء استشارة افتراضية عبر الإنترنت عبر مكالمة فيديو مباشرة مع الطبيب من أي مكان وفي أي وقت.", "erConsultation": "تتيح لك هذه الخدمة إجراء استشارة افتراضية عبر الإنترنت عبر مكالمة فيديو مباشرة مع الطبيب من أي مكان وفي أي وقت.",
"myInvoice": "القائمة", "myInvoice": "القائمة",
"invoiceList": "فواتيري", "invoiceList": "فواتيري",
"allInvoices": "كل الفواتير",
"hospitals": "المستشفيات",
"clinics": "العيادات",
"doctors": "الأطباء",
"selectDoctor": "اختر الطبيب",
"thisItemIsNotAvailable": "هذا العنصر غير متوفر", "thisItemIsNotAvailable": "هذا العنصر غير متوفر",
"beforeAfterImages": "صور قبل وبعد", "beforeAfterImages": "صور قبل وبعد",
"clinicAcceptLivecare": "لا حاجة للانتظار أو الزيارة يمكنك الآن الحصول على استشارة طبية عبر مكالمة فيديو (لايف كير) في اسم العيادة وسيتصل بك الطبيب على الفور", "clinicAcceptLivecare": "لا حاجة للانتظار أو الزيارة يمكنك الآن الحصول على استشارة طبية عبر مكالمة فيديو (لايف كير) في اسم العيادة وسيتصل بك الطبيب على الفور",

@ -744,6 +744,11 @@
"erConsultation": "This service allows you to make an online virtual consultation via video call directly with the doctor from anywhere at any time.", "erConsultation": "This service allows you to make an online virtual consultation via video call directly with the doctor from anywhere at any time.",
"myInvoice": "List", "myInvoice": "List",
"invoiceList": "My Invoice", "invoiceList": "My Invoice",
"allInvoices": "All Invoices",
"hospitals": "Hospitals",
"clinics": "Clinics",
"doctors": "Doctors",
"selectDoctor": "Select Doctor",
"thisItemIsNotAvailable": "This item is not available", "thisItemIsNotAvailable": "This item is not available",
"beforeAfterImages": "Before & After Images", "beforeAfterImages": "Before & After Images",
"clinicAcceptLivecare": "No need to wait or visit You can now get medical consultation via Video call (LiveCare service) in The name of the clinic clinic and the doctor will contact you immediately", "clinicAcceptLivecare": "No need to wait or visit You can now get medical consultation via Video call (LiveCare service) in The name of the clinic clinic and the doctor will contact you immediately",

@ -315,7 +315,13 @@ class AuthenticationRepoImp implements AuthenticationRepo {
} }
if (appState.getSuperUserID == responseID) { if (appState.getSuperUserID == responseID) {
// switchRequest['LoginType'] = 0;
switchRequest['PatientIdentificationID'] = ""; switchRequest['PatientIdentificationID'] = "";
// switchRequest["PatientID"] = responseID;
// switchRequest["SuperUser"] = responseID;
// switchRequest["PatientID"] = appState.getAuthenticatedUser()?.patientId ?? 0;
// switchRequest["PatientMobileNumber"] = newRequest.patientMobileNumber?.toString().startsWith('0') == true ? newRequest.patientMobileNumber.toString() : '0${newRequest.patientMobileNumber}';
switchRequest.removeWhere((key, value) => ['NationalID', 'isDentalAllowedBackend', 'ProjectOutSA', 'ForRegisteration'].contains(key)); switchRequest.removeWhere((key, value) => ['NationalID', 'isDentalAllowedBackend', 'ProjectOutSA', 'ForRegisteration'].contains(key));
} }
} }

@ -387,7 +387,8 @@ class LabViewModel extends ChangeNotifier {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
var recentThree = sort(apiResponse.data!); var sortedResult = sort(apiResponse.data!);
var recentThree = sortedResult.take(3).toList();
mainLabResults = recentThree; mainLabResults = recentThree;
double highRefrenceValue = double.negativeInfinity; double highRefrenceValue = double.negativeInfinity;
@ -395,11 +396,12 @@ class LabViewModel extends ChangeNotifier {
double lowRefenceValue = double.infinity; double lowRefenceValue = double.infinity;
String? flagForLowReferenceRange; String? flagForLowReferenceRange;
recentThree.reversed.forEach((element) { sortedResult.take(3).toList().reversed.forEach((element) {
try { try {
var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
var resultValue = double.parse(element.resultValue!); var resultValue = double.parse(element.resultValue!);
var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? ""); // var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? "");
var transformedValue = resultValue;
if (resultValue > maxY) { if (resultValue > maxY) {
maxY = resultValue; maxY = resultValue;
maxX = maxY; maxX = maxY;
@ -431,9 +433,9 @@ class LabViewModel extends ChangeNotifier {
highRefrenceValue = maxY; highRefrenceValue = maxY;
lowRefenceValue = minY; lowRefenceValue = minY;
} }
//
if (minY > lowRefenceValue) { if (minY > lowRefenceValue) {
minY = lowRefenceValue - 25; minY = lowRefenceValue - getInterval();
} }
this.flagForHighReferenceRange = flagForHighReferenceRange; this.flagForHighReferenceRange = flagForHighReferenceRange;
@ -442,12 +444,16 @@ class LabViewModel extends ChangeNotifier {
lowTransformedReferenceValue = double.parse(transformValueInRange(lowRefenceValue, flagForLowReferenceRange ?? "").toStringAsFixed(1)); lowTransformedReferenceValue = double.parse(transformValueInRange(lowRefenceValue, flagForLowReferenceRange ?? "").toStringAsFixed(1));
this.highRefrenceValue = double.parse(highRefrenceValue.toStringAsFixed(1)); this.highRefrenceValue = double.parse(highRefrenceValue.toStringAsFixed(1));
this.lowRefenceValue = double.parse(lowRefenceValue.toStringAsFixed(1)); this.lowRefenceValue = double.parse(lowRefenceValue.toStringAsFixed(1));
if(maxY<highRefrenceValue){
if (maxY < highRefrenceValue) {
maxY = highRefrenceValue; maxY = highRefrenceValue;
maxX = maxY;
} }
maxY += 25; // if (maxY < highRefrenceValue) {
minY -= 25; // minY = highRefrenceValue - getInterval();
// }
// maxY += 25;
// minY -= 25;
LabResult recentResult = recentThree.first; LabResult recentResult = recentThree.first;
recentResult.uOM = unitOfMeasure; recentResult.uOM = unitOfMeasure;
checkIfGraphShouldBeDisplayed(recentResult); checkIfGraphShouldBeDisplayed(recentResult);
@ -464,6 +470,17 @@ class LabViewModel extends ChangeNotifier {
}, },
); );
} }
num getInterval() {
// return .1;
var maxX = maxY;
if(maxX<1) return .2;
if(maxX >=1.0 && maxX < 5.0) return .3;
if(maxX >=5.0 && maxX < 10.0) return 1.5;
if(maxX >=10.0 && maxX < 50.0) return 2.5;
if(maxX >=50.0 && maxX < 100.0) return 5;
if(maxX >=100.0 && maxX < 200.0) return 10;
return 15;
}
void checkIfGraphShouldBeDisplayed(LabResult recentResult) { void checkIfGraphShouldBeDisplayed(LabResult recentResult) {
shouldShowGraph = recentResult.checkIfGraphShouldBeDisplayed(); shouldShowGraph = recentResult.checkIfGraphShouldBeDisplayed();
@ -586,7 +603,8 @@ class LabViewModel extends ChangeNotifier {
try { try {
var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
var resultValue = double.parse(element.resultValue!); var resultValue = double.parse(element.resultValue!);
var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? ""); // var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? "");
var transformedValue = double.parse(element.resultValue!);
if (resultValue > maxY) { if (resultValue > maxY) {
maxY = resultValue; maxY = resultValue;
} }

@ -5,6 +5,8 @@ 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/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
enum InvoiceFilterType { all, hospital, clinic, doctor }
class MyInvoicesViewModel extends ChangeNotifier { class MyInvoicesViewModel extends ChangeNotifier {
bool isInvoicesListLoading = false; bool isInvoicesListLoading = false;
bool isInvoiceDetailsLoading = false; bool isInvoiceDetailsLoading = false;
@ -14,13 +16,23 @@ class MyInvoicesViewModel extends ChangeNotifier {
NavigationService navServices; NavigationService navServices;
List<GetInvoicesListResponseModel> allInvoicesList = []; List<GetInvoicesListResponseModel> allInvoicesList = [];
List<GetInvoicesListResponseModel> _originalInvoicesList = [];
InvoiceFilterType currentFilter = InvoiceFilterType.all;
late GetInvoiceDetailsResponseModel invoiceDetailsResponseModel; late GetInvoiceDetailsResponseModel invoiceDetailsResponseModel;
// Filter bottom sheet properties
List<String> filterDisplayList = [];
List<String> _filterOriginalList = [];
TextEditingController filterSearchController = TextEditingController();
String? selectedFilterItem;
MyInvoicesViewModel({required this.myInvoicesRepo, required this.errorHandlerService, required this.navServices}); MyInvoicesViewModel({required this.myInvoicesRepo, required this.errorHandlerService, required this.navServices});
setInvoicesListLoading() { setInvoicesListLoading() {
isInvoicesListLoading = true; isInvoicesListLoading = true;
allInvoicesList.clear(); allInvoicesList.clear();
_originalInvoicesList.clear();
currentFilter = InvoiceFilterType.all;
notifyListeners(); notifyListeners();
} }
@ -41,9 +53,10 @@ class MyInvoicesViewModel extends ChangeNotifier {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
allInvoicesList = apiResponse.data!; _originalInvoicesList = apiResponse.data!;
_originalInvoicesList.sort((a, b) => b.appointmentDate!.compareTo(a.appointmentDate!));
allInvoicesList = List.from(_originalInvoicesList);
isInvoicesListLoading = false; isInvoicesListLoading = false;
allInvoicesList.sort((a, b) => b.appointmentDate!.compareTo(a.appointmentDate!));
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);
@ -99,4 +112,128 @@ class MyInvoicesViewModel extends ChangeNotifier {
}, },
); );
} }
void filterInvoices(InvoiceFilterType filterType) {
currentFilter = filterType;
switch (filterType) {
case InvoiceFilterType.all:
allInvoicesList = List.from(_originalInvoicesList);
break;
case InvoiceFilterType.hospital:
allInvoicesList = _originalInvoicesList.where((invoice) {
return invoice.projectName != null && invoice.projectName!.isNotEmpty;
}).toList();
// Group by hospital (projectName) and sort
allInvoicesList.sort((a, b) {
int projectComparison = (a.projectName ?? '').compareTo(b.projectName ?? '');
if (projectComparison != 0) return projectComparison;
return b.appointmentDate!.compareTo(a.appointmentDate!);
});
break;
case InvoiceFilterType.clinic:
allInvoicesList = _originalInvoicesList.where((invoice) {
return invoice.clinicName != null && invoice.clinicName!.isNotEmpty;
}).toList();
// Group by clinic (clinicName) and sort
allInvoicesList.sort((a, b) {
int clinicComparison = (a.clinicName ?? '').compareTo(b.clinicName ?? '');
if (clinicComparison != 0) return clinicComparison;
return b.appointmentDate!.compareTo(a.appointmentDate!);
});
break;
case InvoiceFilterType.doctor:
allInvoicesList = _originalInvoicesList.where((invoice) {
return invoice.doctorName != null && invoice.doctorName!.isNotEmpty;
}).toList();
// Group by doctor (doctorName) and sort
allInvoicesList.sort((a, b) {
int doctorComparison = (a.doctorName ?? '').compareTo(b.doctorName ?? '');
if (doctorComparison != 0) return doctorComparison;
return b.appointmentDate!.compareTo(a.appointmentDate!);
});
break;
}
notifyListeners();
}
void filterInvoicesByHospital(String hospitalName) {
currentFilter = InvoiceFilterType.hospital;
allInvoicesList = _originalInvoicesList
.where((invoice) => invoice.projectName == hospitalName)
.toList();
allInvoicesList.sort((a, b) => b.appointmentDate!.compareTo(a.appointmentDate!));
notifyListeners();
}
void filterInvoicesByClinic(String clinicName) {
currentFilter = InvoiceFilterType.clinic;
allInvoicesList = _originalInvoicesList
.where((invoice) => invoice.clinicName == clinicName)
.toList();
allInvoicesList.sort((a, b) => b.appointmentDate!.compareTo(a.appointmentDate!));
notifyListeners();
}
void filterInvoicesByDoctor(String doctorName) {
currentFilter = InvoiceFilterType.doctor;
allInvoicesList = _originalInvoicesList
.where((invoice) => invoice.doctorName == doctorName)
.toList();
allInvoicesList.sort((a, b) => b.appointmentDate!.compareTo(a.appointmentDate!));
notifyListeners();
}
List<GetInvoicesListResponseModel> getOriginalInvoicesList() {
return _originalInvoicesList;
}
// Filter bottom sheet methods
void prepareFilterList(InvoiceFilterType filterType) {
Set<String> uniqueItems = {};
for (var invoice in _originalInvoicesList) {
String? itemName;
if (filterType == InvoiceFilterType.hospital) {
itemName = invoice.projectName;
} else if (filterType == InvoiceFilterType.clinic) {
itemName = invoice.clinicName;
} else if (filterType == InvoiceFilterType.doctor) {
itemName = invoice.doctorName;
}
if (itemName != null && itemName.isNotEmpty) {
uniqueItems.add(itemName);
}
}
_filterOriginalList = uniqueItems.toList()..sort();
filterDisplayList = List.from(_filterOriginalList);
filterSearchController.clear();
selectedFilterItem = null;
notifyListeners();
}
void searchFilterItems(String query) {
if (query.isEmpty) {
filterDisplayList = List.from(_filterOriginalList);
} else {
filterDisplayList = _filterOriginalList
.where((item) => item.toLowerCase().contains(query.toLowerCase()))
.toList();
}
notifyListeners();
}
void clearFilterSearch() {
filterSearchController.clear();
filterDisplayList = List.from(_filterOriginalList);
notifyListeners();
}
void selectFilterItem(String item) {
selectedFilterItem = item;
notifyListeners();
}
} }

@ -751,6 +751,11 @@ abstract class LocaleKeys {
static const erConsultation = 'erConsultation'; static const erConsultation = 'erConsultation';
static const myInvoice = 'myInvoice'; static const myInvoice = 'myInvoice';
static const invoiceList = 'invoiceList'; static const invoiceList = 'invoiceList';
static const allInvoices = 'allInvoices';
static const hospitals = 'hospitals';
static const clinics = 'clinics';
static const doctors = 'doctors';
static const selectDoctor = 'selectDoctor';
static const thisItemIsNotAvailable = 'thisItemIsNotAvailable'; static const thisItemIsNotAvailable = 'thisItemIsNotAvailable';
static const beforeAfterImages = 'beforeAfterImages'; static const beforeAfterImages = 'beforeAfterImages';
static const clinicAcceptLivecare = 'clinicAcceptLivecare'; static const clinicAcceptLivecare = 'clinicAcceptLivecare';

@ -328,20 +328,20 @@ class LabResultDetails extends StatelessWidget {
}, },
leftLabelFormatter: (value) { leftLabelFormatter: (value) {
value = double.parse(value.toStringAsFixed(1)); value = double.parse(value.toStringAsFixed(1));
// return leftLabels(value.toStringAsFixed(2)); return leftLabels(value.toStringAsFixed(2));
if (value == labmodel.highRefrenceValue) { // if (value == labmodel.highRefrenceValue) {
return leftLabels(LocaleKeys.high.tr()); // return leftLabels(LocaleKeys.high.tr());
} // }
//
if (value == labmodel.lowRefenceValue) { // if (value == labmodel.lowRefenceValue) {
return leftLabels(LocaleKeys.low.tr()); // return leftLabels(LocaleKeys.low.tr());
} // }
return SizedBox.shrink(); return SizedBox.shrink();
// } // }
}, },
graphColor: AppColors.blackColor, graphColor: AppColors.graphGridColor,
graphShadowColor: Colors.transparent, graphShadowColor: AppColors.graphGridColor.withOpacity(.5),
graphGridColor: graphColor.withOpacity(.4), graphGridColor: graphColor.withOpacity(.4),
bottomLabelFormatter: (value, data) { bottomLabelFormatter: (value, data) {
if (data.isEmpty) return SizedBox.shrink(); if (data.isEmpty) return SizedBox.shrink();
@ -373,7 +373,7 @@ class LabResultDetails extends StatelessWidget {
ranges.add(HorizontalRangeAnnotation( ranges.add(HorizontalRangeAnnotation(
y1: model.minY, y1: model.minY,
y2: model.lowRefenceValue, y2: model.lowRefenceValue,
color: AppColors.highAndLow.withOpacity(0.05), color: Colors.transparent,
)); ));
ranges.add(HorizontalRangeAnnotation( ranges.add(HorizontalRangeAnnotation(
@ -385,7 +385,7 @@ class LabResultDetails extends StatelessWidget {
ranges.add(HorizontalRangeAnnotation( ranges.add(HorizontalRangeAnnotation(
y1: model.highRefrenceValue, y1: model.highRefrenceValue,
y2: model.maxY, y2: model.maxY,
color: AppColors.criticalLowAndHigh.withOpacity(0.05), color: Colors.transparent,
)); ));
return ranges; return ranges;
} }
@ -414,15 +414,17 @@ class LabResultDetails extends StatelessWidget {
} }
double? getInterval(LabViewModel labmodel) { double? getInterval(LabViewModel labmodel) {
return .1; // return .1;
// var maxX = labmodel.maxY; var maxX = labmodel.maxY;
// if(maxX<1) return .5; if(maxX<1) return .3;
// if(maxX >1 && maxX < 5) return 1; if(maxX >1 && maxX <= 5) return .7;
// if(maxX >5 && maxX < 10) return 5; if(maxX >5 && maxX <= 10) return 2.5;
// if(maxX >10 && maxX < 50) return 10; if(maxX >10 && maxX <= 50) return 5;
// if(maxX >50 && maxX < 100) return 20; if(maxX >50 && maxX <= 100) return 10;
// if(maxX >100 && maxX < 200) return 30; if(maxX >100 && maxX <= 200) return 30;
// return 50; if(maxX >200 && maxX <= 300) return 50;
if(maxX >300 && maxX <= 400) return 100;
return 200 ;
} }
Widget getLabDescription(BuildContext context) { Widget getLabDescription(BuildContext context) {

@ -13,6 +13,7 @@ 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/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/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/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/invoice_list_card.dart';
import 'package:hmg_patient_app_new/theme/colors.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/appbar/collapsing_list_view.dart';
@ -21,6 +22,8 @@ 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:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../widgets/buttons/custom_button.dart';
class MyInvoicesList extends StatefulWidget { class MyInvoicesList extends StatefulWidget {
const MyInvoicesList({super.key}); const MyInvoicesList({super.key});
@ -40,6 +43,75 @@ class _MyInvoicesListState extends State<MyInvoicesList> {
super.initState(); super.initState();
} }
void _showHospitalFilterBottomSheet(BuildContext context) {
myInvoicesViewModel.prepareFilterList(InvoiceFilterType.hospital);
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.selectHospital.tr(context: context),
context,
child: InvoiceFilterBottomSheet(
title: LocaleKeys.hospital.tr(context: context),
onItemSelected: (selectedHospital) {
Navigator.pop(context);
myInvoicesViewModel.filterInvoicesByHospital(selectedHospital);
},
),
isFullScreen: false,
isCloseButtonVisible: true,
hasBottomPadding: false,
backgroundColor: AppColors.bottomSheetBgColor,
callBackFunc: () {
myInvoicesViewModel.clearFilterSearch();
},
);
}
void _showClinicFilterBottomSheet(BuildContext context) {
myInvoicesViewModel.prepareFilterList(InvoiceFilterType.clinic);
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.selectClinic.tr(context: context),
context,
child: InvoiceFilterBottomSheet(
title: LocaleKeys.clinic.tr(context: context),
onItemSelected: (selectedClinic) {
Navigator.pop(context);
myInvoicesViewModel.filterInvoicesByClinic(selectedClinic);
},
),
isFullScreen: false,
isCloseButtonVisible: true,
hasBottomPadding: false,
backgroundColor: AppColors.bottomSheetBgColor,
callBackFunc: () {
myInvoicesViewModel.clearFilterSearch();
},
);
}
void _showDoctorFilterBottomSheet(BuildContext context) {
myInvoicesViewModel.prepareFilterList(InvoiceFilterType.doctor);
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.selectDoctor.tr(context: context),
context,
child: InvoiceFilterBottomSheet(
title: LocaleKeys.doctor.tr(context: context),
onItemSelected: (selectedDoctor) {
Navigator.pop(context);
myInvoicesViewModel.filterInvoicesByDoctor(selectedDoctor);
},
),
isFullScreen: false,
isCloseButtonVisible: true,
hasBottomPadding: false,
backgroundColor: AppColors.bottomSheetBgColor,
callBackFunc: () {
myInvoicesViewModel.clearFilterSearch();
},
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
myInvoicesViewModel = Provider.of<MyInvoicesViewModel>(context, listen: false); myInvoicesViewModel = Provider.of<MyInvoicesViewModel>(context, listen: false);
@ -50,62 +122,133 @@ class _MyInvoicesListState extends State<MyInvoicesList> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox(height: 24.h), SizedBox(height: 16.h),
Row(
children: [
CustomButton(
text: LocaleKeys.allInvoices.tr(context: context),
onPressed: () {
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),
textColor: myInvoicesVM.currentFilter == InvoiceFilterType.all ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: LocaleKeys.hospitals.tr(context: context),
onPressed: () {
if (myInvoicesVM.allInvoicesList.isEmpty) return;
_showHospitalFilterBottomSheet(context);
},
backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
textColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: LocaleKeys.clinics.tr(context: context),
onPressed: () {
if (myInvoicesVM.allInvoicesList.isEmpty) return;
_showClinicFilterBottomSheet(context);
},
backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
textColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: LocaleKeys.doctors.tr(context: context),
onPressed: () {
if (myInvoicesVM.allInvoicesList.isEmpty) return;
_showDoctorFilterBottomSheet(context);
},
backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
textColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
ListView.builder( ListView.builder(
itemCount: myInvoicesVM.isInvoicesListLoading ? 4 : myInvoicesVM.allInvoicesList.isEmpty ? 1 : myInvoicesVM.allInvoicesList.length, itemCount: myInvoicesVM.isInvoicesListLoading
? 4
: myInvoicesVM.allInvoicesList.isEmpty
? 1
: myInvoicesVM.allInvoicesList.length,
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsetsGeometry.zero, padding: EdgeInsetsGeometry.zero,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return myInvoicesVM.isInvoicesListLoading return myInvoicesVM.isInvoicesListLoading
? LabResultItemView( ? LabResultItemView(onTap: () {}, labOrder: null, index: index, isLoading: true)
onTap: () {}, : myInvoicesVM.allInvoicesList.isNotEmpty
labOrder: null, ? AnimationConfiguration.staggeredList(
index: index, position: index,
isLoading: true, duration: const Duration(milliseconds: 500),
) child: SlideAnimation(
: myInvoicesVM.allInvoicesList.isNotEmpty ? AnimationConfiguration.staggeredList( verticalOffset: 100.0,
position: index, child: FadeInAnimation(
duration: const Duration(milliseconds: 500), child: AnimatedContainer(
child: SlideAnimation( duration: Duration(milliseconds: 300),
verticalOffset: 100.0, curve: Curves.easeInOut,
child: FadeInAnimation( child: InvoiceListCard(
child: AnimatedContainer( getInvoicesListResponseModel: myInvoicesVM.allInvoicesList[index],
duration: Duration(milliseconds: 300), onTap: () async {
curve: Curves.easeInOut, myInvoicesVM.setInvoiceDetailLoading();
child: InvoiceListCard( LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingInvoiceDetails.tr(context: context));
getInvoicesListResponseModel: myInvoicesVM.allInvoicesList[index], await myInvoicesVM.getInvoiceDetails(
onTap: () async { appointmentNo: myInvoicesVM.allInvoicesList[index].appointmentNo!,
myInvoicesVM.setInvoiceDetailLoading(); invoiceNo: myInvoicesVM.allInvoicesList[index].invoiceNo!,
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingInvoiceDetails.tr(context: context)); projectID: myInvoicesVM.allInvoicesList[index].projectID!,
await myInvoicesVM.getInvoiceDetails( onSuccess: (val) {
appointmentNo: myInvoicesVM.allInvoicesList[index].appointmentNo!, LoaderBottomSheet.hideLoader();
invoiceNo: myInvoicesVM.allInvoicesList[index].invoiceNo!, Navigator.of(context).push(
projectID: myInvoicesVM.allInvoicesList[index].projectID!, CustomPageRoute(
onSuccess: (val) { page: MyInvoicesDetailsPage(
LoaderBottomSheet.hideLoader(); getInvoiceDetailsResponseModel: myInvoicesVM.invoiceDetailsResponseModel,
Navigator.of(context).push( getInvoicesListResponseModel: myInvoicesVM.allInvoicesList[index],
CustomPageRoute( ),
page: MyInvoicesDetailsPage(getInvoiceDetailsResponseModel: myInvoicesVM.invoiceDetailsResponseModel, getInvoicesListResponseModel: myInvoicesVM.allInvoicesList[index],), ),
), );
); },
}, onError: (err) {
onError: (err) { LoaderBottomSheet.hideLoader();
LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight(
showCommonBottomSheetWithoutHeight( context,
context, child: Utils.getErrorWidget(loadingText: err),
child: Utils.getErrorWidget(loadingText: err), callBackFunc: () {},
callBackFunc: () {}, isFullScreen: false,
isFullScreen: false, isCloseButtonVisible: true,
isCloseButtonVisible: true, );
); });
}); },
}, ),
),
), ),
), ),
), )
), : Utils.getNoDataWidget(context);
) : Utils.getNoDataWidget(context);
}).paddingSymmetrical(24.w, 0.h), }).paddingSymmetrical(24.w, 0.h),
], ],
); );

@ -0,0 +1,133 @@
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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/debouncer.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/widget_extensions.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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart';
class InvoiceFilterBottomSheet extends StatelessWidget {
final Function(String) onItemSelected;
final String title;
const InvoiceFilterBottomSheet({
super.key,
required this.onItemSelected,
required this.title,
});
@override
Widget build(BuildContext context) {
final debouncer = Debouncer(milliseconds: 500);
return Consumer<MyInvoicesViewModel>(
builder: (context, vm, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextInputWidget(
labelText: LocaleKeys.search.tr(context: context),
hintText: "Search $title",
controller: vm.filterSearchController,
onChange: (value) {
debouncer.run(() {
vm.searchFilterItems(value ?? "");
});
},
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
isAllowLeadingIcon: true,
selectionType: SelectionTypeEnum.search,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
),
),
SizedBox(height: 24.h),
SizedBox(
height: MediaQuery.sizeOf(context).height * 0.4,
child: vm.filterDisplayList.isEmpty
? Center(
child: Text(
"No $title found",
style: TextStyle(
fontSize: 16,
color: AppColors.greyTextColor,
),
),
)
: ListView.separated(
itemBuilder: (_, index) {
final item = vm.filterDisplayList[index];
return FilterListItem(
itemName: item,
).onPress(() {
vm.selectFilterItem(item);
onItemSelected(item);
});
},
separatorBuilder: (_, __) => SizedBox(height: 16.h),
itemCount: vm.filterDisplayList.length,
),
),
],
);
},
);
}
}
class FilterListItem extends StatelessWidget {
final String itemName;
const FilterListItem({super.key, required this.itemName});
AppState get appState => getIt.get<AppState>();
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
itemName,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: AppColors.blackColor,
),
),
),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
).paddingSymmetrical(16.h, 16.h),
);
}
}

@ -257,7 +257,7 @@ class CustomGraph extends StatelessWidget {
isCurved: true, isCurved: true,
isStrokeCapRound: true, isStrokeCapRound: true,
isStrokeJoinRound: true, isStrokeJoinRound: true,
barWidth: 2, barWidth: 3,
gradient: LinearGradient( gradient: LinearGradient(
colors: [resolvedGraphColor, resolvedGraphColor], colors: [resolvedGraphColor, resolvedGraphColor],
begin: Alignment.centerLeft, begin: Alignment.centerLeft,

Loading…
Cancel
Save