diff --git a/messages/de.json b/messages/de.json index 395fc13e3..f56ff12c3 100644 --- a/messages/de.json +++ b/messages/de.json @@ -4121,6 +4121,10 @@ "note": "Kernbereich — immer aktiv" }, "description": "Lege fest, was HealthLog für dich erfasst. Schalte einzelne Bereiche ein oder aus — die Navigation, Kacheln und Auswertungen passen sich sofort an." + }, + "gesundheitsakte": { + "title": "Gesundheitsakte", + "description": "Vollständige Gesundheitsakte als PDF, FHIR R4 oder kombiniertes ZIP-Paket exportieren." } }, "profile": "Profil", diff --git a/messages/en.json b/messages/en.json index 61f6d3588..6e6ad8c2e 100644 --- a/messages/en.json +++ b/messages/en.json @@ -4121,6 +4121,10 @@ "note": "Core area — always on" }, "description": "Choose what HealthLog tracks for you. Turn individual areas on or off — the navigation, tiles, and insights adapt right away." + }, + "gesundheitsakte": { + "title": "Health record", + "description": "Export your full health record as PDF, FHIR R4, or a combined ZIP package." } }, "profile": "Profile", diff --git a/messages/es.json b/messages/es.json index b9f3b6c4e..4e180cd70 100644 --- a/messages/es.json +++ b/messages/es.json @@ -4121,6 +4121,10 @@ "note": "Área esencial — siempre activa" }, "description": "Elige qué registra HealthLog por ti. Activa o desactiva áreas concretas: la navegación, los paneles y los análisis se adaptan al instante." + }, + "gesundheitsakte": { + "title": "Historial médico", + "description": "Exporta tu historial médico completo como PDF, FHIR R4 o un paquete ZIP combinado." } }, "profile": "Perfil", diff --git a/messages/fr.json b/messages/fr.json index 2014175bc..26773b240 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -4121,6 +4121,10 @@ "note": "Domaine essentiel — toujours actif" }, "description": "Choisissez ce que HealthLog suit pour vous. Activez ou désactivez chaque domaine : la navigation, les tuiles et les analyses s'adaptent aussitôt." + }, + "gesundheitsakte": { + "title": "Dossier médical", + "description": "Exportez votre dossier médical complet au format PDF, FHIR R4 ou en paquet ZIP combiné." } }, "profile": "Profil", diff --git a/messages/it.json b/messages/it.json index 9c9d306b4..1ea49f7cc 100644 --- a/messages/it.json +++ b/messages/it.json @@ -4121,6 +4121,10 @@ "note": "Ambito di base — sempre attivo" }, "description": "Scegli cosa registra HealthLog per te. Attiva o disattiva i singoli ambiti: navigazione, riquadri e analisi si adattano subito." + }, + "gesundheitsakte": { + "title": "Cartella clinica", + "description": "Esporta la tua cartella clinica completa in PDF, FHIR R4 o in un pacchetto ZIP combinato." } }, "profile": "Profilo", diff --git a/messages/pl.json b/messages/pl.json index d4144723f..d9572696e 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -4121,6 +4121,10 @@ "note": "Obszar podstawowy — zawsze aktywny" }, "description": "Wybierz, co HealthLog ma dla Ciebie śledzić. Włączaj i wyłączaj poszczególne obszary — nawigacja, kafelki i analizy dostosują się od razu." + }, + "gesundheitsakte": { + "title": "Dokumentacja zdrowotna", + "description": "Wyeksportuj pełną dokumentację zdrowotną jako PDF, FHIR R4 lub łączony pakiet ZIP." } }, "profile": "Profil", diff --git a/src/app/settings/[section]/page.tsx b/src/app/settings/[section]/page.tsx index bc1193242..d8a0c6faa 100644 --- a/src/app/settings/[section]/page.tsx +++ b/src/app/settings/[section]/page.tsx @@ -8,6 +8,7 @@ import { AiSection } from "@/components/settings/ai-section"; import { ApiSection } from "@/components/settings/api-section"; import { DashboardSection } from "@/components/settings/dashboard-section"; import { ExportSection } from "@/components/settings/export-section"; +import { GesundheitsakteSection } from "@/components/settings/gesundheitsakte-section"; import { InsightsSection } from "@/components/settings/insights-section"; import { IntegrationsSection } from "@/components/settings/integrations-section"; import { LayoutSection } from "@/components/settings/layout-section"; @@ -29,8 +30,7 @@ import { SettingsShell } from "@/components/settings/settings-shell"; /** * Dynamic settings section route. Each of the `SETTINGS_SECTION_SLUGS` - * (14 today — 10 nav-visible sections plus the four routable-but-hidden - * Layout child editors) is pre-rendered at build via + * is pre-rendered at build via * `generateStaticParams()` so the URLs are * statically known to Next.js, while the `dynamicParams = false` flag below * tells the router to 404 (instead of attempting on-demand rendering) for any @@ -61,6 +61,7 @@ const SECTION_COMPONENTS: Record< mood: MoodSection, thresholds: ThresholdsSection, api: ApiSection, + gesundheitsakte: GesundheitsakteSection, export: ExportSection, advanced: AdvancedSection, }; diff --git a/src/components/settings/__tests__/export-section.test.tsx b/src/components/settings/__tests__/export-section.test.tsx index fffd2e24f..264277863 100644 --- a/src/components/settings/__tests__/export-section.test.tsx +++ b/src/components/settings/__tests__/export-section.test.tsx @@ -2,7 +2,6 @@ * v1.4.16 Phase B7 — Settings → Export section. * * The consolidated Export page surfaces: - * - Health Record export panel (the page hero, v1.12) * - Measurements CSV * - Medications CSV (with optional intake-history toggle) * - Mood CSV @@ -12,10 +11,10 @@ * download/generate button. SSR-only smoke test — interaction is * exercised by the e2e suite. * - * The health-record export takes the page hero. The doctor-report PDF - * now lives under the health-record export and is no longer offered as - * a separate card here. The four CSV/JSON tiles stay under a "Weitere - * Export-Optionen" / "Other export options" sub-heading. Each component + * v1.18.0 (S5) — the full health-record export moved out to its own + * top-level "Gesundheitsakte" section; this page keeps only the generic + * CSV/JSON data-out paths under a "Weitere Export-Optionen" / "Other + * export options" sub-heading plus the import surface. Each component * owns its own contract test; this suite pins the page-level shape. */ import { describe, expect, it, vi } from "vitest"; @@ -77,12 +76,11 @@ describe(" — SSR smoke", () => { expect(html).not.toContain('data-testid="export-card-cycle"'); }); - it("mounts the health-record export panel as the page hero", () => { + it("no longer mounts the health-record export panel (moved to Gesundheitsakte)", () => { const html = render(); - // v1.12 — the health-record export owns the hero treatment. - expect(html).toContain('data-testid="health-record-export-panel"'); - expect(html).toContain("hero-gradient"); - expect(html).toContain("glow-purple"); + // v1.18.0 (S5) — the full health-record export lives in its own + // top-level Gesundheitsakte section now, not on this page. + expect(html).not.toContain('data-testid="health-record-export-panel"'); }); it("no longer mounts a separate doctor-report card", () => { @@ -130,7 +128,7 @@ describe(" — SSR smoke", () => { expect(buttonMatches?.length ?? 0).toBe(4); }); - it("German locale renders the DE heading + hero copy", () => { + it("German locale renders the DE heading + sub-heading copy", () => { const html = render(, "de"); expect(html).toContain("Export"); expect(html).toContain("Weitere Export-Optionen"); diff --git a/src/components/settings/__tests__/gesundheitsakte-section.test.tsx b/src/components/settings/__tests__/gesundheitsakte-section.test.tsx new file mode 100644 index 000000000..57ab31493 --- /dev/null +++ b/src/components/settings/__tests__/gesundheitsakte-section.test.tsx @@ -0,0 +1,54 @@ +/** + * v1.18.0 (S5) — Settings → Gesundheitsakte section. + * + * The full health-record export (PDF + FHIR R4 + zip package) lifted out + * of Export & Import into its own top-level home. SSR-only smoke test — + * interaction is exercised by the panel's own contract test + e2e. + */ +import { describe, expect, it, vi } from "vitest"; +import { renderToStaticMarkup } from "react-dom/server"; + +vi.mock("next/navigation", () => ({ + useRouter: () => ({ push: vi.fn(), replace: vi.fn() }), + usePathname: () => "/settings/gesundheitsakte", +})); + +vi.mock("@tanstack/react-query", () => ({ + useQuery: () => ({ data: null, isLoading: false, refetch: vi.fn() }), + useQueryClient: () => ({ invalidateQueries: vi.fn() }), + useMutation: () => ({ mutate: vi.fn(), isPending: false }), +})); + +vi.mock("@/hooks/use-auth", () => ({ + useAuth: () => ({ + user: { id: "u1", role: "USER" }, + isAuthenticated: true, + isLoading: false, + refetch: vi.fn(), + }), +})); + +import { I18nProvider } from "@/lib/i18n/context"; +import { GesundheitsakteSection } from "../gesundheitsakte-section"; + +function render(node: React.ReactElement, locale: "en" | "de" = "en") { + return renderToStaticMarkup( + {node}, + ); +} + +describe(" — SSR smoke", () => { + it("renders the section description and the health-record panel", () => { + const html = render(); + expect(html).toContain('id="settings-section-gesundheitsakte-title"'); + expect(html).toContain('data-testid="health-record-export-panel"'); + // Raw key never leaks past i18n. + expect(html).not.toContain("settings.sections.gesundheitsakte."); + }); + + it("German locale resolves without leaking the raw key", () => { + const html = render(, "de"); + expect(html).toContain('data-testid="health-record-export-panel"'); + expect(html).not.toContain("settings.sections.gesundheitsakte."); + }); +}); diff --git a/src/components/settings/__tests__/settings-shell.test.tsx b/src/components/settings/__tests__/settings-shell.test.tsx index d83647fc4..644e9969a 100644 --- a/src/components/settings/__tests__/settings-shell.test.tsx +++ b/src/components/settings/__tests__/settings-shell.test.tsx @@ -67,6 +67,8 @@ describe("SETTINGS_SECTION_SLUGS", () => { // 301-redirects to `/settings/notifications`. // v1.18.0 — `modules` ("Was du trackst") sits right after `account` as // the single front door for enabling/disabling secondary domains. + // v1.18.0 (S5) — `gesundheitsakte` (the full health-record export) lifts + // out of Export & Import into its own top-level entry, before `export`. expect([...SETTINGS_SECTION_SLUGS]).toEqual([ "account", "modules", @@ -80,6 +82,7 @@ describe("SETTINGS_SECTION_SLUGS", () => { "thresholds", "ai", "api", + "gesundheitsakte", "export", "advanced", "about", @@ -204,6 +207,9 @@ describe("", () => { // The ampersand is HTML-escaped by React SSR — assert on the encoded // form so we don't accidentally match a parser that double-escapes. expect(html).toContain("API & Tokens"); + // v1.18.0 (S5) — the health record is its own top-level entry. + expect(html).toContain('href="/settings/gesundheitsakte"'); + expect(html).toContain("Health record"); // v1.4.16 phase B7: the consolidated Export section is a top-level // entry in the sidebar; the link must be present in both locales. expect(html).toContain('href="/settings/export"'); diff --git a/src/components/settings/export-section.tsx b/src/components/settings/export-section.tsx index 6761a8daf..ea99c154e 100644 --- a/src/components/settings/export-section.tsx +++ b/src/components/settings/export-section.tsx @@ -9,18 +9,14 @@ * per export type, each with its own filter inputs and a clear * "Download" / "Generate" button. * - * 1. Health Record export — hero panel at the top of the page - * (PDF + FHIR R4 + zip package, see - * ``) - * 2. Measurements CSV — optional `since`/`until` - * 3. Medications CSV — optional intake-history toggle - * 4. Mood CSV — optional `since`/`until` - * 5. Full JSON Backup — single-file user-scoped dump + * 1. Measurements CSV — optional `since`/`until` + * 2. Medications CSV — optional intake-history toggle + * 3. Mood CSV — optional `since`/`until` + * 4. Full JSON Backup — single-file user-scoped dump * - * v1.12 — the health-record export is the headline "data out" path; it - * takes the page hero. The doctor-report PDF lives under the - * health-record export now and is no longer offered as a separate card - * here (it used to be a small secondary card at the bottom). + * v1.18.0 (S5) — the full health-record export (PDF + FHIR R4 + zip + * package) moved out to its own top-level "Gesundheitsakte" section. This + * page now keeps only the generic data export/import paths. * * Mobile-first: cards stack on `=md`. */ @@ -42,7 +38,6 @@ import { Button } from "@/components/ui/button"; import { DateInput } from "@/components/ui/date-input"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; -import { HealthRecordExportPanel } from "@/components/settings/health-record-export-panel"; import { ImportPanel } from "@/components/settings/import-panel"; import { queryKeys } from "@/lib/query-keys"; import { useTranslations } from "@/lib/i18n/context"; @@ -87,15 +82,9 @@ export function ExportSection() {

- {/* v1.12 — full health-record export is now the page hero. PDF + - FHIR R4 + a combined zip package, with grouped data-section - toggles (collapsed by default) and a format switch. This is the - headline "data out" path; it carries the primary visual weight. */} - - - {/* v1.12 — secondary export options. The four CSV/JSON - destinations live under a dedicated sub-heading. - Mobile: single column. Desktop (md+): 2-column grid. */} + {/* v1.18.0 (S5) — the full health-record export moved to its own + top-level "Gesundheitsakte" section. This page keeps the generic + CSV/JSON data-out paths and the import surface. */}
+
+

+ {t("settings.sections.gesundheitsakte.title")} +

+

+ {t("settings.sections.gesundheitsakte.description")} +

+
+ + +
+ ); +} diff --git a/src/components/settings/section-placeholder.tsx b/src/components/settings/section-placeholder.tsx index c09c9c459..977327aee 100644 --- a/src/components/settings/section-placeholder.tsx +++ b/src/components/settings/section-placeholder.tsx @@ -15,6 +15,7 @@ import { Bell, Blocks, Download, + FileHeart, Info, KeyRound, LayoutDashboard, @@ -46,6 +47,7 @@ const SLUG_ICON: Record = { thresholds: SlidersHorizontal, ai: Sparkles, api: KeyRound, + gesundheitsakte: FileHeart, export: Download, advanced: Settings2, about: Info, diff --git a/src/components/settings/section-slugs.ts b/src/components/settings/section-slugs.ts index 5be9037d5..e943bb479 100644 --- a/src/components/settings/section-slugs.ts +++ b/src/components/settings/section-slugs.ts @@ -31,6 +31,9 @@ // canonical editors; reminder TYPES now live centrally in `notifications`, // each row gated on its module. `/settings/reminders` 301-redirects to // `/settings/notifications` (next.config.ts). +// v1.18.0 (S5) — per-submodule settings split. `gesundheitsakte` (the full +// health-record export, always available) lifts out of Export & Import into +// its own top-level entry. export const SETTINGS_SECTION_SLUGS = [ "account", // v1.18.0 — `modules` ("Was du trackst") sits right after account: the @@ -46,6 +49,7 @@ export const SETTINGS_SECTION_SLUGS = [ "thresholds", "ai", "api", + "gesundheitsakte", "export", "advanced", "about", diff --git a/src/components/settings/settings-shell.tsx b/src/components/settings/settings-shell.tsx index 22a9a7e22..de7c0257b 100644 --- a/src/components/settings/settings-shell.tsx +++ b/src/components/settings/settings-shell.tsx @@ -21,6 +21,7 @@ import { Bell, Blocks, Download, + FileHeart, Info, KeyRound, LayoutDashboard, @@ -116,6 +117,13 @@ export const SETTINGS_SECTIONS: readonly SettingsSection[] = [ }, { slug: "ai", titleKey: "settings.sections.ai.title", icon: Sparkles }, { slug: "api", titleKey: "settings.sections.api.title", icon: KeyRound }, + // v1.18.0 (S5) — the full health record (PDF + FHIR R4 + zip package) + // earns its own home, lifted out of Export & Import. Always available. + { + slug: "gesundheitsakte", + titleKey: "settings.sections.gesundheitsakte.title", + icon: FileHeart, + }, { slug: "export", titleKey: "settings.sections.export.title",