diff --git a/messages/de.json b/messages/de.json index 4f600dbab..e1a27e32d 100644 --- a/messages/de.json +++ b/messages/de.json @@ -3817,7 +3817,7 @@ "subtitle": "Wähle, welche Erinnerungen du erhältst." }, "layout": { - "title": "Layout", + "title": "Dashboard", "description": "Lege fest, wie Dashboard und Insights aufgebaut und angeordnet sind.", "backToHub": "Zurück zu Layout & Personalisierung", "dashboard": { diff --git a/messages/en.json b/messages/en.json index 0c7cde8de..2dcf994be 100644 --- a/messages/en.json +++ b/messages/en.json @@ -3817,7 +3817,7 @@ "subtitle": "Choose which reminders you receive." }, "layout": { - "title": "Layout", + "title": "Dashboard", "description": "Arrange how the dashboard and insights are laid out.", "backToHub": "Back to Layout & Personalization", "dashboard": { diff --git a/messages/es.json b/messages/es.json index 7899eb0a7..4b61aeb82 100644 --- a/messages/es.json +++ b/messages/es.json @@ -3817,7 +3817,7 @@ "subtitle": "Elige qué recordatorios recibes." }, "layout": { - "title": "Diseño", + "title": "Panel", "description": "Organiza cómo están dispuestos el panel y los análisis.", "backToHub": "Volver a Diseño y personalización", "dashboard": { diff --git a/messages/fr.json b/messages/fr.json index e923ab65c..16210e300 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -3817,7 +3817,7 @@ "subtitle": "Choisissez les rappels que vous recevez." }, "layout": { - "title": "Disposition", + "title": "Tableau de bord", "description": "Organisez la disposition du tableau de bord et des analyses.", "backToHub": "Retour à Disposition et personnalisation", "dashboard": { diff --git a/messages/it.json b/messages/it.json index 5a4fece9a..3575c4db8 100644 --- a/messages/it.json +++ b/messages/it.json @@ -3817,7 +3817,7 @@ "subtitle": "Scegli quali promemoria ricevere." }, "layout": { - "title": "Layout", + "title": "Cruscotto", "description": "Organizza la disposizione del cruscotto e degli approfondimenti.", "backToHub": "Torna a Layout e personalizzazione", "dashboard": { diff --git a/messages/pl.json b/messages/pl.json index e99e307cc..04cd5b0fb 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -3817,7 +3817,7 @@ "subtitle": "Wybierz, które przypomnienia otrzymujesz." }, "layout": { - "title": "Układ", + "title": "Pulpit", "description": "Ustal, jak rozłożone są pulpit i analizy.", "backToHub": "Powrót do Układ i personalizacja", "dashboard": { diff --git a/src/components/layout/__tests__/nav-model.test.ts b/src/components/layout/__tests__/nav-model.test.ts index 0ee21814b..89c8c6c7b 100644 --- a/src/components/layout/__tests__/nav-model.test.ts +++ b/src/components/layout/__tests__/nav-model.test.ts @@ -50,9 +50,13 @@ describe("nav-model destination list", () => { expect(hrefs).toContain("/vorsorge"); const vorsorge = NAV_DESTINATIONS.find((d) => d.href === "/vorsorge"); expect(vorsorge?.tKey).toBe("nav.vorsorge"); - // Peer to Labs: sits in the clinical spine, before Insights. - expect(hrefs.indexOf("/labs")).toBeLessThan(hrefs.indexOf("/vorsorge")); - expect(hrefs.indexOf("/vorsorge")).toBeLessThan(hrefs.indexOf("/insights")); + // v1.19.1 (S4) — the fixed spine is Medications → Vorsorge → Labs → + // Illness → Insights, so Vorsorge sits after Medications and before Labs. + expect(hrefs.indexOf("/medications")).toBeLessThan( + hrefs.indexOf("/vorsorge"), + ); + expect(hrefs.indexOf("/vorsorge")).toBeLessThan(hrefs.indexOf("/labs")); + expect(hrefs.indexOf("/labs")).toBeLessThan(hrefs.indexOf("/insights")); }); it("does not list Recovery as a left-nav destination (it is an Insights pill)", () => { @@ -99,15 +103,17 @@ describe("nav-model utility tail (N-1 — one shared list)", () => { }); describe("visibleNavDestinations module gate", () => { - it("includes Cycle (between Medications and Insights) only when its module is enabled", () => { + it("includes Cycle (between Mood and Medications) only when its module is enabled", () => { // v1.18.0 — cycle is `requiresModule: "cycle"` and reads the delegated // `cycle` key from the same resolved module map every other gate uses. + // v1.19.1 (S4) — cycle sits in the head block, after Mood and before the + // fixed Medications → … → Achievements spine. const off = visibleNavDestinations({ cycle: false }).map((d) => d.href); const on = visibleNavDestinations({ cycle: true }).map((d) => d.href); expect(off).not.toContain("/cycle"); expect(on).toContain("/cycle"); - expect(on.indexOf("/medications")).toBeLessThan(on.indexOf("/cycle")); - expect(on.indexOf("/cycle")).toBeLessThan(on.indexOf("/insights")); + expect(on.indexOf("/mood")).toBeLessThan(on.indexOf("/cycle")); + expect(on.indexOf("/cycle")).toBeLessThan(on.indexOf("/medications")); }); it("drops a module-gated entry (mood / labs / coach / achievements / insights / medications) when its module is disabled", () => { diff --git a/src/components/layout/__tests__/sidebar-nav.test.tsx b/src/components/layout/__tests__/sidebar-nav.test.tsx index 14e46af07..049694844 100644 --- a/src/components/layout/__tests__/sidebar-nav.test.tsx +++ b/src/components/layout/__tests__/sidebar-nav.test.tsx @@ -109,17 +109,18 @@ describe(" cycle entry gate (v1.15.0 / v1.18.0 module map)", () => { expect(html).not.toContain('href="/cycle"'); }); - it("places the Cycle entry between Medications and Insights when enabled", () => { + it("places the Cycle entry between Mood and Medications when enabled", () => { const html = render({ modules: { cycle: true } }); - const med = html.indexOf('href="/medications"'); + const mood = html.indexOf('href="/mood"'); const cycle = html.indexOf('href="/cycle"'); - const insights = html.indexOf('href="/insights"'); - expect(med).toBeGreaterThan(-1); + const med = html.indexOf('href="/medications"'); + expect(mood).toBeGreaterThan(-1); expect(cycle).toBeGreaterThan(-1); - expect(insights).toBeGreaterThan(-1); - // Cycle sits after Medications and before Insights in document order. - expect(med).toBeLessThan(cycle); - expect(cycle).toBeLessThan(insights); + expect(med).toBeGreaterThan(-1); + // v1.19.1 (S4) — Cycle sits after Mood and before Medications in document + // order; Medications opens the fixed clinical/insight spine below it. + expect(mood).toBeLessThan(cycle); + expect(cycle).toBeLessThan(med); }); }); diff --git a/src/components/layout/nav-model.ts b/src/components/layout/nav-model.ts index c8f083d31..c0c94a18d 100644 --- a/src/components/layout/nav-model.ts +++ b/src/components/layout/nav-model.ts @@ -63,6 +63,10 @@ export interface NavDestination { * (after Medications) and is filtered out when the account gate is off; * the order is otherwise identical for both surfaces. */ +// v1.19.1 (S4) — the clinical/insight spine below the dashboard/measurements/ +// mood/cycle head follows one fixed sequence, each entry directly below the +// previous: Medications → Vorsorge → Labs → Illness → Insights → Coach → +// Achievements. A disabled module simply drops out; the rest keep this order. export const NAV_DESTINATIONS: ReadonlyArray = [ { href: "/", tKey: "nav.dashboard", icon: Home, tourId: "nav-dashboard" }, { @@ -78,6 +82,13 @@ export const NAV_DESTINATIONS: ReadonlyArray = [ tourId: "nav-mood", requiresModule: "mood", }, + { + href: "/cycle", + tKey: "nav.cycle", + icon: Droplets, + tourId: "nav-cycle", + requiresModule: "cycle", + }, { href: "/medications", tKey: "nav.medications", @@ -87,12 +98,23 @@ export const NAV_DESTINATIONS: ReadonlyArray = [ // module; the nav entry now drops when the account turns the module off. requiresModule: "medications", }, + // v1.17.1 — Vorsorge (preventive-care) gets a top-level nav home in the + // clinical spine. It is a first-class tracking surface ("wann muss ich was + // wo machen"), not pure configuration, so it belongs in the model both bars + // render — not buried three taps deep under Settings → Reminders. The + // Reminders hub still links to it; this is the direct front door. { - href: "/cycle", - tKey: "nav.cycle", - icon: Droplets, - tourId: "nav-cycle", - requiresModule: "cycle", + href: "/vorsorge", + tKey: "nav.vorsorge", + icon: Stethoscope, + tourId: "nav-vorsorge", + // v1.18.1 — deliberately NOT module-gated (no `requiresModule`). Unlike + // labs / illness / cycle (opt-in clinical-spine verticals born off by + // default), preventive-care reminders are a CORE surface available to + // every account from birth: a reminder can target core vitals + // (weight / BP / pulse) that are never behind a module toggle, and a + // free-text "Großes Blutbild" reminder belongs to no module at all. + // Gating the entry would orphan reminders the user can still create. }, { href: "/labs", @@ -112,25 +134,6 @@ export const NAV_DESTINATIONS: ReadonlyArray = [ tourId: "nav-illness", requiresModule: "illness", }, - // v1.17.1 — Vorsorge (preventive-care) gets a top-level nav home in the - // clinical spine, peer to Labs and Recovery. It is a first-class tracking - // surface ("wann muss ich was wo machen"), not pure configuration, so it - // belongs in the model both bars render — not buried three taps deep under - // Settings → Reminders. The Reminders hub still links to it; this is the - // direct front door so the customer knows where it lives. - { - href: "/vorsorge", - tKey: "nav.vorsorge", - icon: Stethoscope, - tourId: "nav-vorsorge", - // v1.18.1 — deliberately NOT module-gated (no `requiresModule`). Unlike - // labs / illness / cycle (opt-in clinical-spine verticals born off by - // default), preventive-care reminders are a CORE surface available to - // every account from birth: a reminder can target core vitals - // (weight / BP / pulse) that are never behind a module toggle, and a - // free-text "Großes Blutbild" reminder belongs to no module at all. - // Gating the entry would orphan reminders the user can still create. - }, // v1.18.0 — Workouts and Recovery both left the left-nav: each already // surfaces as an Insights tab-strip pill (`/insights/workouts` gated on // a workout row, `/insights/recovery` always present), so neither is a diff --git a/src/components/settings/__tests__/settings-shell.test.tsx b/src/components/settings/__tests__/settings-shell.test.tsx index da80f4bed..ebdc1945e 100644 --- a/src/components/settings/__tests__/settings-shell.test.tsx +++ b/src/components/settings/__tests__/settings-shell.test.tsx @@ -284,9 +284,11 @@ describe("", () => { // `/notifications` inbox now shares the "Notifications" label. expect(html).toContain("Notifications"); // v1.17.1 (F-2) — the dashboard + insights arrangement editors are - // reached through one Layout hub entry. v1.18.1 (D6) — renamed to "Layout". + // reached through one hub entry (route stays `/settings/layout`). + // v1.19.1 (S3) — the nav label now reads "Dashboard" to match where + // it leads; the route is unchanged. expect(html).toContain('href="/settings/layout"'); - expect(html).toContain(">Layout"); + expect(html).toContain(">Dashboard"); // v1.18.1 (D4) — Channels + Sources are their own left-side entries. expect(html).toContain('href="/settings/channels"'); expect(html).toContain('href="/settings/sources"'); @@ -322,10 +324,11 @@ describe("", () => { // compound "Benachrichtigungs-Kanäle" wrapped). The `/notifications` // inbox now shares the "Benachrichtigungen" label. expect(html).toContain("Benachrichtigungen"); - // v1.17.1 (F-2) — the arrangement editors are reached through one - // Layout hub entry. v1.18.1 (D6) — renamed to "Layout" in every locale. + // v1.17.1 (F-2) — the arrangement editors are reached through one hub + // entry (route stays `/settings/layout`). v1.19.1 (S3) — the nav label + // reads "Dashboard" in every locale to match where it leads. expect(html).toContain('href="/settings/layout"'); - expect(html).toContain(">Layout"); + expect(html).toContain(">Dashboard"); // v1.18.1 (D4) — Channels ("Kanäle") + Sources ("Quellen-Priorität") // are their own left-side entries. expect(html).toContain('href="/settings/channels"');