components/Dashboard.ts

Purpose

This module implements an interactive physiological temperature history dashboard for React Native.

It visualizes historical core body temperature data using:

  • a zoomable time-series chart
  • dynamic Y-axis scaling
  • pinch-to-zoom gestures
  • scrollable historical navigation
  • adaptive tick labeling
  • physiological temperature-zone overlays
  • localized time formatting
  • unit-system conversion support

The dashboard is designed for mobile performance and readability across a wide range of time spans, from one hour to one week.

Primary responsibilities include:

  • loading historical sensor data
  • normalizing sparse temperature readings into a continuous grid
  • dynamically scaling chart axes
  • managing chart zoom and scroll state
  • rendering temperature risk zones
  • adapting labels and formatting to localization and unit settings

Invariants

Temperature Domain Bounds

The chart operates within a bounded physiological temperature range:

36°C ≤ chart domain ≤ 40°C

using:

TEMP_MIN = 36
TEMP_MAX = 40

Dynamic scaling never exceeds these hard limits.


Zoom Bounds

Chart zoom level is always constrained to:

1× ≤ zoom ≤ 8×

using:

MAX_ZOOM = 8

Time Ordering

All chart operations assume chronological ordering.

allData, grid, and rendered chart points are expected to remain sorted ascending by timestamp.

Interpolation logic depends on this invariant.


Grid Normalization

The dashboard always converts sparse sensor readings into a regular minute-aligned grid.

This guarantees:

  • stable chart spacing
  • smooth interpolation
  • predictable tick generation
  • consistent zoom behavior

The grid interval is fixed at:

1 minute

Interpolation Safety

Interpolated points are generated only between neighboring readings.

For any generated grid point:

  • values remain bounded between adjacent samples
  • interpolation is linear
  • timestamps remain monotonic

Maximum Render Density

Rendered chart density is bounded by:

MAX_CHART_POINTS = 150

Large datasets are downsampled before rendering.

This prevents:

  • excessive React rendering cost
  • gesture lag
  • chart layout instability

Dynamic Y-Axis Consistency

When zoomed in:

  • Y-axis bounds derive only from visible data
  • additional padding is applied
  • axis bounds are normalized through niceAxis()

This guarantees:

  • readable vertical resolution
  • stable tick spacing
  • visually meaningful zoom behavior

Tick Spacing Constraints

X-axis tick labels maintain minimum horizontal separation:

MIN_TICK_SPACING = 58

This prevents overlapping labels during zoom transitions.


Scroll/Zoom Synchronization

Pinch gestures preserve the focal point under the user’s fingers.

During zoom:

  • content-space coordinates remain stable
  • scroll position is recalculated
  • chart scaling and scrolling stay synchronized

This invariant is maintained through:

  • focalContent
  • screenFocal
  • scrollXShared

Actual vs Interpolated Samples

Each grid point is tagged with:

isActual: boolean

Only actual sensor readings may render visible chart dots.

Interpolated points are visually differentiated.


Refresh Cadence

Historical data automatically refreshes every:

60 seconds

while the component remains mounted.


Localization Guarantees

Time formatting adapts to the active i18n language.

Supported variants currently include:

  • English (en-US)
  • Spanish (es-ES)

Variants

Time Range Variants

The dashboard supports multiple selectable history windows:

LabelDuration
1H1 hour
6H6 hours
24H24 hours
3D3 days
1W1 week

Each range affects:

  • fetched history window
  • tick interval spacing
  • chart density
  • formatting behavior

Zoom State Variants

Unzoomed ()

  • horizontal scrolling disabled
  • full-range Y-axis shown
  • baseline overview mode

Zoomed (>1×)

  • scrolling enabled
  • dynamic Y-axis enabled
  • visible-range analysis mode

Axis Scaling Variants

The chart dynamically adjusts Y-axis granularity.

Possible step sizes include:

0.25°C
0.5°C
1°C

depending on visible temperature variance.


Tick Formatting Variants

Short-Range Views

Displays:

HH:mm

Multi-Day Views

Displays:

Day HH:mm

Example:

Mon 14:30

Dot Rendering Variants

Dot visibility depends on chart density.

As zoom increases:

  • more actual points become individually visible

At low zoom:

  • dots are sparsified to reduce clutter

Physiological Zone Variants

The chart renders several background severity bands:

ZoneRangeColor
normal35–38°Cgreen
watch38–38.5°Cyellow
caution38.5–40°Corange
danger40–41°Cred

Bands dynamically reposition as the Y-axis changes.


Empty-State Variant

If no temperature history exists:

  • the chart is hidden
  • localized “no data” text is shown

Unit System Variants

The dashboard supports:

  • metric
  • imperial

Temperature labels are formatted through:

formatTemp()

Exports

Dashboard

export function Dashboard({
	unitSystem,
}: {
	unitSystem: "metric" | "imperial";
})

Primary exported chart component.

Responsibilities include:

  • history loading
  • gesture management
  • zoom state
  • chart rendering
  • dynamic axis computation
  • localization-aware formatting

Internal Types

DataPoint

type DataPoint = {
	value: number;
	timestamp: number;
};

Represents a timestamped core temperature sample.


RangeLabel

Derived union type representing valid dashboard ranges.

Examples:

"1H" | "6H" | "24H" | "3D" | "1W"

Internal Helper Functions

formatTime(ts, spanMs)

Formats timestamps for X-axis labels.

Adapts formatting based on visible timespan and locale.


pickInterval(spanMs)

Chooses adaptive tick intervals based on visible chart duration.

Used to prevent label overcrowding.


downsample(arr)

Reduces large datasets to a bounded rendering size.

Preserves:

  • ordering
  • endpoint visibility

buildGrid(data)

Transforms sparse readings into a regular minute-aligned interpolated grid.

Returns points tagged with isActual.


buildTickLabels(...)

Generates collision-aware X-axis tick labels.

Ensures:

  • minimum spacing
  • visible-range adaptation
  • proportional positioning

niceAxis(rawMin, rawMax)

Computes visually stable Y-axis bounds and step intervals.

Returns:

{
	lo,
	hi,
	step,
	sections
}

Used for dynamic Y-axis rendering.


Gesture System

Pinch Gesture

Implemented using:

  • react-native-gesture-handler
  • react-native-reanimated

Supports:

  • live pinch scaling
  • focal-point preservation
  • synchronized scrolling

Scroll Coordination

Horizontal scrolling is enabled only when zoomed in.

Scroll state affects:

  • dynamic Y-axis calculations
  • visible-window determination
  • gesture focal tracking

Rendering Architecture

The chart layout is split into:

SectionBehavior
Y-axisfixed
chart bodyhorizontally scrollable
X-axis labelsindependently positioned
zone overlaysabsolutely positioned

This architecture allows independent scrolling and stable axis rendering during zoom operations.