mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
chore(targets): deprecate the targets page and drop it from the menu
Target editing now lives inline on the insights pages, so the standalone targets page is no longer a navigation destination. Remove its sidebar and bottom-nav (overflow) entries and add a top deprecation banner that points the user at the insights pages and notes the page will be removed in a future release. The page stays routable: the /zielwerte to /targets redirect remains and the route still renders. Removal later is UI-only because the insights panel consumes the same targets read and threshold-override write. New deprecation strings land across all six locales.
This commit is contained in:
1 parent
c72715bb62
commit
cd1eaa1a4e
13 files changed
+185
-12
No files matched your search
@@ -0,0 +1,3 @@
|
||||
# Targets (Zielwerte) page EOL tracker
|
||||
|
||||
- `/targets` deprecated in v1.8.6 — target-range editing moved inline into Insights (per-metric reference panel mounts `<TargetEditSheet>`); page dropped from nav, kept routable via the `/zielwerte`→`/targets` proxy redirect; remove candidate after ~10 releases (UI-only removal — `GET /api/insights/targets`, `PUT/DELETE /api/user/thresholds`, and `src/components/targets/*` all stay because Insights consumes them).
|
||||
@@ -4001,6 +4001,10 @@
|
||||
"targets": {
|
||||
"title": "Zielwerte",
|
||||
"introText": "Deine aktuellen Werte im Vergleich zu empfohlenen Zielbereichen. Quellen der Leitlinien sind auf jeder Karte verlinkt.",
|
||||
"deprecation": {
|
||||
"title": "Diese Seite wird eingestellt",
|
||||
"body": "Zielbereiche werden jetzt direkt in den Insights bearbeitet, im Referenzbereich jeder Metrik. Diese Seite bleibt vorerst erreichbar und wird in einem künftigen Release entfernt."
|
||||
},
|
||||
"bmi": "BMI",
|
||||
"average30d": "30-Tage-Schnitt:",
|
||||
"consistency": {
|
||||
|
||||
@@ -4001,6 +4001,10 @@
|
||||
"targets": {
|
||||
"title": "Targets",
|
||||
"introText": "Your current values compared to recommended target ranges. Guideline sources are linked on each card.",
|
||||
"deprecation": {
|
||||
"title": "This page is being retired",
|
||||
"body": "Target ranges are now edited directly from the Insights pages, on each metric's reference panel. This page stays available for now and will be removed in a future release."
|
||||
},
|
||||
"bmi": "BMI",
|
||||
"average30d": "30-day average:",
|
||||
"consistency": {
|
||||
|
||||
@@ -4001,6 +4001,10 @@
|
||||
"targets": {
|
||||
"title": "Valores objetivo",
|
||||
"introText": "Tus valores actuales frente a los rangos objetivo recomendados. Las fuentes de las guías clínicas están enlazadas en cada tarjeta.",
|
||||
"deprecation": {
|
||||
"title": "Esta página se va a retirar",
|
||||
"body": "Los rangos objetivo ahora se editan directamente desde las páginas de Insights, en el panel de referencia de cada métrica. Esta página sigue disponible por ahora y se eliminará en una versión futura."
|
||||
},
|
||||
"bmi": "IMC",
|
||||
"average30d": "Media 30 días:",
|
||||
"consistency": {
|
||||
|
||||
@@ -4001,6 +4001,10 @@
|
||||
"targets": {
|
||||
"title": "Valeurs cibles",
|
||||
"introText": "Tes valeurs actuelles face aux plages cibles recommandées. Les sources des recommandations sont liées sur chaque carte.",
|
||||
"deprecation": {
|
||||
"title": "Cette page va être retirée",
|
||||
"body": "Les plages cibles se modifient désormais directement depuis les pages Insights, sur le panneau de référence de chaque métrique. Cette page reste accessible pour l'instant et sera supprimée dans une prochaine version."
|
||||
},
|
||||
"bmi": "IMC",
|
||||
"average30d": "Moy. 30 jours :",
|
||||
"consistency": {
|
||||
|
||||
@@ -4001,6 +4001,10 @@
|
||||
"targets": {
|
||||
"title": "Valori obiettivo",
|
||||
"introText": "I tuoi valori attuali rispetto agli intervalli obiettivo consigliati. Le fonti delle linee guida sono collegate su ogni card.",
|
||||
"deprecation": {
|
||||
"title": "Questa pagina verrà rimossa",
|
||||
"body": "Gli intervalli obiettivo ora si modificano direttamente dalle pagine Insights, nel pannello di riferimento di ogni metrica. Questa pagina resta disponibile per ora e verrà rimossa in una versione futura."
|
||||
},
|
||||
"bmi": "IMC",
|
||||
"average30d": "Media 30 giorni:",
|
||||
"consistency": {
|
||||
|
||||
@@ -4001,6 +4001,10 @@
|
||||
"targets": {
|
||||
"title": "Wartości docelowe",
|
||||
"introText": "Twoje aktualne wartości na tle zalecanych zakresów docelowych. Źródła wytycznych podlinkowane na każdej karcie.",
|
||||
"deprecation": {
|
||||
"title": "Ta strona zostanie wycofana",
|
||||
"body": "Zakresy docelowe edytuje się teraz bezpośrednio na stronach Insights, w panelu referencyjnym każdej metryki. Ta strona pozostaje na razie dostępna i zostanie usunięta w przyszłej wersji."
|
||||
},
|
||||
"bmi": "BMI",
|
||||
"average30d": "Średnia 30-dniowa:",
|
||||
"consistency": {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
|
||||
import { I18nProvider } from "@/lib/i18n/context";
|
||||
|
||||
/**
|
||||
* v1.8.6 — `/targets` deprecation banner.
|
||||
*
|
||||
* The Targets (Zielwerte) page is deprecated: target-range editing moved
|
||||
* inline into the Insights category pages. The page stays routable but
|
||||
* carries a top banner telling the user where editing lives now and that
|
||||
* the page will be removed in a future release. This SSR test pins the
|
||||
* banner copy + structure.
|
||||
*
|
||||
* The page leans on `useAuth`, two `useQuery` reads, `useFeatureFlags`
|
||||
* and `useCoachHandoff`; all are stubbed so the render reaches the
|
||||
* banner without standing up an App-Router runtime or a QueryClient.
|
||||
*/
|
||||
|
||||
vi.mock("@/hooks/use-auth", () => ({
|
||||
useAuth: () => ({
|
||||
isAuthenticated: true,
|
||||
user: { id: "u1", disableCoach: false },
|
||||
}),
|
||||
}));
|
||||
|
||||
// The page issues two `useQuery` reads (targets payload + provider
|
||||
// chain). Drive both off the same stub: the first call resolves the
|
||||
// targets payload, the rest return a null provider chain.
|
||||
let queryCall = 0;
|
||||
const TARGETS_PAYLOAD = {
|
||||
targets: [],
|
||||
bpDiastolic: { current: null, average30: null, range: null },
|
||||
profile: { heightCm: 175, age: 40, gender: "male", glucoseUnit: "mg/dL" },
|
||||
};
|
||||
vi.mock("@tanstack/react-query", () => ({
|
||||
useQuery: () => {
|
||||
queryCall += 1;
|
||||
if (queryCall === 1) {
|
||||
return { data: TARGETS_PAYLOAD, isLoading: false };
|
||||
}
|
||||
return { data: null, isLoading: false };
|
||||
},
|
||||
}));
|
||||
|
||||
// Turn Coach off so the page never mounts the SSE drawer machinery.
|
||||
vi.mock("@/hooks/use-feature-flags", () => ({
|
||||
useFeatureFlags: () => ({
|
||||
enabled: false,
|
||||
coach: false,
|
||||
briefing: false,
|
||||
insightStatus: false,
|
||||
correlations: false,
|
||||
healthScoreExplainer: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@/hooks/use-coach-handoff", () => ({
|
||||
useCoachHandoff: () => ({
|
||||
coachOpen: false,
|
||||
setCoachOpen: vi.fn(),
|
||||
coachPrefill: null,
|
||||
coachScope: null,
|
||||
askCoach: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
const { default: TargetsPage } = await import("../page");
|
||||
|
||||
function render(locale: "en" | "de" = "en") {
|
||||
queryCall = 0;
|
||||
return renderToStaticMarkup(
|
||||
<I18nProvider initialLocale={locale}>
|
||||
<TargetsPage />
|
||||
</I18nProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe("<TargetsPage> deprecation banner", () => {
|
||||
it("renders the deprecation banner with title + body", () => {
|
||||
const html = render();
|
||||
expect(html).toContain('data-slot="targets-deprecation"');
|
||||
expect(html).toContain("This page is being retired");
|
||||
expect(html).toContain("Insights");
|
||||
// Reuses the warning-card visual pattern.
|
||||
expect(html).toContain("border-warning");
|
||||
});
|
||||
|
||||
it("localises the banner copy", () => {
|
||||
const html = render("de");
|
||||
expect(html).toContain("Diese Seite wird eingestellt");
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,21 @@
|
||||
"use client";
|
||||
|
||||
// DEPRECATED since v1.8.6. Target-range editing moved inline into the
|
||||
// Insights category pages (the per-metric reference panel now mounts the
|
||||
// same <TargetEditSheet>), so this standalone page no longer appears in
|
||||
// the navigation. It stays routable behind the `/zielwerte`→`/targets`
|
||||
// proxy redirect and is a remove candidate after ~10 releases.
|
||||
//
|
||||
// What stays in place because Insights consumes the same API:
|
||||
// • GET /api/insights/targets — the read powering this page is the
|
||||
// same payload the Insights panel reads (`queryKeys.insightsTargets()`).
|
||||
// • PUT/DELETE /api/user/thresholds — the threshold override write the
|
||||
// inline <TargetEditSheet> calls.
|
||||
// • src/components/targets/* — TargetCard, TargetEditSheet, RangeBar,
|
||||
// ConsistencyStrip, TargetStatusPill, source-link helper are all
|
||||
// reused by the inline Insights panel.
|
||||
// Removing the page later is a UI-only change; no data or API moves.
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { AlertCircle, Loader2 } from "lucide-react";
|
||||
@@ -200,6 +216,23 @@ export default function TargetsPage() {
|
||||
<p className="mt-2 text-sm">{t("targets.introText")}</p>
|
||||
</div>
|
||||
|
||||
{/* v1.8.6 — deprecation banner. Target editing moved inline into
|
||||
the Insights category pages; this page stays reachable but will
|
||||
be removed in a future release. */}
|
||||
<Card className="border-warning border-l-4" data-slot="targets-deprecation">
|
||||
<CardContent className="flex gap-3 py-3">
|
||||
<AlertCircle className="text-warning mt-0.5 h-4 w-4 shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm font-medium">
|
||||
{t("targets.deprecation.title")}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("targets.deprecation.body")}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Profile incomplete hint */}
|
||||
{profileIncomplete && (
|
||||
<Card className="border-warning border-l-4">
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
* - exactly five primary links + one "More" button render in the strip
|
||||
* - the More button is keyboard-reachable as a `<button>` with
|
||||
* `aria-haspopup="dialog"`
|
||||
* - Targets + Achievements live in the More sheet (closed by default
|
||||
* under SSR), not in the always-visible strip
|
||||
* - Achievements lives in the More sheet (closed by default under SSR),
|
||||
* not in the always-visible strip
|
||||
* - WCAG 2.5.5 floor: every entry is `min-h-11 min-w-11`
|
||||
*
|
||||
* v1.8.6 — the Targets (Zielwerte) page is deprecated and dropped from
|
||||
* the menu, so it no longer appears in the strip or the overflow set.
|
||||
*/
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
@@ -60,6 +63,15 @@ describe("<BottomNav> 5+More layout", () => {
|
||||
expect(matches.length).toBeGreaterThanOrEqual(6);
|
||||
});
|
||||
|
||||
it("no longer lists the deprecated /targets entry (v1.8.6)", () => {
|
||||
// Targets was dropped from the OVERFLOW set; the strip never carried
|
||||
// it. The closed-sheet SSR render must therefore contain no /targets
|
||||
// link — a regression net against the entry creeping back into the
|
||||
// primary strip or the overflow set.
|
||||
const html = render();
|
||||
expect(html).not.toContain('href="/targets"');
|
||||
});
|
||||
|
||||
it("More button carries an accessible label", () => {
|
||||
const html = render();
|
||||
// Attribute order isn't guaranteed by React's renderer, so match
|
||||
|
||||
@@ -84,6 +84,18 @@ describe("<SidebarNav> bug-report toggle", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("<SidebarNav> targets deprecation (v1.8.6)", () => {
|
||||
it("no longer renders the deprecated /targets entry", () => {
|
||||
// The Targets (Zielwerte) page is deprecated; target editing moved
|
||||
// inline into Insights, so the sidebar drops the entry. The main nav
|
||||
// links render into the SSR markup, so asserting absence here is a
|
||||
// real regression net (Insights, the sibling that stays, still shows).
|
||||
const html = render();
|
||||
expect(html).not.toContain('href="/targets"');
|
||||
expect(html).toContain('href="/insights"');
|
||||
});
|
||||
});
|
||||
|
||||
describe("<SidebarNav> admin entry mirrors Settings (no sub-item expansion)", () => {
|
||||
// v1.4.16 A1: the maintainer reported the global sidebar expanding admin
|
||||
// sub-items on `/admin/*` was unwanted UX — the in-shell `<AdminShell>`
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Lightbulb,
|
||||
MoreHorizontal,
|
||||
Pill,
|
||||
Target,
|
||||
Trophy,
|
||||
Waves,
|
||||
} from "lucide-react";
|
||||
@@ -35,10 +34,15 @@ interface NavLink {
|
||||
// just-pass on iPhone-13-mini-class screens. The mobile audit
|
||||
// recommended 5+More — so the four most-used surfaces stay in the
|
||||
// strip (Home, Measurements, Mood, Medications) plus Insights as
|
||||
// the fifth, and Targets + Achievements move into a "More" sheet.
|
||||
// the fifth, and Achievements moves into a "More" sheet.
|
||||
//
|
||||
// Order is preserved relative to the v1.4.15 bottom-nav so muscle
|
||||
// memory keeps working for the four core actions.
|
||||
//
|
||||
// v1.8.6 — the Targets (Zielwerte) page is deprecated: target editing
|
||||
// moved inline into Insights, so its overflow entry is gone. The page
|
||||
// stays routable (the `/zielwerte`→`/targets` proxy redirect remains)
|
||||
// but no longer appears in the menu.
|
||||
const PRIMARY: ReadonlyArray<NavLink> = [
|
||||
{ href: "/", tKey: "nav.dashboard", icon: Home },
|
||||
{ href: "/measurements", tKey: "nav.measurements", icon: Activity },
|
||||
@@ -48,7 +52,6 @@ const PRIMARY: ReadonlyArray<NavLink> = [
|
||||
];
|
||||
|
||||
const OVERFLOW: ReadonlyArray<NavLink> = [
|
||||
{ href: "/targets", tKey: "nav.targets", icon: Target },
|
||||
{ href: "/achievements", tKey: "nav.achievements", icon: Trophy },
|
||||
];
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
Settings,
|
||||
Shield,
|
||||
Sun,
|
||||
Target,
|
||||
Trophy,
|
||||
Waves,
|
||||
} from "lucide-react";
|
||||
@@ -73,12 +72,6 @@ const navItems = [
|
||||
icon: Lightbulb,
|
||||
tourId: "nav-insights",
|
||||
},
|
||||
{
|
||||
href: "/targets",
|
||||
tKey: "nav.targets",
|
||||
icon: Target,
|
||||
tourId: "nav-targets",
|
||||
},
|
||||
{
|
||||
href: "/achievements",
|
||||
tKey: "nav.achievements",
|
||||
|
||||
Reference in new issue
Block a user