Skip to content

Commit b92a163

Browse files
authored
fix(dashboard): glucose in-range coloring, treatment markers, loop pill units (#422)
Glucose chart "In Range" coloring used the personal target range (Low/High) for both point colors and the dashed reference lines. A narrow target (e.g. 95-95) collapsed the In Range band onto a single value. Color by a fixed clinical band (54/70/180/250) instead, independent of the target, and carry the personal target separately (ChartThresholdsDto.TargetLow/TargetHigh) as its own reference line. Carb and bolus markers were positioned at magnitude-dependent heights, so the two half-circles only combined into a single circle by coincidence. Anchor both to a shared baseline (IOB/COB track center) so a same-time carb+bolus always forms one circle; magnitude stays in the labels. Treatment hemisphere SVGs had their arc sweep flags reversed, so the arcs bulged outside the icon viewBox: the bolus legend symbol showed as a flat line and the carbs symbol was clipped away entirely; the chart markers bulged opposite to their labels. Correct the sweep flag in BolusIcon, CarbsIcon, BolusMarker and CarbMarker so bolus is a dome (above baseline) and carbs a bowl (below baseline), consistent across legend and chart. The Loop pill formatted eventual BG with a local helper defaulting to mmol/L and was never passed the user's unit preference. Use the shared bg() helper so it follows the global glucose-unit preference like every other BG display. Regenerated NSwag client/schemas for the new threshold fields.
1 parent 5fe56a6 commit b92a163

19 files changed

Lines changed: 4818 additions & 4717 deletions

File tree

src/API/Nocturne.API/Services/ChartData/Stages/ProfileLoadStage.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ namespace Nocturne.API.Services.ChartData.Stages;
1010
/// </summary>
1111
/// <remarks>
1212
/// <para>
13-
/// Hard-coded very-low (54 mg/dL) and very-high (250 mg/dL) thresholds are not configurable
14-
/// per profile; low and high targets are read from the active profile at <see cref="ChartDataContext.EndTime"/>.
15-
/// When no profile is available the fallback thresholds are 70/180 mg/dL and 1.0 U/hr basal.
13+
/// The coloring thresholds (very-low 54, low 70, high 180, very-high 250 mg/dL) are a fixed clinical
14+
/// glycemic band, not the patient's personal target — so a narrow target (e.g. 95-95) does not
15+
/// collapse the "In Range" band onto a single value. The personal target is read from the active
16+
/// profile at <see cref="ChartDataContext.EndTime"/> and carried separately as
17+
/// <see cref="ChartThresholdsDto.TargetLow"/>/<see cref="ChartThresholdsDto.TargetHigh"/> for a
18+
/// distinct reference line. When no profile is available there is no target and basal defaults to 1.0 U/hr.
1619
/// </para>
1720
/// </remarks>
1821
/// <seealso cref="IChartDataStage"/>
@@ -25,6 +28,8 @@ ILogger<ProfileLoadStage> logger
2528
) : IChartDataStage
2629
{
2730
private const double DefaultVeryLow = 54;
31+
private const double DefaultLow = 70;
32+
private const double DefaultHigh = 180;
2833
private const double DefaultVeryHigh = 250;
2934

3035
public async Task<ChartDataContext> ExecuteAsync(ChartDataContext context, CancellationToken cancellationToken)
@@ -42,9 +47,11 @@ public async Task<ChartDataContext> ExecuteAsync(ChartDataContext context, Cance
4247
thresholds = new ChartThresholdsDto
4348
{
4449
VeryLow = DefaultVeryLow,
45-
Low = await targetRangeResolver.GetLowBGTargetAsync(context.EndTime, ct: cancellationToken),
46-
High = await targetRangeResolver.GetHighBGTargetAsync(context.EndTime, ct: cancellationToken),
50+
Low = DefaultLow,
51+
High = DefaultHigh,
4752
VeryHigh = DefaultVeryHigh,
53+
TargetLow = await targetRangeResolver.GetLowBGTargetAsync(context.EndTime, ct: cancellationToken),
54+
TargetHigh = await targetRangeResolver.GetHighBGTargetAsync(context.EndTime, ct: cancellationToken),
4855
};
4956
defaultBasalRate = await basalRateResolver.GetBasalRateAsync(context.EndTime, ct: cancellationToken);
5057

@@ -55,8 +62,8 @@ public async Task<ChartDataContext> ExecuteAsync(ChartDataContext context, Cance
5562
thresholds = new ChartThresholdsDto
5663
{
5764
VeryLow = DefaultVeryLow,
58-
Low = 70,
59-
High = 180,
65+
Low = DefaultLow,
66+
High = DefaultHigh,
6067
VeryHigh = DefaultVeryHigh,
6168
};
6269
defaultBasalRate = 1.0;

src/Core/Nocturne.Core.Models/ChartDataDtos.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,28 @@ public class DashboardChartData
106106
}
107107

108108
/// <summary>
109-
/// Glucose threshold configuration derived from the active profile.
109+
/// Glucose threshold configuration for the dashboard chart.
110110
/// </summary>
111+
/// <remarks>
112+
/// <see cref="Low"/>/<see cref="High"/>/<see cref="VeryLow"/>/<see cref="VeryHigh"/> are the fixed
113+
/// clinical glycemic band used to color readings (Very Low / Low / In Range / High / Very High) and
114+
/// draw the in-range reference lines — they are independent of the patient's personal target.
115+
/// <see cref="TargetLow"/>/<see cref="TargetHigh"/> carry the personal BG target from the active
116+
/// profile, shown as a separate reference line; null when no profile is available.
117+
/// </remarks>
111118
public record ChartThresholdsDto
112119
{
113120
public double Low { get; init; }
114121
public double High { get; init; }
115122
public double VeryLow { get; init; }
116123
public double VeryHigh { get; init; }
117124
public double GlucoseYMax { get; init; }
125+
126+
/// <summary>Personal BG target lower bound (mg/dL) from the active profile; null when no profile.</summary>
127+
public double? TargetLow { get; init; }
128+
129+
/// <summary>Personal BG target upper bound (mg/dL) from the active profile; null when no profile.</summary>
130+
public double? TargetHigh { get; init; }
118131
}
119132

120133
/// <summary>

src/Web/packages/app/src/lib/api/generated/index.ts

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)