diff --git a/lib/core/service/medical/labs_service.dart b/lib/core/service/medical/labs_service.dart index b206ec11..b7106f46 100644 --- a/lib/core/service/medical/labs_service.dart +++ b/lib/core/service/medical/labs_service.dart @@ -721,27 +721,35 @@ class LabsService extends BaseService { List getThresholdValue() { return [ ThresholdRange( - label: 'criticalLow', + label: 'LCL', + value: 0, + color: Color(0xffffffff), + lineColor: Color(0xFFe9a2a4)), ThresholdRange( + label: 'CL', value: 0, color: Color(0xffffffff), lineColor: Color(0xFFe9a2a4)), ThresholdRange( - label: 'low', + label: 'L', value: 20, color: Color(0xFFf2fbf5), lineColor: Color(0xFFeecd94)), ThresholdRange( - label: 'normal', + label: 'N', value: 40, color: Color(0xFFf2fbf5), lineColor: Color(0xFF5dc36b)), ThresholdRange( - label: 'high', + label: 'H', value: 60, color: Color(0xffffffff), lineColor: Color(0xFFeecd94)), ThresholdRange( - label: 'criticalHigh', + label: 'CH', + value: 80, + color: Color(0xffffffff), + lineColor: Color(0xFFe9a2a4)), ThresholdRange( + label: 'HCH', value: 80, color: Color(0xffffffff), lineColor: Color(0xFFe9a2a4)), diff --git a/lib/core/viewModels/medical/labs_view_model.dart b/lib/core/viewModels/medical/labs_view_model.dart index 7f9faccb..f6744491 100644 --- a/lib/core/viewModels/medical/labs_view_model.dart +++ b/lib/core/viewModels/medical/labs_view_model.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'dart:ui'; +import 'package:flutter/cupertino.dart'; import 'package:hmg_patient_app/core/enum/filter_type.dart'; import 'package:hmg_patient_app/core/enum/viewstate.dart'; import 'package:hmg_patient_app/core/model/labs/LabOrderResult.dart'; @@ -29,6 +30,8 @@ class LabsViewModel extends BaseViewModel { List threePointGraphValue = []; List completeeGraphValues = []; List threshold = []; + double maxYForThreeDots = 0.0; + double maxYForCompleteGraph = 0.0; List months = ['Jan','Feb','Mar','April','May','Jun','July','Aug','Sep','Oct','Nov','Dec']; @@ -164,6 +167,7 @@ class LabsViewModel extends BaseViewModel { } getPatientLabOrdersResults({PatientLabOrders? patientLabOrder, String? procedure, bool? isVidaPlus}) async { + maxYForThreeDots = 0.0; setState(ViewState.Busy); await _labsService.getPatientLabOrdersResults( patientLabOrder: patientLabOrder, @@ -202,11 +206,19 @@ class LabsViewModel extends BaseViewModel { recentThree.reversed.forEach((element) { try { var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); + // debugPrint("the double.parse(element.resultValue!) is ${double.parse(element.resultValue!)}"); + if(double.parse(element.resultValue!)> maxYForThreeDots){ + debugPrint("the double.parse(element.resultValue!) is ${double.parse(element.resultValue!)}"); + + maxYForThreeDots = double.parse(element.resultValue!); + } // threePointGraphValue.add(DataPoint( labelValue: counter,value : _labsService.transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""), label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime)); - threePointGraphValue.add(DataPoint( labelValue: counter,value : _labsService.transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""), actualValue: element.resultValue!,label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime)); + threePointGraphValue.add(DataPoint( labelValue: counter,value : double.parse(element.resultValue!), actualValue: element.resultValue!,label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime, referenceRangeValue:element.calculatedResultFlag ??"IRR")); counter++; } catch (e) {} }); + debugPrint("the maxY is ${maxYForThreeDots}"); + threePointGraphValue.last.label = "thisResult"; threePointGraphValue.last.isStringResource = true; setState(ViewState.Idle); @@ -218,6 +230,7 @@ class LabsViewModel extends BaseViewModel { createFullGraphDatPoints( VoidCallback onComplete ){ + maxYForCompleteGraph = 0.0; completeeGraphValues.clear(); setState(ViewState.Busy); double counter = 1; @@ -226,8 +239,11 @@ class LabsViewModel extends BaseViewModel { try { print("the mapping is being done"); var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); + if(double.parse(element.resultValue!)> maxYForCompleteGraph){ + maxYForCompleteGraph = double.parse(element.resultValue!); + } // completeeGraphValues.add(DataPoint( labelValue: counter,value : _labsService.transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""), label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime)); - completeeGraphValues.add(DataPoint( labelValue: counter,value : _labsService.transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??""), label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime,actualValue: element.resultValue!)); + completeeGraphValues.add(DataPoint( labelValue: counter,value : double.parse(element.resultValue!), label: "${months[dateTime.month-1]} ${dateTime.year}", date: dateTime,actualValue: element.resultValue!, referenceRangeValue:element.calculatedResultFlag??"IRR" )); } catch (e) { print("the mapping is having exception $e"); diff --git a/lib/widgets/data_display/medical/LabResult/FlowChartPage.dart b/lib/widgets/data_display/medical/LabResult/FlowChartPage.dart index 7aa63e29..bd3757c7 100644 --- a/lib/widgets/data_display/medical/LabResult/FlowChartPage.dart +++ b/lib/widgets/data_display/medical/LabResult/FlowChartPage.dart @@ -36,7 +36,9 @@ class FlowChartPage extends StatelessWidget { procedure: filterName, isVidaPlus: Utils.isVidaPlusProject( projectViewModel!, int.parse(patientLabOrder!.projectID!))), - builder: (context, model, w) => SizedBox( + builder: (context, model, w) { + print("the maxY in builder is ${model.maxYForCompleteGraph}"); + return SizedBox( height: MediaQuery.sizeOf(context).height * .75, child: (model.state == ViewState.Busy) ? Material( @@ -58,19 +60,31 @@ class FlowChartPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ + Padding( padding: const EdgeInsets.only( - top: 16.0, right: 16.0, bottom: 10), - child: GestureDetector( - onTap: () => Navigator.pop(context), - child: Align( - alignment: Alignment.centerRight, - child: Icon( - Icons.close, + top: 16.0, right: 16.0, bottom: 10, left: 16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(filterName??"", style: TextStyle( + fontSize: 21, + fontWeight: FontWeight.w700, color: Colors.black, - size: 28, + letterSpacing: -0.64, + ),), + GestureDetector( + onTap: () => Navigator.pop(context), + child: Align( + alignment: Alignment.centerRight, + child: Icon( + Icons.close, + color: Colors.black, + size: 28, + ), + ), ), - ), + ], ), ), Container( @@ -110,6 +124,7 @@ class FlowChartPage extends StatelessWidget { child: DynamicResultChart( dataPoints: model.threePointGraphValue, thresholds: model.threshold, + maxY: model.maxYForThreeDots, width: MediaQuery.sizeOf(context).width - 77, scrollDirection: Axis.horizontal, height: MediaQuery.sizeOf(context).height / 4, @@ -124,12 +139,14 @@ class FlowChartPage extends StatelessWidget { return FullScreenGraph( completeeGraphValues: model.completeeGraphValues, threshold: model.threshold, + maxY : model.maxYForCompleteGraph ); })); }); }, - child: Text(TranslationBase.of(context) - .seeAllGraphValues), + // child: Text(TranslationBase.of(context) + // .seeAllGraphValues), + child: Utils.tableColumnValueWithUnderLine(TranslationBase.of(context).seeAllGraphValues, isLast: true, isCapitable: false), ) ], ), @@ -151,7 +168,7 @@ class FlowChartPage extends StatelessWidget { ), ), ), - ), + );} ); } } diff --git a/lib/widgets/data_display/medical/LabResult/Lab_Result_details_wideget.dart b/lib/widgets/data_display/medical/LabResult/Lab_Result_details_wideget.dart index 088cf153..b8961a39 100644 --- a/lib/widgets/data_display/medical/LabResult/Lab_Result_details_wideget.dart +++ b/lib/widgets/data_display/medical/LabResult/Lab_Result_details_wideget.dart @@ -28,7 +28,7 @@ class _VitalSignDetailsWidgetState extends State { columnWidths: { 0: FlexColumnWidth(2), 1: FlexColumnWidth(1), - 2: FlexColumnWidth(1), + // 2: FlexColumnWidth(1), }, children: fullData(widget.labResult!, context), ); @@ -41,7 +41,7 @@ class _VitalSignDetailsWidgetState extends State { children: [ Utils.tableColumnTitle(TranslationBase.of(context).date), Utils.tableColumnTitle(TranslationBase.of(context).labResults), - Utils.tableColumnTitle(TranslationBase.of(context).change), + // Utils.tableColumnTitle(TranslationBase.of(context).change), ], ), ); @@ -52,7 +52,7 @@ class _VitalSignDetailsWidgetState extends State { children: [ Utils.tableColumnValue(DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(labOrderResultList![i].verifiedOnDateTime!)), isLast: i == (labOrderResultList.length - 1), mProjectViewModel: projectViewModel), Utils.tableColumnValue(labOrderResultList[i].resultValue!, isLast: i == (labOrderResultList.length - 1), mProjectViewModel: projectViewModel), - changeResult(labOrderResultList[i].changeResult ?? '', labOrderResultList[i].calculatedResultFlag??'IRR') + // changeResult(labOrderResultList[i].changeResult ?? '', labOrderResultList[i].calculatedResultFlag??'IRR') ], ), ); diff --git a/lib/widgets/data_display/medical/LabResult/full_screen_lab_result_graph.dart b/lib/widgets/data_display/medical/LabResult/full_screen_lab_result_graph.dart index a4e05b7a..af516642 100644 --- a/lib/widgets/data_display/medical/LabResult/full_screen_lab_result_graph.dart +++ b/lib/widgets/data_display/medical/LabResult/full_screen_lab_result_graph.dart @@ -8,8 +8,9 @@ import 'package:flutter/material.dart'; class FullScreenGraph extends StatelessWidget { final List completeeGraphValues ; final List threshold ; + final double maxY ; - const FullScreenGraph({super.key, required this.completeeGraphValues, required this.threshold}); + const FullScreenGraph({super.key, required this.completeeGraphValues, required this.threshold, required this. maxY}); @override Widget build(BuildContext context) { return AppScaffold( @@ -30,13 +31,13 @@ class FullScreenGraph extends StatelessWidget { child: DynamicResultChart( dataPoints: completeeGraphValues, thresholds: threshold, + maxY : maxY, // width:((completeeGraphValues.length <=2)? MediaQuery.sizeOf(context).height : (MediaQuery.sizeOf(context).height* ( // completeeGraphValues.length <= 3 // ? 1 // : (completeeGraphValues.length/3)) // ) )-77, width: MediaQuery.sizeOf(context).height-100, - maxX: completeeGraphValues.length.toDouble(), scrollDirection: Axis.horizontal, height: MediaQuery.sizeOf(context).width, isFullScreenGraph: true, diff --git a/lib/widgets/data_display/medical/LabResult/lab_result_graph.dart b/lib/widgets/data_display/medical/LabResult/lab_result_graph.dart index f811b5a6..d8ff9c40 100644 --- a/lib/widgets/data_display/medical/LabResult/lab_result_graph.dart +++ b/lib/widgets/data_display/medical/LabResult/lab_result_graph.dart @@ -8,24 +8,33 @@ class DynamicResultChart extends StatelessWidget { final List thresholds; final double? width; final double height; - final double? maxX; + final double? maxY; + final Axis scrollDirection; final bool isFullScreenGraph; - const DynamicResultChart( - {super.key, required this.dataPoints, required this.thresholds, required this.width, required this.scrollDirection, required this.height, this.maxX, this.isFullScreenGraph = false}); + DynamicResultChart( + {super.key, required this.dataPoints, required this.thresholds, required this.width, required this.scrollDirection, required this.height, this.maxY, this.isFullScreenGraph = false}); @override Widget build(BuildContext context) { var minY = 0.0; - var maxY = 0.0; - if (thresholds.isNotEmpty) { - print("the thresholds are $thresholds"); - minY = thresholds.first.value; - maxY = thresholds.last.value + 1; - } else { - return SizedBox.shrink(); - } + // var maxY = 0.0; + print("the maxY from parameter is $maxY"); + double interval = 20; + if((maxY??0)>10 &&(maxY??0)<=20) interval = 2; + else if((maxY??0)>5 &&(maxY??0)<=10) interval = 1; + else if((maxY??0)>=0 &&(maxY??0)<=5) interval = .4; + // else if((maxY??0)>=100) interval = 20; + print("the interval parameter is $interval"); + + // if (thresholds.isNotEmpty) { + // print("the thresholds are $thresholds"); + // minY = thresholds.first.value; + // maxY = thresholds.last.value + 1; + // } else { + // return SizedBox.shrink(); + // } final spots = dataPoints.asMap().entries.map((entry) { return FlSpot(entry.key.toDouble(), entry.value.value); @@ -42,10 +51,10 @@ class DynamicResultChart extends StatelessWidget { padding: const EdgeInsets.only(top: 8.0, bottom: 8), child: LineChart( LineChartData( - minY: minY, - maxY: maxY, - minX: dataPoints.first.labelValue - 1, - maxX: maxX, + minY: 0, + maxY: ((maxY?.ceilToDouble()??0.0)+interval ).floorToDouble(), + // minX: dataPoints.first.labelValue - 1, + // maxX: maxX, lineTouchData: LineTouchData(touchTooltipData: LineTouchTooltipData(getTooltipItems: (touchedSpots) { if (touchedSpots.isEmpty) return []; @@ -72,32 +81,52 @@ class DynamicResultChart extends StatelessWidget { })), titlesData: FlTitlesData( leftTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - reservedSize: 77, - interval: .1, // Let fl_chart handle it - getTitlesWidget: (value, _) { - // print("the value is ======== ${value}"); - - // Compare with 2-decimal precision to avoid close duplicates - final matchingThreshold = thresholds.firstWhere( - (t) => t.value.toStringAsFixed(1) == value.toStringAsFixed(1), - orElse: () => ThresholdRange(label: '', value: 0, color: Colors.transparent, lineColor: Colors.transparent), - ); - var actualValue = (matchingThreshold.actualValue != null) - ? "${TranslationBase.of(context).getTranslation(matchingThreshold.label)} (${matchingThreshold.actualValue})" - : '${TranslationBase.of(context).getTranslation(matchingThreshold.label)}'; - if (matchingThreshold.label.isNotEmpty) { + sideTitles : SideTitles( + showTitles: true, + reservedSize: 50, + interval: interval, + getTitlesWidget: (value, meta) { + // Skip the last (maxY) label + if (value == ((maxY?.ceilToDouble()??0.0)+interval ).floorToDouble()) { + return const SizedBox.shrink(); + } return Text( - actualValue, + value.toStringAsFixed(1), style: const TextStyle(fontSize: 10), ); - } - return const SizedBox.shrink(); - }, - ), + }, + ) + + ), + // leftTitles: AxisTitles( + // sideTitles: SideTitles( + // showTitles: true, + // reservedSize: 77, + // interval: .1, // Let fl_chart handle it + // getTitlesWidget: (value, _) { + // // print("the value is ======== ${value}"); + // + // // Compare with 2-decimal precision to avoid close duplicates + // final matchingThreshold = thresholds.firstWhere( + // (t) => t.value.toStringAsFixed(1) == value.toStringAsFixed(1), + // orElse: () => ThresholdRange(label: '', value: 0, color: Colors.transparent, lineColor: Colors.transparent), + // ); + // + // var actualValue = (matchingThreshold.actualValue != null) + // ? "${TranslationBase.of(context).getTranslation(matchingThreshold.label)} (${matchingThreshold.actualValue})" + // : '${TranslationBase.of(context).getTranslation(matchingThreshold.label)}'; + // if (matchingThreshold.label.isNotEmpty) { + // return Text( + // "${value.toStringAsFixed(0)}", + // style: const TextStyle(fontSize: 10), + // ); + // } + // return const SizedBox.shrink(); + // }, + // ), + // ), bottomTitles: AxisTitles( axisNameSize: 60, sideTitles: SideTitles( @@ -128,27 +157,38 @@ class DynamicResultChart extends StatelessWidget { topTitles: AxisTitles(), rightTitles: AxisTitles(), ), - borderData: FlBorderData(show: false), + borderData: FlBorderData(show: true,border: const Border( + bottom: BorderSide( + color: Colors.grey, + width: 0.5, + ), + left: BorderSide( + color: Colors.grey, + width: .5 + ), + right:BorderSide.none, + top: BorderSide.none, + ),), lineBarsData: _buildColoredLineSegments(dataPoints, thresholds), - gridData: FlGridData(show: false), - extraLinesData: ExtraLinesData( - horizontalLines: thresholds.map((t) { - return HorizontalLine( - y: t.value, - color: t.lineColor, - strokeWidth: 2, - // label: HorizontalLineLabel( - // show: true, - // alignment: Alignment.centerLeft, - // style: const TextStyle(fontSize: 10), - // labelResolver: (_) => '${t.label} (${t.value.toStringAsFixed(2)})', - // ), - ); - }).toList(), - ), - rangeAnnotations: RangeAnnotations( - horizontalRangeAnnotations: _buildRangeShades(thresholds), - ), + gridData: FlGridData(show: true, drawVerticalLine: false, getDrawingHorizontalLine:(value)=> FlLine(color: Colors.grey, strokeWidth: 0.5)), + // extraLinesData: ExtraLinesData( + // horizontalLines: [ + // HorizontalLine( + // color: Colors.black, + // strokeWidth: 2, + // dashArray: [5, 5], // optional: dashed line + // label: HorizontalLineLabel( + // show: true, + // alignment: Alignment.centerRight, + // style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold), + // labelResolver: (line) => 'Threshold ${line.y.toInt()}', + // ), + // ), + // ], + // ), + // rangeAnnotations: RangeAnnotations( + // horizontalRangeAnnotations: _buildRangeShades(thresholds), + // ), ), ), ), @@ -157,10 +197,11 @@ class DynamicResultChart extends StatelessWidget { List _buildColoredLineSegments(List dataPoints, List thresholds) { List segments = []; - Color getColor(double value) { + Color getColor(String value) { + print("the value to compare is $value"); for (int i = thresholds.length - 1; i >= 0; i--) { - if (value >= thresholds[i].value) { - return thresholds[i].lineColor; + if (value == thresholds[i].label) { + return thresholds[i].lineColor; } } return Colors.grey; @@ -173,20 +214,34 @@ class DynamicResultChart extends StatelessWidget { final spot1 = FlSpot(i.toDouble(), dp1.value); final spot2 = FlSpot((i + 1).toDouble(), dp2.value); - final color1 = getColor(dp1.value); - final color2 = getColor(dp2.value); + final color1 = getColor(dp1.referenceRangeValue); + final color2 = getColor(dp2.referenceRangeValue); segments.add(LineChartBarData( spots: [spot1, spot2], - isCurved: false, - barWidth: 2, + isCurved: true, + isStrokeCapRound: true, + isStrokeJoinRound: true, gradient: LinearGradient( colors: [color1, color2], begin: Alignment.centerLeft, end: Alignment.centerRight, ), - dotData: FlDotData(show: false), // We'll handle dots separately - )); + // dotData: FlDotData( + // show: true, + // getDotPainter: (spot, _, __, index) { + // final value = spot.y; + // final color = getColor(value); + // return FlDotCirclePainter( + // radius: 4, + // color: color, + // strokeWidth: 1, + // strokeColor: Colors.white, + // ); + // }, + // ), + ) + ); } // Add dot markers separately so they are on top @@ -197,16 +252,23 @@ class DynamicResultChart extends StatelessWidget { print("the spots are $allSpots"); var data = [ - ...segments, + // ...segments LineChartBarData( spots: allSpots, - isCurved: false, - barWidth: 0, // invisible line + isCurved: true, + isStrokeCapRound: true, + isStrokeJoinRound: true, + barWidth: 2, // invisible line + gradient: LinearGradient( + colors: [Color(0xFF5dc36b), Color(0xFF5dc36b)], + begin: Alignment.centerLeft, + end: Alignment.centerRight, + ), dotData: FlDotData( show: true, getDotPainter: (spot, _, __, index) { final value = spot.y; - final color = getColor(value); + final color = Color(0xFF5dc36b); return FlDotCirclePainter( radius: 4, color: color, @@ -256,8 +318,9 @@ class DataPoint { String actualValue; final DateTime date; bool isStringResource; + String referenceRangeValue; - DataPoint({required this.value, required this.label, required this.date, this.isStringResource = false, this.labelValue = 0.0, this.actualValue = ""}); + DataPoint({required this.value, required this.label, required this.date, this.isStringResource = false, this.labelValue = 0.0, this.actualValue = "", this.referenceRangeValue= ""}); } class ThresholdRange { diff --git a/lib/widgets/data_display/medical/LabResult/newUI/LabItem.dart b/lib/widgets/data_display/medical/LabResult/newUI/LabItem.dart index 55cd84e8..ed4ed02e 100644 --- a/lib/widgets/data_display/medical/LabResult/newUI/LabItem.dart +++ b/lib/widgets/data_display/medical/LabResult/newUI/LabItem.dart @@ -86,7 +86,7 @@ class _LabItemState extends State { subTitle: data?.resultValue ?? '', referenceRange: data?.referanceRange ?? '', percentage: data?.percentage ?? 0.0, - note: data?.testShortDescription ?? '', + note: data?.notes ?? '', source: "", buttonText: "", type: data?.calculatedResultFlag?.getType() ?? ResultTypes.unknown,