mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
fix(insights): point the empty-state CTAs at the existing measurements route
The B4 insights empty-state CTAs (blutdruck, gewicht, puls, bmi) linked to `/measurements/new`, a route that does not exist; a tap landed on a Next.js 404. Re-route via `/measurements?add=<TYPE>`, the parameter the measurements page now consumes. Each CTA picks a sensible default: BLOOD_PRESSURE for the BP page, WEIGHT for the weight and BMI pages, PULSE for the pulse page. The mood, sleep, and medication empty-states already pointed at their own dedicated routes and need no change. Add a `?add=<TYPE>` consumer to the measurements page. The consumer opens the add-measurement dialog with the right `defaultType` on first render, then `router.replace` strips the param so the back button returns to a clean `/measurements`. The allow-list mirrors MEASUREMENT_TYPES inside `measurement-form.tsx` so a stale link cannot trap the user on an unknown form. The render-driven set-state pattern follows account-section.tsx and satisfies the `react-hooks/set-state-in-effect` rule.
This commit is contained in:
1 parent
7ad894b535
commit
e52b64bf34
5 files changed
+75
-17
No files matched your search
@@ -27,9 +27,12 @@ import { hasMetricData } from "@/lib/insights/metric-availability";
|
||||
* assessment, and the chart's target zones tied to the user's age.
|
||||
*
|
||||
* v1.4.27 F17 — when the user has zero BP observations, the page
|
||||
* short-circuits to an empty-state CTA pointing at `/measurements/new`.
|
||||
* Apple Health convention: empty surfaces lead to onboarding hints
|
||||
* rather than chart skeletons.
|
||||
* short-circuits to an empty-state CTA pointing at
|
||||
* `/measurements?add=BLOOD_PRESSURE`. Apple Health convention: empty
|
||||
* surfaces lead to onboarding hints rather than chart skeletons.
|
||||
* v1.4.27 MB6 — the previous `/measurements/new` href hit a 404; the
|
||||
* measurements page now consumes `?add=<TYPE>` and auto-opens the
|
||||
* dialog with the matching default type.
|
||||
*/
|
||||
interface AnalyticsData {
|
||||
summaries: Record<string, DataSummary>;
|
||||
@@ -79,7 +82,7 @@ export default function InsightsBlutdruckPage() {
|
||||
description={t("insights.emptyState.bloodPressure.description")}
|
||||
action={
|
||||
<Button size="sm" asChild>
|
||||
<Link href="/measurements/new">
|
||||
<Link href="/measurements?add=BLOOD_PRESSURE">
|
||||
{t("insights.emptyState.bloodPressure.cta")}
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
@@ -68,7 +68,9 @@ export default function InsightsBmiPage() {
|
||||
// v1.4.27 F17 — BMI is derived from WEIGHT. When no weight readings
|
||||
// exist yet, the existing "set your height" branch can never compute
|
||||
// anything useful either; surface the empty-state CTA pointing at
|
||||
// `/measurements/new` instead so the user logs the weight first.
|
||||
// `/measurements?add=WEIGHT` instead so the user logs the weight
|
||||
// first. v1.4.27 MB6 — query-param replaces the dead
|
||||
// `/measurements/new` route.
|
||||
if (
|
||||
isAuthenticated &&
|
||||
analytics &&
|
||||
@@ -86,7 +88,7 @@ export default function InsightsBmiPage() {
|
||||
description={t("insights.emptyState.bmi.description")}
|
||||
action={
|
||||
<Button size="sm" asChild>
|
||||
<Link href="/measurements/new">
|
||||
<Link href="/measurements?add=WEIGHT">
|
||||
{t("insights.emptyState.bmi.cta")}
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
@@ -76,7 +76,7 @@ export default function InsightsGewichtPage() {
|
||||
description={t("insights.emptyState.weight.description")}
|
||||
action={
|
||||
<Button size="sm" asChild>
|
||||
<Link href="/measurements/new">
|
||||
<Link href="/measurements?add=WEIGHT">
|
||||
{t("insights.emptyState.weight.cta")}
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
@@ -71,8 +71,10 @@ export default function InsightsPulsPage() {
|
||||
|
||||
// v1.4.27 F17 — gate the sub-page on at least one pulse observation.
|
||||
// Brand-new accounts (no manual logs, no Apple-Health upload yet)
|
||||
// see a one-line empty-state with a CTA into `/measurements/new`
|
||||
// rather than a chart skeleton over a blank target band.
|
||||
// see a one-line empty-state with a CTA into
|
||||
// `/measurements?add=PULSE` rather than a chart skeleton over a
|
||||
// blank target band. v1.4.27 MB6 — query-param replaces the dead
|
||||
// `/measurements/new` route.
|
||||
if (
|
||||
isAuthenticated &&
|
||||
analytics &&
|
||||
@@ -90,7 +92,7 @@ export default function InsightsPulsPage() {
|
||||
description={t("insights.emptyState.pulse.description")}
|
||||
action={
|
||||
<Button size="sm" asChild>
|
||||
<Link href="/measurements/new">
|
||||
<Link href="/measurements?add=PULSE">
|
||||
{t("insights.emptyState.pulse.cta")}
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
@@ -1,22 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { MeasurementForm } from "@/components/measurements/measurement-form";
|
||||
import { MeasurementList } from "@/components/measurements/measurement-list";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ResponsiveSheet } from "@/components/ui/responsive-sheet";
|
||||
import { Plus, Loader2 } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
|
||||
// v1.4.27 MB6 — the insights empty-state CTAs link here with a
|
||||
// `?add=<TYPE>` query param to auto-open the add-measurement dialog
|
||||
// with the right default type. Keep the allow-list aligned with
|
||||
// MEASUREMENT_TYPES inside `measurement-form.tsx`; an unknown value
|
||||
// is dropped silently so a stale link can never trap the user on a
|
||||
// broken form.
|
||||
const ALLOWED_ADD_TYPES = new Set([
|
||||
"BLOOD_PRESSURE",
|
||||
"WEIGHT",
|
||||
"PULSE",
|
||||
"BMI",
|
||||
"GLUCOSE",
|
||||
"TEMPERATURE",
|
||||
"BODY_FAT",
|
||||
"HEART_RATE",
|
||||
]);
|
||||
|
||||
export default function MeasurementsPage() {
|
||||
const { isAuthenticated, isLoading } = useAuth();
|
||||
const router = useRouter();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const searchParams = useSearchParams();
|
||||
const { t } = useTranslations();
|
||||
|
||||
// v1.4.27 MB6 — read the `?add=<TYPE>` query param during render so
|
||||
// the dialog opens on the first paint (no flash of the page behind
|
||||
// it). The state-from-prop pattern follows `account-section.tsx`:
|
||||
// store the "addToken we acted on" sentinel and let render kick off
|
||||
// the open + replace transition once per param value. The lint rule
|
||||
// `react-hooks/set-state-in-effect` rejects setState from inside an
|
||||
// effect, so the open-on-param work is render-driven instead.
|
||||
const addParam = searchParams.get("add");
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [defaultType, setDefaultType] = useState<string | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const [consumedAddParam, setConsumedAddParam] = useState<string | null>(null);
|
||||
|
||||
if (addParam && addParam !== consumedAddParam) {
|
||||
setConsumedAddParam(addParam);
|
||||
if (ALLOWED_ADD_TYPES.has(addParam)) {
|
||||
setDefaultType(addParam);
|
||||
setDialogOpen(true);
|
||||
}
|
||||
// Drop the query string so the back-button leaves the user on
|
||||
// `/measurements` rather than re-opening the dialog.
|
||||
router.replace("/measurements");
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !isAuthenticated) {
|
||||
router.push("/auth/login");
|
||||
@@ -50,12 +91,22 @@ export default function MeasurementsPage() {
|
||||
|
||||
<ResponsiveSheet
|
||||
open={dialogOpen}
|
||||
onOpenChange={setDialogOpen}
|
||||
onOpenChange={(open) => {
|
||||
setDialogOpen(open);
|
||||
if (!open) setDefaultType(undefined);
|
||||
}}
|
||||
title={t("measurements.addMeasurement")}
|
||||
>
|
||||
<MeasurementForm
|
||||
onSuccess={() => setDialogOpen(false)}
|
||||
onCancel={() => setDialogOpen(false)}
|
||||
defaultType={defaultType}
|
||||
onSuccess={() => {
|
||||
setDialogOpen(false);
|
||||
setDefaultType(undefined);
|
||||
}}
|
||||
onCancel={() => {
|
||||
setDialogOpen(false);
|
||||
setDefaultType(undefined);
|
||||
}}
|
||||
/>
|
||||
</ResponsiveSheet>
|
||||
|
||||
|
||||
Reference in new issue
Block a user