diff --git a/apps/web/src/pages/EntityDetail/ActivityChart.tsx b/apps/web/src/pages/EntityDetail/ActivityChart.tsx
index 542e81c..811124b 100644
--- a/apps/web/src/pages/EntityDetail/ActivityChart.tsx
+++ b/apps/web/src/pages/EntityDetail/ActivityChart.tsx
@@ -24,7 +24,7 @@ interface ActivityChartProps {
}
const CHART_HEIGHT = 260
-const MARGIN = { bottom: 30, left: 50, right: 60, top: 10 }
+const MARGIN = { bottom: 30, left: 50, right: 110, top: 10 }
/** Hypnogram Y-axis labels in display order (top to bottom). */
const HYPNOGRAM_LABELS = ['Awake', 'REM', 'Light', 'Deep']
@@ -95,12 +95,14 @@ const drawLineOverlay = (
unit: string,
axisSide: 'left' | 'right',
axisOffset: number = 0,
+ forceZeroMin: boolean = false,
) => {
const yExtent = d3.extent(data, (d) => d[1]) as [number, number]
const padding = (yExtent[1] - yExtent[0]) * 0.1 || 5
+ const yMin = forceZeroMin && yExtent[0] >= 0 ? 0 : yExtent[0] - padding
const yScale = d3
.scaleLinear()
- .domain([yExtent[0] - padding, yExtent[1] + padding])
+ .domain([yMin, yExtent[1] + padding])
.range([innerHeight, 0])
if (axisSide === 'right') {
@@ -223,7 +225,7 @@ export const ActivityChart = ({
const hrVisible = hasData(hrData)
const axisSide = hrVisible || hasHypnogram ? 'right' : 'left'
const offset = axisSide === 'right' && hrVisible ? 45 : 0
- drawLineOverlay(g, xScale, innerWidth, innerHeight, hrvData, '#14b8a6', 'ms', axisSide, offset)
+ drawLineOverlay(g, xScale, innerWidth, innerHeight, hrvData, '#14b8a6', 'ms', axisSide, offset, true)
}
// Tooltip crosshair and interaction overlay
diff --git a/apps/web/src/pages/EntityDetail/EditableActivityFields.tsx b/apps/web/src/pages/EntityDetail/EditableActivityFields.tsx
index fc4428d..44b7638 100644
--- a/apps/web/src/pages/EntityDetail/EditableActivityFields.tsx
+++ b/apps/web/src/pages/EntityDetail/EditableActivityFields.tsx
@@ -75,17 +75,16 @@ export const EditableActivityFields = ({
{durationLabel}
{draftDuration}
-
- Notes
-
-
-
+
+
+
+ Notes
+
>
)
diff --git a/apps/web/src/pages/EntityDetail/ExerciseDetail.tsx b/apps/web/src/pages/EntityDetail/ExerciseDetail.tsx
index 16b792f..82f2ff6 100644
--- a/apps/web/src/pages/EntityDetail/ExerciseDetail.tsx
+++ b/apps/web/src/pages/EntityDetail/ExerciseDetail.tsx
@@ -1,7 +1,8 @@
/**
* Exercise-specific detail view with HR chart and HR zone bar.
*/
-import { Activity } from '../../state/api'
+import { useQuery } from '@tanstack/react-query'
+import { Activity, fetchMetricTimeSeries } from '../../state/api'
import { ActivityChart } from './ActivityChart'
import { type ActivityDraft, EditableActivityFields } from './EditableActivityFields'
@@ -64,6 +65,17 @@ export const ExerciseDetail = ({
| string
| undefined
+ const caloriesQuery = useQuery({
+ queryFn: () => fetchMetricTimeSeries('calories_active', displayStart, displayEnd),
+ queryKey: ['detail-calories', displayStart.toISOString(), displayEnd.toISOString()],
+ staleTime: 5 * 60 * 1000,
+ })
+
+ const totalCalories =
+ caloriesQuery.data && caloriesQuery.data.length > 0 ?
+ Math.round(caloriesQuery.data.reduce((sum, [, val]) => sum + val, 0))
+ : undefined
+
return (
<>
@@ -82,6 +94,15 @@ export const ExerciseDetail = ({
onDraftChange={onDraftChange}
/>
+ {!isEditing && totalCalories !== undefined && (
+
+
+ Active Calories
+ {totalCalories} kcal
+
+
+ )}
+
{!isEditing && activity.avg_hrv !== undefined && (
@@ -96,7 +117,7 @@ export const ExerciseDetail = ({
{/* HR chart with overlays */}
>
)
diff --git a/apps/web/src/pages/EntityDetail/index.tsx b/apps/web/src/pages/EntityDetail/index.tsx
index 8cbacd8..3b9fce5 100644
--- a/apps/web/src/pages/EntityDetail/index.tsx
+++ b/apps/web/src/pages/EntityDetail/index.tsx
@@ -449,9 +449,9 @@ export const EntityDetail = () => {
return (
diff --git a/apps/web/src/pages/EntityDetail/style.css b/apps/web/src/pages/EntityDetail/style.css
index d12451e..47c714c 100644
--- a/apps/web/src/pages/EntityDetail/style.css
+++ b/apps/web/src/pages/EntityDetail/style.css
@@ -20,6 +20,14 @@
text-decoration: underline;
}
+.back-link-btn {
+ background: none;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+ font: inherit;
+}
+
/* Deleted banner */
.deleted-banner {
display: flex;
@@ -617,6 +625,13 @@
box-shadow: 0 0 0 2px rgba(103, 58, 184, 0.2);
}
+.edit-notes-block {
+ margin-top: 0.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
.edit-notes-input {
width: 100%;
padding: 0.375rem 0.5rem;