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:
focalContentscreenFocalscrollXShared
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:
| Label | Duration |
|---|---|
1H | 1 hour |
6H | 6 hours |
24H | 24 hours |
3D | 3 days |
1W | 1 week |
Each range affects:
- fetched history window
- tick interval spacing
- chart density
- formatting behavior
Zoom State Variants
Unzoomed (1×)
- 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:
| Zone | Range | Color |
|---|---|---|
| normal | 35–38°C | green |
| watch | 38–38.5°C | yellow |
| caution | 38.5–40°C | orange |
| danger | 40–41°C | red |
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-handlerreact-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:
| Section | Behavior |
|---|---|
| Y-axis | fixed |
| chart body | horizontally scrollable |
| X-axis labels | independently positioned |
| zone overlays | absolutely positioned |
This architecture allows independent scrolling and stable axis rendering during zoom operations.