From 1a70d6d241522b4d3a197331e7476beeed2e8862 Mon Sep 17 00:00:00 2001 From: taha Date: Sun, 20 Sep 2026 14:58:34 +0300 Subject: [PATCH] lab result view updated for the package groups and special results --- android/settings.gradle | 2 +- lib/config/localized_values.dart | 4 +- .../labs/all_special_lab_result_model.dart | 9 +- lib/core/model/labs/lab_result.dart | 11 + .../labs/patient_lab_special_result.dart | 16 +- .../lab_order/labs_service.dart | 38 ++ lib/core/viewModel/labs_view_model.dart | 1 + .../laboratory_result/lab_result_widget.dart | 399 +++++++-------- .../laboratory_result_widget.dart | 457 +++++++++++------- .../special_lab_result_details_page.dart | 147 ++++-- 10 files changed, 678 insertions(+), 406 deletions(-) diff --git a/android/settings.gradle b/android/settings.gradle index b4401ef1..c6195390 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -18,7 +18,7 @@ pluginManagement { plugins { id 'dev.flutter.flutter-plugin-loader' version "1.0.0" // apply true - id "org.jetbrains.kotlin.android" version "2.0.0" apply false + id "org.jetbrains.kotlin.android" version "2.2.0" apply false id 'com.android.application' version "8.9.0" apply false id "com.google.gms.google-services" version "4.4.0" apply false diff --git a/lib/config/localized_values.dart b/lib/config/localized_values.dart index 9a0baa84..7c2aedd6 100644 --- a/lib/config/localized_values.dart +++ b/lib/config/localized_values.dart @@ -1291,8 +1291,8 @@ const Map> localizedValues = { "ar":"لا يوجد تشخيص سابق" }, "showAttachment":{ - "en":"Show\nAttachment", - "ar":"عرض\nالمرفقات" + "en":"Show Attachment", + "ar":"عرض المرفقات" }, "selectAnAttachment":{ "en":"Select An Attachment", diff --git a/lib/core/model/labs/all_special_lab_result_model.dart b/lib/core/model/labs/all_special_lab_result_model.dart index ad78cd4d..e3315e31 100644 --- a/lib/core/model/labs/all_special_lab_result_model.dart +++ b/lib/core/model/labs/all_special_lab_result_model.dart @@ -58,6 +58,8 @@ class AllSpecialLabResultModel { bool? IsLabTestAttachment; // for the attachment icon in the lab result list dynamic LabAccessionNo; dynamic FileHash; + dynamic testDescription; + dynamic testShortDescription; AllSpecialLabResultModel( {this.actualDoctorRate, @@ -110,7 +112,10 @@ class AllSpecialLabResultModel { //this.speciality, this.status, this.statusDesc, - this.strOrderDate}); + this.strOrderDate, + this.testShortDescription, + this.testDescription, + }); AllSpecialLabResultModel.fromJson(Map json) { actualDoctorRate = json['ActualDoctorRate']; @@ -167,6 +172,8 @@ class AllSpecialLabResultModel { IsLabTestAttachment = json['IsLabTestAttachment'] ?? false; LabAccessionNo = json['LabAccessionNo'] ?? json['AccessionNo']; + testDescription = json['Description'] ; + testShortDescription = json['TestShortDescription']; if (json['FileNameWithExtensionList'] != null) { FileNameWithExtensionList = []; diff --git a/lib/core/model/labs/lab_result.dart b/lib/core/model/labs/lab_result.dart index 75040ccb..3026f870 100644 --- a/lib/core/model/labs/lab_result.dart +++ b/lib/core/model/labs/lab_result.dart @@ -21,10 +21,15 @@ class LabResult { String? verifiedOn; dynamic verifiedOnDateTime; bool? IsLabTestAttachment; // for the attachment icon in the lab result list + bool? IsPackage; // for the attachment icon in the lab result list dynamic LabAccessionNo; List? FileNameWithExtensionList; dynamic FileHash; List? UploadedDocuments; + String? packageName = ""; + String? TestShortDescription = ""; + String? PackageShortDescription = ""; + LabResult( {this.description, @@ -52,6 +57,8 @@ class LabResult { this.LabAccessionNo, this.FileNameWithExtensionList, this.FileHash, + this.TestShortDescription, + this.PackageShortDescription, this.UploadedDocuments}); LabResult.fromJson(Map json) { @@ -77,8 +84,12 @@ class LabResult { verifiedOn = json['VerifiedOn']; verifiedOnDateTime = json['VerifiedOnDateTime']; IsLabTestAttachment = json['IsLabTestAttachment'] ?? false; + IsPackage = json['IsPackage'] ?? false; LabAccessionNo = json['LabAccessionNo'] ?? json['AccessionNo']; + packageName = json['PackageName'] ??null ; + TestShortDescription = json['TestShortDescription'] ??null ; + PackageShortDescription = json['PackageShortDescription'] ??null ; if (json['FileNameWithExtensionList'] != null) { FileNameWithExtensionList = []; diff --git a/lib/core/model/labs/patient_lab_special_result.dart b/lib/core/model/labs/patient_lab_special_result.dart index 809f26b7..5b96a143 100644 --- a/lib/core/model/labs/patient_lab_special_result.dart +++ b/lib/core/model/labs/patient_lab_special_result.dart @@ -12,6 +12,10 @@ class PatientLabSpecialResult { List? UploadedDocuments; bool? IsLabTestAttachment; // for the attachment icon in the lab result list dynamic LabAccessionNo; + String? testCode; + String? testDescription; + String? testID; + String? testShortDescription; PatientLabSpecialResult( {this.invoiceNo, @@ -21,7 +25,12 @@ class PatientLabSpecialResult { this.resultDataTxt, this.FileNameWithExtensionList, this.UploadedDocuments, - this.IsLabTestAttachment}); + this.IsLabTestAttachment, + this.testCode, + this.testDescription, + this.testShortDescription, + this.testID, + }); PatientLabSpecialResult.fromJson(Map json) { SetupID = json['SetupID']; @@ -46,6 +55,11 @@ class PatientLabSpecialResult { } IsLabTestAttachment = json['IsLabTestAttachment'] ?? false; + + testCode = json['TestCode']; + testDescription = json['Description']; + testID = json['TestID']; + testShortDescription = json['TestShortDescription']; } Map toJson() { diff --git a/lib/core/service/patient_medical_file/lab_order/labs_service.dart b/lib/core/service/patient_medical_file/lab_order/labs_service.dart index 62f7174c..451a6bfb 100644 --- a/lib/core/service/patient_medical_file/lab_order/labs_service.dart +++ b/lib/core/service/patient_medical_file/lab_order/labs_service.dart @@ -60,6 +60,7 @@ class LabsService extends BaseService { List patientLabSpecialResult = []; List labResultList = []; + Map> mappedLabResultList = {}; List labOrdersResultsList = []; List labAttachment = []; List labOrdersResultHistoryList = []; @@ -125,11 +126,48 @@ class LabsService extends BaseService { labResultList.add(LabResult.fromJson(lab)); }); } + + ///sorting the list based on the package name or test code right now if the vida3 add the package name for the doctor application endpoint then it will work cause the elements are going to give the priority to the package name if it is not null or empty otherwise it will give the priority to the test code and then it will sort the list based on the package name first then goes for the test code to sort and enjoy + mappedLabResultList = groupLabResultsByPackageName(labResultList); }, onFailure: (String error, int statusCode) { hasError = true; super.error = error; }, body: body); } + Map> groupLabResultsByPackageName(List labResults) { + Map> groupedResults = {}; + + // Group results by package name + for (var labResult in labResults) { + String packageName = labResult.packageName?? labResult.testCode ?? 'Unknown Package'; + + if (groupedResults.containsKey(packageName)) { + // Package name exists, add to existing list + groupedResults[packageName]!.add(labResult); + } else { + // Package name doesn't exist, create new key with new list + groupedResults[packageName] = [labResult]; + } + } + + // Sort each list alphabetically by description + groupedResults.forEach((key, list) { + list.sort((a, b) { + String descA = (a.description ?? '').toLowerCase(); + String descB = (b.description ?? '').toLowerCase(); + return descA.compareTo(descB); + }); + }); + + // Sort the map keys alphabetically and return a new sorted map + var sortedKeys = groupedResults.keys.toList()..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase())); + Map> sortedGroupedResults = {}; + for (var key in sortedKeys) { + sortedGroupedResults[key] = groupedResults[key]!; + } + + return sortedGroupedResults; + } Future getPatientLabOrdersResults({PatientLabOrders? patientLabOrder, required String procedure, required PatiantInformtion patient}) async { hasError = false; diff --git a/lib/core/viewModel/labs_view_model.dart b/lib/core/viewModel/labs_view_model.dart index 13c70d5e..6bd856bd 100644 --- a/lib/core/viewModel/labs_view_model.dart +++ b/lib/core/viewModel/labs_view_model.dart @@ -74,6 +74,7 @@ class LabsViewModel extends BaseViewModel { List get patientLabSpecialResult => _labsService.patientLabSpecialResult; List get labResultList => _labsService.labResultList; + Map> get mappedLabResultList => _labsService.mappedLabResultList; List labResultLists = []; diff --git a/lib/screens/patients/profile/lab_result/laboratory_result/lab_result_widget.dart b/lib/screens/patients/profile/lab_result/laboratory_result/lab_result_widget.dart index b22be730..b590c8a5 100644 --- a/lib/screens/patients/profile/lab_result/laboratory_result/lab_result_widget.dart +++ b/lib/screens/patients/profile/lab_result/laboratory_result/lab_result_widget.dart @@ -6,12 +6,10 @@ import 'package:doctor_app_flutter/core/viewModel/project_view_model.dart'; import 'package:doctor_app_flutter/core/model/patient/patiant_info_model.dart'; import 'package:doctor_app_flutter/screens/patients/profile/lab_result/laboratory_result/flow_chart_page.dart'; import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart'; -import 'package:doctor_app_flutter/utils/utils.dart'; import 'package:doctor_app_flutter/widgets/shared/app_texts_widget.dart'; import 'package:doctor_app_flutter/widgets/transitions/fade_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:open_filex/open_filex.dart'; import 'package:provider/provider.dart'; class LabResultWidget extends StatelessWidget { @@ -24,15 +22,16 @@ class LabResultWidget extends StatelessWidget { LabResultWidget({Key? key, required this.filterName, required this.patientLabResultList, required this.patientLabOrder, required this.patient, required this.isInpatient, required this.model}) : super(key: key); - late ProjectViewModel projectViewModel; @override Widget build(BuildContext context) { - projectViewModel = Provider.of(context); + Provider.of(context); return Container( width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 8), child: Column( + mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // if (!isInpatient) @@ -41,234 +40,246 @@ class LabResultWidget extends StatelessWidget { children: [ Row( children: [ - AppText( - filterName, - fontSize: SizeConfig.textMultiplier! * 2.0, - fontWeight: FontWeight.w700, - ), - ], - ), - ], - ), - SizedBox( - height: 8, - ), - Row( - children: [ - Expanded( - child: Container( - child: Center( + Visibility( + visible: filterName.isNotEmpty, child: AppText( - TranslationBase.of(context).description, - color: Colors.black, - fontSize: SizeConfig.textMultiplier! * 2.0, + filterName, + fontSize: SizeConfig.textMultiplier! * 1.6, fontWeight: FontWeight.w700, ), ), - ), - ), - Expanded( - child: Container( - child: Center( - child: AppText( - TranslationBase.of(context).value, - color: Colors.black, - fontSize: SizeConfig.textMultiplier! * 2.0, - fontWeight: FontWeight.w700, - ), - ), - ), - ), - Expanded( - child: Container( - child: Center( - child: AppText( - TranslationBase.of(context).range, - fontSize: SizeConfig.textMultiplier! * 2.0, - fontWeight: FontWeight.w700, - ), - ), - ), + ], ), - Expanded( - child: Container(), - ) ], ), - Divider( - color: Colors.grey.shade500, - thickness: 1, - ), SizedBox( - height: 12, + height: 16, ), ListView.builder( itemCount: patientLabResultList.length, scrollDirection: Axis.vertical, - physics: ScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemBuilder: (context, index) { return Column( + mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - decoration: BoxDecoration( - shape: BoxShape.rectangle, - color: Color(0X55F2F2F2), - borderRadius: BorderRadius.all( - Radius.circular(8.0), - ), - ), - child: Row( + SizedBox(height: 8,), + Visibility( + visible: index == 0, + child: Column( children: [ - Expanded( - child: Container( - padding: EdgeInsets.all(10), - child: Center( - child: AppText( - patientLabResultList[index].description!, - textAlign: TextAlign.center, - fontSize: SizeConfig.textMultiplier! * 1.8, - isCopyable: true, - ), - ), - ), - ), - Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (patientLabResultList[index].checkResultStatus() != 0) - Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.red.shade700, - ), - child: Icon( - patientLabResultList[index].checkResultStatus() == 1 ? Icons.arrow_upward : Icons.arrow_downward, - color: Colors.white, - size: 16, - ), + Visibility( + visible: patientLabResultList[index].PackageShortDescription != null && patientLabResultList[index].PackageShortDescription?.isNotEmpty == true , + child:Column( + children: [ + AppText( + patientLabResultList[index].PackageShortDescription??"", + fontSize: SizeConfig.textMultiplier! * 1.3, + fontWeight: FontWeight.w400, + color: Colors.black87, + isCopyable: true, ), - Expanded( - child: Container( - alignment: Alignment.center, - padding: EdgeInsets.symmetric(vertical: 10, horizontal: 4), - child: Center( - child: AppText( - patientLabResultList[index].resultValue ?? "" + " " + "${patientLabResultList[index].uOM ?? ""}", - textAlign: TextAlign.center, - isCopyable: true, - fontSize: SizeConfig.textMultiplier! * 1.8, - ), - ), + SizedBox( + height: 8, ), - ), - ], - ), - ), - Expanded( - child: Container( - padding: EdgeInsets.all(10), - // color: Colors.white, - child: Center( - child: AppText( - patientLabResultList[index].referanceRange!, - textAlign: TextAlign.center, - fontSize: SizeConfig.textMultiplier! * 1.8, - isCopyable: true, - ), - ), - ), - ), - Expanded( - child: Column( - children: [ - Visibility( - visible: patientLabResultList[index].IsLabTestAttachment ?? false, - child: InkWell( - onTap: () async { - var labAccessNumber = patientLabResultList[index].LabAccessionNo; - // if (patientLabResultList[index].FileNameWithExtensionList?.isNotEmpty == true || patientLabResultList[index].FileNameWithExtensionList?.length == 1) { - // await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, FileName: patientLabResultList[index].FileNameWithExtensionList?.first); - // model.openPDF(); - // return; - // } - // - // if (patientLabResultList[index].UploadedDocuments?.isNotEmpty == true || patientLabResultList[index].UploadedDocuments?.length == 1) { - // await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, uploadedDocument: patientLabResultList[index].UploadedDocuments?.first); - // model.openPDF(); - // return; - // } - showUploadedDocumentsBottomSheet( - context, - documents: patientLabResultList[index].UploadedDocuments, - fileName: patientLabResultList[index].FileNameWithExtensionList, - labAccessNumber: labAccessNumber, - projectID: int.parse(patientLabResultList[index].projectID!), - setupID: patientLabResultList[index].setupID); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AppText( - TranslationBase.of(context).showAttachment, - textDecoration: TextDecoration.underline, - color: Colors.red.shade700, - fontSize: SizeConfig.textMultiplier! * 1.2, - fontWeight: FontWeight.w700, - ), - ], - ), + ], + ) ), + Visibility( + visible: (patientLabResultList.any((lab) => lab.IsLabTestAttachment ?? false)) && (patientLabResultList.any((lab) => lab.IsPackage ?? false)), + child: Center( + child: InkWell( + onTap: () async { + final labWithAttachment = patientLabResultList[index]; + var labAccessNumber = labWithAttachment.LabAccessionNo; + + showUploadedDocumentsBottomSheet( + context, + documents: labWithAttachment.UploadedDocuments, + fileName: labWithAttachment.FileNameWithExtensionList, + labAccessNumber: labAccessNumber, + projectID: int.parse(labWithAttachment.projectID!), + setupID: labWithAttachment.setupID, + ); + }, + child: Container( + width: double.infinity, + + padding: EdgeInsets.symmetric(vertical: 14, horizontal: 24), + decoration: BoxDecoration( + color: Colors.red.shade50, + borderRadius: BorderRadius.circular(16), ), - ), - SizedBox( - height: 8, - ), - InkWell( - onTap: () { - Navigator.push( - context, - FadePage( - page: FlowChartPage( - filterName: patientLabResultList[index].description!, - patientLabOrder: patientLabOrder, - patient: patient, - isInpatient: isInpatient, - ), - ), - ); - }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ + Icon( + Icons.description_outlined, + color: Colors.red.shade700, + size: 20, + ), + SizedBox(width: 8), AppText( - TranslationBase.of(context).showMoreBtn, - textDecoration: TextDecoration.underline, + TranslationBase.of(context).showAttachment, color: Colors.red.shade700, fontSize: SizeConfig.textMultiplier! * 1.4, - fontWeight: FontWeight.w700, ), ], ), ), - ], + ), ), ), + SizedBox(height: 8,) ], ), ), + + // Label (e.g. "Diagnosis:") - bold + underlined, like the screenshot + Row( + children: [ + Expanded( + child: AppText( + patientLabResultList[index].description!, + fontSize: SizeConfig.textMultiplier! * 1.9, + + color: Colors.black, + ), + ), + SizedBox( + width: 8, + ), + // Value - plain text below the label + AppText( + (patientLabResultList[index].resultValue ?? "") + (patientLabResultList[index].uOM != null ? " ${patientLabResultList[index].uOM}" : ""), + fontSize: SizeConfig.textMultiplier! * 1.8, + fontWeight: FontWeight.w400, + color: Colors.black87, + isCopyable: true, + ), + ], + ), + + Visibility( + visible: patientLabResultList[index].TestShortDescription != null , + child:Column( + children: [ + SizedBox( + height: 20, + ), + AppText( + patientLabResultList[index].TestShortDescription??"", + fontSize: SizeConfig.textMultiplier! * 1.3, + fontWeight: FontWeight.w400, + color: Colors.black87, + isCopyable: true, + ), + SizedBox( + height: 8, + ), + ], + ) ), SizedBox( height: 16, ), + Center( + child: InkWell( + onTap: () { + Navigator.push( + context, + FadePage( + page: FlowChartPage( + filterName: patientLabResultList.first.description!, + patientLabOrder: patientLabOrder, + patient: patient, + isInpatient: isInpatient, + ), + ), + ); + }, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 14, horizontal: 24), + decoration: BoxDecoration( + color: Colors.red.shade50, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.show_chart, + color: Colors.red.shade700, + size: 20, + ), + SizedBox(width: 8), + AppText( + TranslationBase.of(context).showMoreBtn, + color: Colors.red.shade700, + fontSize: SizeConfig.textMultiplier! * 1.4, + ), + ], + ), + ), + ), + ), + SizedBox( + height: 16, + ), + Visibility( + visible: (patientLabResultList[index].IsLabTestAttachment== true ) && (patientLabResultList[index].IsPackage == false), + child: Center( + child: InkWell( + onTap: () async { + final labWithAttachment = patientLabResultList.firstWhere( + (lab) => lab.IsLabTestAttachment ?? false, + ); + var labAccessNumber = labWithAttachment.LabAccessionNo; + + showUploadedDocumentsBottomSheet( + context, + documents: labWithAttachment.UploadedDocuments, + fileName: labWithAttachment.FileNameWithExtensionList, + labAccessNumber: labAccessNumber, + projectID: int.parse(labWithAttachment.projectID!), + setupID: labWithAttachment.setupID, + ); + }, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 14, horizontal: 24), + decoration: BoxDecoration( + color: Colors.red.shade50, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.description_outlined, + color: Colors.red.shade700, + size: 20, + ), + SizedBox(width: 8), + AppText( + TranslationBase.of(context).showAttachment, + color: Colors.red.shade700, + fontSize: SizeConfig.textMultiplier! * 1.4, + ), + ], + ), + ), + ), + ), + ), ], ); }), SizedBox( height: 8, ), - Divider(), ], ), ); @@ -357,13 +368,13 @@ class LabResultWidget extends StatelessWidget { } void showUploadedDocumentsBottomSheet( - BuildContext context, { - List? documents, - List? fileName, - String? labAccessNumber, - int? projectID, - String? setupID, - }) { + BuildContext context, { + List? documents, + List? fileName, + String? labAccessNumber, + int? projectID, + String? setupID, + }) { const double kItemHeight = 66.0; // ListTile with contentPadding: 10 final double screenHeight = MediaQuery.of(context).size.height; final double maxSheetHeight = screenHeight * 0.5; @@ -422,7 +433,7 @@ class LabResultWidget extends StatelessWidget { child: const Divider(height: 1, color: Colors.grey), ), itemBuilder: (context, index) { - final docName = documents?[index]?.fileName ?? fileName?[index]; + final docName = documents?[index].fileName ?? fileName?[index]; return ListTile( contentPadding: const EdgeInsets.all(10), diff --git a/lib/screens/patients/profile/lab_result/laboratory_result/laboratory_result_widget.dart b/lib/screens/patients/profile/lab_result/laboratory_result/laboratory_result_widget.dart index c5643fcc..7b09b13f 100644 --- a/lib/screens/patients/profile/lab_result/laboratory_result/laboratory_result_widget.dart +++ b/lib/screens/patients/profile/lab_result/laboratory_result/laboratory_result_widget.dart @@ -35,6 +35,7 @@ class LaboratoryResultWidget extends StatefulWidget { class _LaboratoryResultWidgetState extends State { bool _isShowMoreGeneral = true; bool _isShowMore = true; + int expandedIndex = 0; late ProjectViewModel projectViewModel; @override @@ -51,15 +52,23 @@ class _LaboratoryResultWidgetState extends State { baseViewModel: model, child: SafeArea( child: Container( - margin: EdgeInsets.all(15), + margin: EdgeInsets.all(4), child: Column( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (model.labResultLists.isNotEmpty) + if (model.mappedLabResultList.isNotEmpty) Container( + decoration: BoxDecoration( + shape: BoxShape.rectangle, + color: Colors.white, + borderRadius: BorderRadius.all( + Radius.circular(16.0), + ), + ), + child: Column( children: [ InkWell( @@ -73,13 +82,13 @@ class _LaboratoryResultWidgetState extends State { child: Container( padding: EdgeInsets.all(10.0), margin: EdgeInsets.only(left: 5, right: 5), - decoration: BoxDecoration( - shape: BoxShape.rectangle, - color: Colors.white, - borderRadius: BorderRadius.all( - Radius.circular(5.0), - ), - ), + // decoration: BoxDecoration( + // shape: BoxShape.rectangle, + // color: Colors.white, + // borderRadius: BorderRadius.all( + // Radius.circular(5.0), + // ), + // ), child: Row( children: [ Expanded( @@ -107,33 +116,34 @@ class _LaboratoryResultWidgetState extends State { AnimatedContainer( padding: EdgeInsets.all(10.0), margin: EdgeInsets.only(left: 5, right: 5), - decoration: BoxDecoration( - shape: BoxShape.rectangle, - color: Colors.white, - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(5.0), - bottomRight: Radius.circular(5.0), - ), - ), + // decoration: BoxDecoration( + // shape: BoxShape.rectangle, + // color: Colors.white, + // borderRadius: BorderRadius.only( + // bottomLeft: Radius.circular(5.0), + // bottomRight: Radius.circular(5.0), + // ), + // ), duration: Duration(milliseconds: 7000), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - ListView.builder( - itemCount: model.labResultLists.length, - scrollDirection: Axis.vertical, - physics: ScrollPhysics(), - shrinkWrap: true, - itemBuilder: (context, index) { - return LabResultWidget( - patientLabOrder: widget.patientLabOrder!, - filterName: model.labResultLists[index].filterName, - patientLabResultList: model.labResultLists[index].patientLabResultList, - patient: widget.patient!, - isInpatient: widget.isInpatient!, - model: model, - ); - }), + // ListView.builder( + // itemCount: model.mappedLabResultList.length, + // scrollDirection: Axis.vertical, + // physics: ScrollPhysics(), + // shrinkWrap: true, + // itemBuilder: (context, index) { + // return LabResultWidget( + // patientLabOrder: widget.patientLabOrder!, + // filterName: model.labResultLists[index].filterName, + // patientLabResultList: model.labResultLists[index].patientLabResultList, + // patient: widget.patient!, + // isInpatient: widget.isInpatient!, + // model: model, + // ); + // }), + mappedListDisplay(model) ], ), ), @@ -150,139 +160,85 @@ class _LaboratoryResultWidgetState extends State { height: 15, ), if (widget.details?.isNotEmpty == true) - Column( - children: [ - InkWell( - onTap: () { - setState(() { - _isShowMore = !_isShowMore; - }); - }, - child: Container( - padding: EdgeInsets.all(10.0), - margin: EdgeInsets.only(left: 5, right: 5), - decoration: BoxDecoration( - shape: BoxShape.rectangle, - color: Colors.white, - borderRadius: BorderRadius.all( - Radius.circular(5.0), - )), - child: Row( - children: [ - Expanded( - child: Container( - margin: EdgeInsets.only(left: 0, right: 0), - child: AppText( - TranslationBase.of(context).specialResult, - fontSize: SizeConfig.textMultiplier! * 2.3, - bold: false, - ))), - Container( - width: 25, - height: 25, - child: Icon( - _isShowMore ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, - color: Colors.grey[800], - size: 22, - ), - ) - ], + Container( + decoration: BoxDecoration( + shape: BoxShape.rectangle, + color: Colors.white, + borderRadius: BorderRadius.all( + Radius.circular(16.0), + )), + child: Column( + children: [ + InkWell( + onTap: () { + setState(() { + _isShowMore = !_isShowMore; + }); + }, + child: Container( + padding: EdgeInsets.all(10.0), + margin: EdgeInsets.only(left: 5, right: 5), + child: Row( + children: [ + Expanded( + child: Container( + margin: EdgeInsets.only(left: 0, right: 0), + child: AppText( + TranslationBase.of(context).specialResult, + fontSize: SizeConfig.textMultiplier! * 2.3, + bold: false, + ))), + Container( + width: 25, + height: 25, + child: Icon( + _isShowMore ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, + color: Colors.grey[800], + size: 22, + ), + ) + ], + ), ), ), - ), - if (_isShowMore) - AnimatedContainer( - padding: EdgeInsets.all(10.0), - margin: EdgeInsets.only(left: 5, right: 5), - decoration: BoxDecoration( - shape: BoxShape.rectangle, - color: Colors.white, - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(5.0), - bottomRight: Radius.circular(5.0), - )), - duration: Duration(milliseconds: 7000), - child: ListView.separated( - separatorBuilder: (context, index) => Divider( - color: Colors.grey[300], - thickness: 1, - ), - itemCount: widget.details?.length ?? 0, - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - String? resultData; - try { - resultData = (widget.details?[index])?.resultDataHTML?.toString(); - } catch (_) { - resultData = null; - } - final displayData = (resultData == null || resultData.trim().isEmpty) - ? TranslationBase.of(context).noDataAvailable - : resultData; - - return Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - !Utils.isTextHtml(displayData) - ? AppText(displayData) - : Html(data: displayData), - ], - ), - ), - Visibility( - visible: widget.details?[index].IsLabTestAttachment ?? false, - child: InkWell( - onTap: () async { - var labAccessNumber = widget.details?[index].LabAccessionNo; - // if (widget.details?[index].FileNameWithExtensionList?.isNotEmpty == true || widget.details?[index].FileNameWithExtensionList?.length == 1) { - // await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, FileName: widget.details?[index].FileNameWithExtensionList?.first); - // model.openPDF(); - // return; - // } - // - // if (widget.details?[index].UploadedDocuments?.isNotEmpty == true || widget.details?[index].UploadedDocuments?.length == 1) { - // await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, uploadedDocument: widget.details?[index].UploadedDocuments?.first); - // model.openPDF(); - // return; - // } + if (_isShowMore) + AnimatedContainer( + padding: EdgeInsets.all(10.0), + margin: EdgeInsets.only(left: 5, right: 5), + // decoration: BoxDecoration( + // shape: BoxShape.rectangle, + // color: Colors.white, + // borderRadius: BorderRadius.only( + // bottomLeft: Radius.circular(5.0), + // bottomRight: Radius.circular(5.0), + // )), + duration: Duration(milliseconds: 7000), + child: ListView.separated( + separatorBuilder: (context, index) => Divider( + color: Colors.grey[300], + thickness: 1, + ), + itemCount: widget.details?.length ?? 0, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + String? resultData; + try { + resultData = (widget.details?[index])?.resultDataHTML?.toString(); + } catch (_) { + resultData = null; + } + final displayData = (resultData == null || resultData.trim().isEmpty) + ? TranslationBase.of(context).noDataAvailable + : resultData.trim(); - showUploadedDocumentsBottomSheet( - context, - model, - documents: widget.details?[index].UploadedDocuments, - fileName: widget.details?[index].FileNameWithExtensionList, - labAccessNumber: labAccessNumber, - projectID: int.parse(widget.details?[index].ProjectID??"0"), - setupID: widget.details?[index].SetupID); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AppText( - TranslationBase.of(context).showAttachment, - textDecoration: TextDecoration.underline, - color: Colors.red.shade700, - fontSize: SizeConfig.textMultiplier! * 1.2, - fontWeight: FontWeight.w700, - ), - ], - ), - ), - ), - - ], - ); - }, + return buildLabDetailItem(context, index, displayData, model); + }, + ), ), - ), - SizedBox( - height: 10, - ), - ], + + ], + ), ), ], ), @@ -294,6 +250,83 @@ class _LaboratoryResultWidgetState extends State { ), ); } + Widget buildLabDetailItem(BuildContext context, int index, String displayData, LabsViewModel model) { + return Card( + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + ), + elevation: 8, + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + !Utils.isTextHtml(displayData) + ? AppText(displayData) + : Html(data: displayData), + Visibility( + visible: widget.details?[index].IsLabTestAttachment ?? false, + child: Column( + children: [ + SizedBox(height: 8,), + InkWell( + onTap: () async { + var labAccessNumber = widget.details?[index].LabAccessionNo; + // if (widget.details?[index].FileNameWithExtensionList?.isNotEmpty == true || widget.details?[index].FileNameWithExtensionList?.length == 1) { + // await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, FileName: widget.details?[index].FileNameWithExtensionList?.first); + // model.openPDF(); + // return; + // } + // + // if (widget.details?[index].UploadedDocuments?.isNotEmpty == true || widget.details?[index].UploadedDocuments?.length == 1) { + // await model.getLabAttachments(patient, labAccessNumber: labAccessNumber, uploadedDocument: widget.details?[index].UploadedDocuments?.first); + // model.openPDF(); + // return; + // } + + showUploadedDocumentsBottomSheet( + context, + model, + documents: widget.details?[index].UploadedDocuments, + fileName: widget.details?[index].FileNameWithExtensionList, + labAccessNumber: labAccessNumber, + projectID: int.parse(widget.details?[index].ProjectID ?? "0"), + setupID: widget.details?[index].SetupID); + }, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 14, horizontal: 24), + decoration: BoxDecoration( + color: Colors.red.shade50, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.description_outlined, + color: Colors.red.shade700, + size: 20, + ), + SizedBox(width: 8), + AppText( + TranslationBase.of(context).showAttachment, + color: Colors.red.shade700, + fontSize: SizeConfig.textMultiplier! * 1.4, + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ); + } void showUploadedDocumentsBottomSheet( BuildContext context, LabsViewModel model, { @@ -361,7 +394,7 @@ class _LaboratoryResultWidgetState extends State { child: const Divider(height: 1, color: Colors.grey), ), itemBuilder: (context, index) { - final docName = documents?[index]?.fileName ?? fileName?[index]; + final docName = documents?[index].fileName ?? fileName?[index]; return ListTile( contentPadding: const EdgeInsets.all(10), @@ -403,4 +436,104 @@ class _LaboratoryResultWidgetState extends State { }, ); } + + mappedListDisplay(LabsViewModel model) { + final packageEntries = model.mappedLabResultList.entries.toList(); + return ListView.separated( + separatorBuilder: (_, __) => Padding(padding: EdgeInsets.all(4), + child: Divider( + height: 2, + ) ,), + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: packageEntries.length, + itemBuilder: (context, packageIndex) { + final packageName = packageEntries[packageIndex].key; + final labResults = packageEntries[packageIndex].value; + final isExpanded = expandedIndex == packageIndex; + + return InkWell( + onTap: () { + setState(() { + if (isExpanded) { + expandedIndex = -1; // Collapse if already expanded + } else + expandedIndex = packageIndex; + }); + }, + child: Card( + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + ), + elevation: 2, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Container( + margin: EdgeInsets.only(left: 0, right: 0), + child: AppText( + packageName, + fontSize: SizeConfig.textMultiplier! * 1.8, + bold: false, + ))), + // Arrow rotates smoothly between down/up instead of snapping + AnimatedRotation( + turns: isExpanded ? 0.5 : 0.0, + duration: const Duration(milliseconds: 250), + curve: Curves.easeInOut, + child: Container( + width: 25, + height: 25, + child: Icon( + Icons.keyboard_arrow_down, + color: Colors.grey[800], + size: 22, + ), + ), + ) + ], + ), + + + // Animated expand/collapse of the lab result content + AnimatedSize( + duration: const Duration(milliseconds: 250), + curve: Curves.easeInOut, + alignment: Alignment.topCenter, + child: AnimatedCrossFade( + duration: const Duration(milliseconds: 250), + sizeCurve: Curves.easeInOut, + firstCurve: Curves.easeIn, + secondCurve: Curves.easeIn, + crossFadeState: isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, + firstChild: const SizedBox(width: double.infinity, height: 0), + secondChild: Column( + mainAxisSize: MainAxisSize.min, + children: [ + LabResultWidget( + patientLabOrder: widget.patientLabOrder!, + filterName: "", + patientLabResultList: labResults, + patient: widget.patient!, + isInpatient: widget.isInpatient!, + model: model, + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + }, + ); + } } diff --git a/lib/screens/patients/profile/lab_result/special_result/special_lab_result_details_page.dart b/lib/screens/patients/profile/lab_result/special_result/special_lab_result_details_page.dart index 45ecd2b8..62b84024 100644 --- a/lib/screens/patients/profile/lab_result/special_result/special_lab_result_details_page.dart +++ b/lib/screens/patients/profile/lab_result/special_result/special_lab_result_details_page.dart @@ -3,6 +3,7 @@ import 'package:doctor_app_flutter/core/viewModel/PatientMedicalReportViewModel. import 'package:doctor_app_flutter/core/model/patient/patiant_info_model.dart'; import 'package:doctor_app_flutter/core/viewModel/labs_view_model.dart'; import 'package:doctor_app_flutter/screens/base/base_view.dart'; +import 'package:doctor_app_flutter/utils/extenstions_utils.dart'; import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart'; import 'package:doctor_app_flutter/widgets/patients/profile/app_bar/patient-profile-app-bar.dart'; import 'package:doctor_app_flutter/widgets/patients/patient_service_title.dart'; @@ -52,29 +53,68 @@ class SpecialLabResultDetailsPage extends StatelessWidget { child: Column( children: [ (resultData != null && resultData.isNotEmpty) - ? Container( - width: double.infinity, - margin: EdgeInsets.symmetric(horizontal: 16, vertical: 16), - padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16), - decoration: BoxDecoration( - color: Colors.white, - shape: BoxShape.rectangle, - borderRadius: BorderRadius.all(Radius.circular(8)), - border: Border.fromBorderSide( - BorderSide( - color: Colors.white, - width: 1.0, - ), - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Html(data: resultData ?? ""), - HtmlWidget(resultData ?? ""), - Visibility( - visible: labResult.IsLabTestAttachment ?? false, - child: InkWell( + ? Card( + color: Colors.white, + margin: EdgeInsets.symmetric(horizontal: 16, vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + ), + elevation: 4, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + Visibility( + visible: labResult.testDescription != null && (labResult.testDescription as String).isNotEmpty , + child:Column( + children: [ + SizedBox( + height: 8, + ), + AppText( + labResult.testDescription ??"", + fontSize: SizeConfig.textMultiplier! * 1.6, + fontWeight: FontWeight.w700, + color: Colors.black87, + isCopyable: true, + ), + SizedBox( + height: 8, + ), + ], + ) ), + Visibility( + visible: labResult.testShortDescription != null && (labResult.testShortDescription as String).isNotEmpty , + child:Column( + children: [ + SizedBox( + height: 8, + ), + AppText( + labResult.testShortDescription ??"", + fontSize: SizeConfig.textMultiplier! * 1.3, + fontWeight: FontWeight.w400, + color: Colors.black87, + isCopyable: true, + ), + SizedBox( + height: 8, + ), + ], + ) ), + // Html(data: resultData ?? ""), + HtmlWidget(resultData ?? ""), + + Visibility( + visible: labResult.IsLabTestAttachment ?? false, + child: Column( + children: [ + SizedBox( + height: 12, + ), + InkWell( onTap: () async { var labAccessNumber = labResult.LabAccessionNo; // if (patientLabResultList[index].FileNameWithExtensionList?.isNotEmpty == true || patientLabResultList[index].FileNameWithExtensionList?.length == 1) { @@ -89,32 +129,49 @@ class SpecialLabResultDetailsPage extends StatelessWidget { // return; // } - showUploadedDocumentsBottomSheet(context, - documents: labResult.UploadedDocuments, - fileName: labResult.FileNameWithExtensionList, - labAccessNumber: labAccessNumber, - projectID: int.parse(labResult.projectID!), - // projectID: 160, - setupID: labResult.setupID, - model: model); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AppText( - TranslationBase - .of(context) - .showAttachment, - textDecoration: TextDecoration.underline, - color: Colors.red.shade700, - fontSize: SizeConfig.textMultiplier! * 1.2, - fontWeight: FontWeight.w700, + showUploadedDocumentsBottomSheet( + context, + documents: labResult.UploadedDocuments, + fileName: labResult.FileNameWithExtensionList, + labAccessNumber: labAccessNumber, + projectID: int.parse(labResult.projectID!), + // projectID: 160, + setupID: labResult.setupID, + model: model, + ); + }, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 14, horizontal: 16), + decoration: BoxDecoration( + color: Colors.red.shade50, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.description_outlined, + color: Colors.red.shade700, + size: 18, + ), + SizedBox(width: 6), + AppText( + TranslationBase.of(context).showAttachment, + color: Colors.red.shade700, + fontSize: SizeConfig.textMultiplier! * 1.4, + fontWeight: FontWeight.w400, + ), + ], + ), + ), ), ], ), ), - ), - ], + ], + ), ), ) : Container(