From 537b31f4ac38de9e3825e2332e73880b96e7defd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Mon, 29 Jun 2026 15:47:39 +0200 Subject: [PATCH] feat(settings): fold delivery channels into Notifications, rename the Dashboard hub to Appearance Two surgical, additive, reversible changes to the flat settings navigation. Notifications becomes one surface with two sequenced, labelled groups: Reminders ("what you receive") first, then Delivery channels ("where it lands") = the existing channels panel, unchanged. The standalone Channels sidebar entry and slug are dropped; /settings/channels 301-redirects to /settings/notifications#channels (mirroring the reminders redirect). The per-channel anchors keep resolving. The mis-titled "Dashboard" area (slug kept as layout, so no route churn) is renamed Appearance / Darstellung and widened from two links to six: dashboard arrangement, insights overview and pills, plus deep-links to the view/sort/ order cards of medications, labs, the illness journal, and checkups. Each module keeps its view cards on its own page; the hub only indexes them. The checkups view card gains a vorsorge-view anchor so the deep-link lands. i18n updated across all six locales; settings and notifications tests track the new structure. --- messages/de.json | 39 ++++++--- messages/en.json | 39 ++++++--- messages/es.json | 39 ++++++--- messages/fr.json | 39 ++++++--- messages/it.json | 39 ++++++--- messages/pl.json | 39 ++++++--- next.config.ts | 11 +++ src/app/settings/[section]/page.tsx | 6 +- .../vorsorge-settings.tsx | 5 +- .../__tests__/layout-section.test.tsx | 37 ++++++--- .../settings/__tests__/sections.test.tsx | 12 ++- .../__tests__/settings-shell.test.tsx | 34 ++++---- src/components/settings/channels-section.tsx | 20 ----- src/components/settings/layout-section.tsx | 60 +++++++++++--- .../settings/notifications-section.tsx | 80 ++++++++++++++----- .../settings/section-placeholder.tsx | 2 - src/components/settings/section-slugs.ts | 7 +- src/components/settings/settings-shell.tsx | 27 +++---- 18 files changed, 347 insertions(+), 188 deletions(-) delete mode 100644 src/components/settings/channels-section.tsx diff --git a/messages/de.json b/messages/de.json index c97fb115e..012ec7cdf 100644 --- a/messages/de.json +++ b/messages/de.json @@ -4033,15 +4033,17 @@ }, "notifications": { "title": "Benachrichtigungen", - "description": "Alle Erinnerungen an einem Ort — was wann erinnert wird. Die Zustellkanäle liegen unter Integrationen.", - "channelsHint": "Telegram, ntfy, Web-Push, Webhook und E-Mail richtest du jetzt unter", - "channelsHintLink": "Integrationen → Kanäle", - "subtitle": "Wähle, welche Erinnerungen du erhältst." + "description": "Alle Erinnerungen an einem Ort — was wann erinnert wird und wohin es zugestellt wird.", + "subtitle": "Wähle, welche Erinnerungen du erhältst und wohin sie zugestellt werden.", + "remindersHeading": "Erinnerungen", + "remindersDescription": "Was du erhältst.", + "channelsHeading": "Zustellkanäle", + "channelsDescription": "Wohin Erinnerungen und Hinweise zugestellt werden." }, "layout": { - "title": "Dashboard", - "description": "Lege fest, wie Dashboard und Insights aufgebaut und angeordnet sind.", - "backToHub": "Zurück zu Layout & Personalisierung", + "title": "Darstellung", + "description": "Lege fest, wie Dashboard, Auswertungen und deine Tracking-Module dargestellt und angeordnet sind.", + "backToHub": "Zurück zu Darstellung", "dashboard": { "title": "Dashboard", "description": "Kachel-Layout und Reihenfolge." @@ -4050,7 +4052,23 @@ "title": "Insights", "description": "Reihenfolge der Übersicht und der Navigations-Pills." }, - "subtitle": "Passe an, wie Dashboard und Insights angeordnet sind." + "subtitle": "Lege fest, wie Dashboard, Auswertungen und Tracking-Module dargestellt werden.", + "medications": { + "title": "Medikamente", + "description": "Listenansicht und manuelle Reihenfolge." + }, + "labs": { + "title": "Labor", + "description": "Ansicht, Sortierung und manuelle Reihenfolge." + }, + "illness": { + "title": "Krankheitstagebuch", + "description": "Listenansicht und Reihenfolge der Episoden." + }, + "vorsorge": { + "title": "Vorsorge", + "description": "Listenansicht und Reihenfolge der Erinnerungen." + } }, "dashboard": { "title": "Dashboard", @@ -4308,11 +4326,6 @@ "description": "Coach aktivieren, Ton und Umfang, gespeicherte Erinnerungen.", "subtitle": "Stelle den Coach, seine proaktiven Hinweise und sein Gedächtnis ein." }, - "channels": { - "title": "Kanäle", - "description": "Zustellung über Telegram, ntfy, Web-Push, Webhook und E-Mail.", - "subtitle": "Wohin Erinnerungen und Hinweise zugestellt werden." - }, "security": { "title": "Sicherheit", "subtitle": "Zwei-Faktor-Authentifizierung, Wiederherstellungscodes und Passkeys." diff --git a/messages/en.json b/messages/en.json index 3f2844f28..d9d3f6f76 100644 --- a/messages/en.json +++ b/messages/en.json @@ -4033,15 +4033,17 @@ }, "notifications": { "title": "Notifications", - "description": "Every reminder in one place — what gets reminded and when. The delivery channels live under Integrations.", - "channelsHint": "Telegram, ntfy, Web Push, Webhook, and Email are now set up under", - "channelsHintLink": "Integrations → Channels", - "subtitle": "Choose which reminders you receive." + "description": "Every reminder in one place — what gets reminded and when, and where it's delivered.", + "subtitle": "Choose which reminders you receive and where they're delivered.", + "remindersHeading": "Reminders", + "remindersDescription": "What you receive.", + "channelsHeading": "Delivery channels", + "channelsDescription": "Where reminders and alerts are delivered." }, "layout": { - "title": "Dashboard", - "description": "Arrange how the dashboard and insights are laid out.", - "backToHub": "Back to Layout & Personalization", + "title": "Appearance", + "description": "Choose how the dashboard, insights, and your tracking modules look and are arranged.", + "backToHub": "Back to Appearance", "dashboard": { "title": "Dashboard", "description": "Tile layout and order." @@ -4050,7 +4052,23 @@ "title": "Insights", "description": "Overview sections and navigation-pill order." }, - "subtitle": "Personalize how your dashboard and insights are arranged." + "subtitle": "Personalize how your dashboard, insights, and tracking modules look.", + "medications": { + "title": "Medications", + "description": "List view and manual order." + }, + "labs": { + "title": "Labs", + "description": "View, sort, and manual order." + }, + "illness": { + "title": "Illness journal", + "description": "List view and episode order." + }, + "vorsorge": { + "title": "Checkups", + "description": "List view and reminder order." + } }, "dashboard": { "title": "Dashboard", @@ -4308,11 +4326,6 @@ "description": "Enable the Coach, tone and scope, stored memories.", "subtitle": "Tune the Coach, its proactive nudges, and its memory." }, - "channels": { - "title": "Channels", - "description": "Telegram, ntfy, Web Push, Webhook, and Email delivery.", - "subtitle": "Where reminders and alerts are delivered." - }, "security": { "title": "Security", "subtitle": "Two-factor authentication, recovery codes, and passkeys." diff --git a/messages/es.json b/messages/es.json index 98aee4b3d..58da7252e 100644 --- a/messages/es.json +++ b/messages/es.json @@ -4033,15 +4033,17 @@ }, "notifications": { "title": "Notificaciones", - "description": "Todos los recordatorios en un solo lugar: qué se recuerda y cuándo. Los canales de entrega están en Integraciones.", - "channelsHint": "Telegram, ntfy, Web Push, Webhook y correo se configuran ahora en", - "channelsHintLink": "Integraciones → Canales", - "subtitle": "Elige qué recordatorios recibes." + "description": "Todos los recordatorios en un solo lugar: qué se recuerda y cuándo, y dónde se entrega.", + "subtitle": "Elige qué recordatorios recibes y dónde se entregan.", + "remindersHeading": "Recordatorios", + "remindersDescription": "Lo que recibes.", + "channelsHeading": "Canales de entrega", + "channelsDescription": "Dónde se entregan los recordatorios y avisos." }, "layout": { - "title": "Panel", - "description": "Organiza cómo están dispuestos el panel y los análisis.", - "backToHub": "Volver a Diseño y personalización", + "title": "Apariencia", + "description": "Elige cómo se ven y se organizan el panel, los análisis y tus módulos de seguimiento.", + "backToHub": "Volver a Apariencia", "dashboard": { "title": "Panel", "description": "Diseño y orden de los mosaicos." @@ -4050,7 +4052,23 @@ "title": "Análisis", "description": "Orden de la vista general y de las pestañas de navegación." }, - "subtitle": "Personaliza cómo se organizan tu panel e insights." + "subtitle": "Personaliza cómo se ven tu panel, los análisis y los módulos de seguimiento.", + "medications": { + "title": "Medicamentos", + "description": "Vista de lista y orden manual." + }, + "labs": { + "title": "Laboratorio", + "description": "Vista, orden y orden manual." + }, + "illness": { + "title": "Diario de enfermedades", + "description": "Vista de lista y orden de episodios." + }, + "vorsorge": { + "title": "Revisiones", + "description": "Vista de lista y orden de recordatorios." + } }, "dashboard": { "title": "Panel", @@ -4308,11 +4326,6 @@ "description": "Activa el Coach, tono y alcance, recuerdos guardados.", "subtitle": "Ajusta el Coach, sus avisos proactivos y su memoria." }, - "channels": { - "title": "Canales", - "description": "Envío por Telegram, ntfy, Web Push, Webhook y correo electrónico.", - "subtitle": "Dónde se entregan los recordatorios y avisos." - }, "security": { "title": "Seguridad", "subtitle": "Autenticación de dos factores, códigos de recuperación y claves de acceso." diff --git a/messages/fr.json b/messages/fr.json index 80c0be5fe..54a2ddd4d 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -4033,15 +4033,17 @@ }, "notifications": { "title": "Notifications", - "description": "Tous les rappels au même endroit — quoi et quand. Les canaux de diffusion se trouvent dans Intégrations.", - "channelsHint": "Telegram, ntfy, Web Push, Webhook et e-mail se configurent désormais dans", - "channelsHintLink": "Intégrations → Canaux", - "subtitle": "Choisissez les rappels que vous recevez." + "description": "Tous les rappels au même endroit — quoi et quand, et où c'est envoyé.", + "subtitle": "Choisissez les rappels que vous recevez et où ils sont envoyés.", + "remindersHeading": "Rappels", + "remindersDescription": "Ce que vous recevez.", + "channelsHeading": "Canaux de diffusion", + "channelsDescription": "Où sont envoyés les rappels et alertes." }, "layout": { - "title": "Tableau de bord", - "description": "Organisez la disposition du tableau de bord et des analyses.", - "backToHub": "Retour à Disposition et personnalisation", + "title": "Apparence", + "description": "Choisissez l'apparence et l'agencement du tableau de bord, des analyses et de vos modules de suivi.", + "backToHub": "Retour à Apparence", "dashboard": { "title": "Tableau de bord", "description": "Disposition et ordre des tuiles." @@ -4050,7 +4052,23 @@ "title": "Analyses", "description": "Ordre de la vue d'ensemble et des onglets de navigation." }, - "subtitle": "Personnalisez l'agencement de votre tableau de bord et de vos insights." + "subtitle": "Personnalisez l'apparence du tableau de bord, des analyses et des modules de suivi.", + "medications": { + "title": "Médicaments", + "description": "Vue en liste et ordre manuel." + }, + "labs": { + "title": "Analyses de labo", + "description": "Vue, tri et ordre manuel." + }, + "illness": { + "title": "Journal des maladies", + "description": "Vue en liste et ordre des épisodes." + }, + "vorsorge": { + "title": "Examens", + "description": "Vue en liste et ordre des rappels." + } }, "dashboard": { "title": "Tableau de bord", @@ -4308,11 +4326,6 @@ "description": "Activez le Coach, le ton et la portée, les souvenirs enregistrés.", "subtitle": "Réglez le Coach, ses suggestions proactives et sa mémoire." }, - "channels": { - "title": "Canaux", - "description": "Distribution via Telegram, ntfy, Web Push, Webhook et e-mail.", - "subtitle": "Où sont envoyés les rappels et alertes." - }, "security": { "title": "Sécurité", "subtitle": "Authentification à deux facteurs, codes de récupération et clés d'accès." diff --git a/messages/it.json b/messages/it.json index 03a3fa8ed..81dbc2a7a 100644 --- a/messages/it.json +++ b/messages/it.json @@ -4033,15 +4033,17 @@ }, "notifications": { "title": "Notifiche", - "description": "Tutti i promemoria in un unico posto — cosa e quando. I canali di recapito si trovano in Integrazioni.", - "channelsHint": "Telegram, ntfy, Web Push, Webhook ed e-mail ora si configurano in", - "channelsHintLink": "Integrazioni → Canali", - "subtitle": "Scegli quali promemoria ricevere." + "description": "Tutti i promemoria in un unico posto — cosa e quando, e dove vengono recapitati.", + "subtitle": "Scegli quali promemoria ricevere e dove vengono recapitati.", + "remindersHeading": "Promemoria", + "remindersDescription": "Ciò che ricevi.", + "channelsHeading": "Canali di recapito", + "channelsDescription": "Dove vengono recapitati promemoria e avvisi." }, "layout": { - "title": "Cruscotto", - "description": "Organizza la disposizione del cruscotto e degli approfondimenti.", - "backToHub": "Torna a Layout e personalizzazione", + "title": "Aspetto", + "description": "Scegli l'aspetto e la disposizione di dashboard, approfondimenti e dei tuoi moduli di monitoraggio.", + "backToHub": "Torna ad Aspetto", "dashboard": { "title": "Cruscotto", "description": "Layout e ordine dei riquadri." @@ -4050,7 +4052,23 @@ "title": "Approfondimenti", "description": "Ordine della panoramica e delle schede di navigazione." }, - "subtitle": "Personalizza la disposizione di dashboard e insights." + "subtitle": "Personalizza l'aspetto di dashboard, approfondimenti e moduli di monitoraggio.", + "medications": { + "title": "Farmaci", + "description": "Vista elenco e ordine manuale." + }, + "labs": { + "title": "Analisi di laboratorio", + "description": "Vista, ordinamento e ordine manuale." + }, + "illness": { + "title": "Diario delle malattie", + "description": "Vista elenco e ordine degli episodi." + }, + "vorsorge": { + "title": "Controlli", + "description": "Vista elenco e ordine dei promemoria." + } }, "dashboard": { "title": "Cruscotto", @@ -4308,11 +4326,6 @@ "description": "Attiva il Coach, tono e ambito, ricordi salvati.", "subtitle": "Regola il Coach, i suoi suggerimenti proattivi e la sua memoria." }, - "channels": { - "title": "Canali", - "description": "Recapito tramite Telegram, ntfy, Web Push, Webhook ed e-mail.", - "subtitle": "Dove vengono recapitati promemoria e avvisi." - }, "security": { "title": "Sicurezza", "subtitle": "Autenticazione a due fattori, codici di recupero e passkey." diff --git a/messages/pl.json b/messages/pl.json index 4b2ba446c..664cc2d9e 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -4033,15 +4033,17 @@ }, "notifications": { "title": "Powiadomienia", - "description": "Wszystkie przypomnienia w jednym miejscu — co i kiedy. Kanały dostarczania znajdują się w Integracjach.", - "channelsHint": "Telegram, ntfy, Web Push, Webhook i e-mail konfigurujesz teraz w", - "channelsHintLink": "Integracje → Kanały", - "subtitle": "Wybierz, które przypomnienia otrzymujesz." + "description": "Wszystkie przypomnienia w jednym miejscu — co i kiedy oraz gdzie są dostarczane.", + "subtitle": "Wybierz, które przypomnienia otrzymujesz i gdzie są dostarczane.", + "remindersHeading": "Przypomnienia", + "remindersDescription": "Co otrzymujesz.", + "channelsHeading": "Kanały dostarczania", + "channelsDescription": "Gdzie dostarczane są przypomnienia i powiadomienia." }, "layout": { - "title": "Pulpit", - "description": "Ustal, jak rozłożone są pulpit i analizy.", - "backToHub": "Powrót do Układ i personalizacja", + "title": "Wygląd", + "description": "Ustal, jak wyglądają i są rozłożone pulpit, analizy i Twoje moduły śledzenia.", + "backToHub": "Powrót do Wygląd", "dashboard": { "title": "Pulpit", "description": "Układ i kolejność kafelków." @@ -4050,7 +4052,23 @@ "title": "Analizy", "description": "Kolejność przeglądu i pigułek nawigacyjnych." }, - "subtitle": "Dostosuj układ pulpitu i analiz." + "subtitle": "Dostosuj wygląd pulpitu, analiz i modułów śledzenia.", + "medications": { + "title": "Leki", + "description": "Widok listy i ręczna kolejność." + }, + "labs": { + "title": "Laboratorium", + "description": "Widok, sortowanie i ręczna kolejność." + }, + "illness": { + "title": "Dziennik chorób", + "description": "Widok listy i kolejność epizodów." + }, + "vorsorge": { + "title": "Badania kontrolne", + "description": "Widok listy i kolejność przypomnień." + } }, "dashboard": { "title": "Pulpit", @@ -4308,11 +4326,6 @@ "description": "Włącz Coacha, ton i zakres, zapisane wspomnienia.", "subtitle": "Dostosuj Coacha, jego proaktywne podpowiedzi i pamięć." }, - "channels": { - "title": "Kanały", - "description": "Dostarczanie przez Telegram, ntfy, Web Push, Webhook i e-mail.", - "subtitle": "Gdzie dostarczane są przypomnienia i powiadomienia." - }, "security": { "title": "Bezpieczeństwo", "subtitle": "Uwierzytelnianie dwuskładnikowe, kody odzyskiwania i klucze dostępu." diff --git a/next.config.ts b/next.config.ts index 2c6e0c4ed..219619fea 100644 --- a/next.config.ts +++ b/next.config.ts @@ -134,6 +134,17 @@ const nextConfig: NextConfig = { destination: "/settings/notifications", permanent: true, }, + // v1.25.3 — the standalone delivery-channels page folded into + // Notifications as an in-page group. The channels content now lives + // under the `#channels` anchor on the Notifications surface, so the old + // URL 301-redirects there (mirroring the `/settings/reminders` precedent + // above). The per-channel anchors (`#telegram`, `#ntfy`, …) inside the + // panel keep resolving. + { + source: "/settings/channels", + destination: "/settings/notifications#channels", + permanent: true, + }, // v1.22.0 — the preventive-care surface moved from the German slug // `/vorsorge` to the English `/checkups` (the feature reads "Checkups" // in every non-German locale now). 301-redirect so bookmarks, the PWA's diff --git a/src/app/settings/[section]/page.tsx b/src/app/settings/[section]/page.tsx index 3b8d0b82a..9042a54b6 100644 --- a/src/app/settings/[section]/page.tsx +++ b/src/app/settings/[section]/page.tsx @@ -26,9 +26,8 @@ import { NotificationsSection } from "@/components/settings/notifications-sectio import { PrivacySection } from "@/components/settings/privacy-section"; import { SectionPlaceholder } from "@/components/settings/section-placeholder"; import { VorsorgeSection } from "@/components/settings/vorsorge-section"; -// v1.18.1 (D4) — `channels` and `sources` are standalone left-side entries -// again (split out of the Integrations sub-tabs). -import { ChannelsSection } from "@/components/settings/channels-section"; +// v1.18.1 (D4) — `sources` is a standalone left-side entry (split out of the +// Integrations sub-tabs). v1.25.3 — `channels` folded into Notifications. import { SharingSection } from "@/components/settings/sharing-section"; import { SourcesSection } from "@/components/settings/sources-section"; import { ThresholdsSection } from "@/components/settings/thresholds-section"; @@ -66,7 +65,6 @@ const SECTION_COMPONENTS: Record< ai: AiSection, coach: CoachSection, integrations: IntegrationsSection, - channels: ChannelsSection, sources: SourcesSection, notifications: NotificationsSection, layout: LayoutSection, diff --git a/src/components/measurement-reminders/vorsorge-settings.tsx b/src/components/measurement-reminders/vorsorge-settings.tsx index 979394b31..b87a9d9ca 100644 --- a/src/components/measurement-reminders/vorsorge-settings.tsx +++ b/src/components/measurement-reminders/vorsorge-settings.tsx @@ -63,7 +63,10 @@ export function VorsorgeSettings() { // `
` blocks with `text-sm` headings. return (
- + {/* v1.25.3 — `id="vorsorge-view"` + `scroll-mt-28` so the Appearance hub + deep-link (`/settings/vorsorge#vorsorge-view`) lands on this card + below the sticky page header, mirroring the labs/illness anchors. */} + ", () => { - it("links to the arrangement editors it still hosts", () => { + it("links to every view surface it indexes (anchor deep-links)", () => { const html = render(); - for (const href of ["/settings/dashboard", "/settings/insights"]) { + for (const href of [ + "/settings/dashboard", + "/settings/insights", + "/settings/medications#medications-view", + "/settings/labs#labs-view", + "/settings/illness#illness-view", + "/settings/vorsorge#vorsorge-view", + ]) { expect(html).toContain(`href="${href}"`); } - // v1.18.0 (S5) — Medications (Medikamente) and Mood (Stimmung) - // graduated to their own nav entries and are no longer linked here. - expect(html).not.toContain('href="/settings/medications"'); - expect(html).not.toContain('href="/settings/mood"'); }); - it("renders the hub heading and a card per editor", () => { + it("renders one card per indexed surface (6 links)", () => { const html = render(); + // Pin the link count: every hub card is an ``. + const links = html.match(/href="\/settings\//g) ?? []; + expect(links.length).toBe(6); expect(html).toContain("Dashboard"); expect(html).toContain("Insights"); }); it("resolves its copy in German too", () => { const html = render("de"); - expect(html).toContain("Layout"); + // The hub body is the link list; the German module labels resolve. + expect(html).toContain("Medikamente"); + expect(html).toContain("Labor"); expect(html).not.toContain("settings.sections.layout."); }); }); diff --git a/src/components/settings/__tests__/sections.test.tsx b/src/components/settings/__tests__/sections.test.tsx index 5744b4ef7..183ef2bf7 100644 --- a/src/components/settings/__tests__/sections.test.tsx +++ b/src/components/settings/__tests__/sections.test.tsx @@ -188,19 +188,25 @@ describe("settings sections — SSR smoke", () => { expect(html).toContain("Withings"); }); - it(" renders the single reminder-types home", () => { + it(" renders the reminders + delivery-channels groups", () => { const html = renderFramed("notifications", ); // v1.18.6 (W9) — the visible heading comes from the frame; the proactive // Coach nudge moved to Settings → Coach, so it no longer renders here. expect(html).toContain("settings-section-notifications-title"); - expect(html).not.toContain("Notification channels"); expect(html).not.toContain('data-slot="notifications-channels-cross-link"'); expect(html).not.toContain('data-slot="notifications-inbox-cross-link"'); // The mood card fails OPEN (mock user carries no module map), so both - // remaining reminder-type cards render. The coach-nudge card is gone. + // reminder-type cards render under the Reminders group. The coach-nudge + // card is gone. expect(html).toContain('id="mood-reminder"'); expect(html).toContain('id="low-stock"'); expect(html).not.toContain('id="coach-nudge"'); + // v1.25.3 — the delivery-channels group is now part of this surface: the + // `#channels` anchor and the embedded channels panel both render. + expect(html).toContain('id="channels"'); + expect(html).toContain('data-slot="notification-channels-panel"'); + expect(html).toContain("Reminders"); + expect(html).toContain("Delivery channels"); // No raw i18n key leaks past the provider. expect(html).not.toContain("settings.sections.notifications."); }); diff --git a/src/components/settings/__tests__/settings-shell.test.tsx b/src/components/settings/__tests__/settings-shell.test.tsx index 004d13aed..e2142a1ec 100644 --- a/src/components/settings/__tests__/settings-shell.test.tsx +++ b/src/components/settings/__tests__/settings-shell.test.tsx @@ -91,6 +91,9 @@ describe("SETTINGS_SECTION_SLUGS", () => { // sits right after `ai`. // v1.18.1 (D4) — `channels` + `sources` split out of the Integrations // sub-tabs into their own left-side entries, right after `integrations`. + // v1.25.3 — `channels` folded back into Notifications as an in-page group; + // `/settings/channels` 301-redirects to `/settings/notifications#channels`. + // `sources` keeps its own entry. // v1.18.7 — `labs`, `illness`, `vorsorge` move into the shell as // first-class sections, right after `mood` (the three were standalone // `ModuleSettingsFrame` pages). @@ -102,7 +105,6 @@ describe("SETTINGS_SECTION_SLUGS", () => { "security", "modules", "integrations", - "channels", "sources", "notifications", "layout", @@ -293,14 +295,15 @@ describe("", () => { // (single-line; the longer "Notification channels" wrapped). The // `/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 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. + // v1.17.1 (F-2) — the view/arrangement editors are reached through one hub + // entry (route stays `/settings/layout`). v1.25.3 — the hub is renamed + // "Appearance" (DE "Darstellung") and indexes every module's view surface; + // the route is unchanged. expect(html).toContain('href="/settings/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(">Appearance"); + // v1.25.3 — Channels folded into Notifications, so it is no longer a + // left-side entry; Sources keeps its own. + expect(html).not.toContain('href="/settings/channels"'); expect(html).toContain('href="/settings/sources"'); // v1.18.0 (S5) — Medications (Medikamente) and Mood (Stimmung, gated; // fail-open mock shows both) are their own nav entries now. @@ -334,15 +337,14 @@ 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 hub - // entry (route stays `/settings/layout`). v1.19.1 (S3) — the nav label - // reads "Dashboard" in every locale to match where it leads. + // v1.17.1 (F-2) — the view editors are reached through one hub entry + // (route stays `/settings/layout`). v1.25.3 — the German nav label reads + // "Darstellung" (Appearance) to match the renamed hub. expect(html).toContain('href="/settings/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"'); - expect(html).toContain("Kanäle"); + expect(html).toContain(">Darstellung"); + // v1.25.3 — Channels ("Kanäle") folded into Notifications; Sources keeps + // its own left-side entry. + expect(html).not.toContain('href="/settings/channels"'); // v1.18.0 (S5) — Medications + Mood are their own nav entries now. expect(html).toContain('href="/settings/medications"'); expect(html).toContain('href="/settings/mood"'); diff --git a/src/components/settings/channels-section.tsx b/src/components/settings/channels-section.tsx deleted file mode 100644 index ebceea87e..000000000 --- a/src/components/settings/channels-section.tsx +++ /dev/null @@ -1,20 +0,0 @@ -"use client"; - -/** - * `` — Settings → Kanäle (notification delivery channels). - * - * v1.18.1 (D4) — split out of the Integrations sub-tabs into its own left-side - * entry. A channel (Telegram / ntfy / Web Push / Webhook / Email) is a - * delivery provider — the same family as a connected device — and now reads as - * a first-class settings home rather than a tab hidden behind Integrationen. - * - * The content is the existing ``. v1.18.6 (W9) — - * the visible heading + subtitle now come from the shared - * `` in the route; this wrapper is the panel only. - */ - -import { NotificationChannelsPanel } from "@/components/settings/integrations/notification-channels-panel"; - -export function ChannelsSection() { - return ; -} diff --git a/src/components/settings/layout-section.tsx b/src/components/settings/layout-section.tsx index 7605fcdc4..1eb01308c 100644 --- a/src/components/settings/layout-section.tsx +++ b/src/components/settings/layout-section.tsx @@ -2,8 +2,12 @@ import Link from "next/link"; import { + CalendarCheck, ChevronRight, + FlaskConical, LayoutDashboard, + Pill, + Thermometer, TrendingUp, type LucideIcon, } from "lucide-react"; @@ -12,17 +16,23 @@ import { useTranslations } from "@/lib/i18n/context"; import { SettingsCard } from "./settings-card"; /** - * v1.17.1 (F-2) — the one "Layout & Personalization" home. + * v1.17.1 (F-2) — the "Appearance" home (slug stays `layout`). * - * Before this section, "how my app is laid out" was scattered across - * several unrelated settings sections with no shared framing. This - * section is the single front door for the arrangement editors: it - * presents them as one consistent set of cards and links to each editor, - * which keep their own routes and mutation flows intact. + * "How my app looks and is arranged" was scattered across several unrelated + * settings sections with no shared framing. This section is the single front + * door: it presents the view/arrangement surfaces as one consistent set of + * cards and links to each editor, which keep their own routes, their own + * cards, and their mutation flows intact — the hub indexes them, it does not + * own them. * - * v1.18.0 (S5) — Medications (Medikamente) and Mood (Stimmung) graduated - * to their own top-level nav entries, so the hub now hosts only the - * dashboard + insights arrangement editors. + * v1.25.3 — the hub widens from the dashboard + insights arrangement editors + * to the full set of view surfaces: it now also deep-links to the view/sort/ + * order cards of medications, labs, the illness journal, and checkups. Each + * of those modules keeps its view cards on its own settings page; the links + * land on the existing card anchors (`#medications-view`, `#labs-view`, + * `#illness-view`, `#vorsorge-view`). Data/behaviour settings (biomarker + * catalog, local OCR, injection-site exclusions, per-reminder toggles, the + * mood tag/group catalog) stay on their module pages and are NOT indexed here. */ interface LayoutLink { href: string; @@ -44,10 +54,34 @@ const LAYOUT_LINKS: ReadonlyArray = [ titleKey: "settings.sections.layout.insights.title", descriptionKey: "settings.sections.layout.insights.description", }, - // v1.18.0 (S5) — Medications (Medikamente) and Mood (Stimmung) graduated - // to their own top-level nav entries, so they are no longer linked from - // the Layout hub. Only the dashboard + insights arrangement editors - // remain here. + // v1.25.3 — view/sort/order surfaces of the tracking modules. Each link + // deep-links to the module's existing view card anchor; the cards stay on + // their own settings page. The module routes resolve regardless of the + // module's enabled state, so the link never dead-ends. + { + href: "/settings/medications#medications-view", + icon: Pill, + titleKey: "settings.sections.layout.medications.title", + descriptionKey: "settings.sections.layout.medications.description", + }, + { + href: "/settings/labs#labs-view", + icon: FlaskConical, + titleKey: "settings.sections.layout.labs.title", + descriptionKey: "settings.sections.layout.labs.description", + }, + { + href: "/settings/illness#illness-view", + icon: Thermometer, + titleKey: "settings.sections.layout.illness.title", + descriptionKey: "settings.sections.layout.illness.description", + }, + { + href: "/settings/vorsorge#vorsorge-view", + icon: CalendarCheck, + titleKey: "settings.sections.layout.vorsorge.title", + descriptionKey: "settings.sections.layout.vorsorge.description", + }, ]; export function LayoutSection() { diff --git a/src/components/settings/notifications-section.tsx b/src/components/settings/notifications-section.tsx index edeeb8df3..28bb7fb1a 100644 --- a/src/components/settings/notifications-section.tsx +++ b/src/components/settings/notifications-section.tsx @@ -1,8 +1,10 @@ "use client"; import { useAuth } from "@/hooks/use-auth"; +import { useTranslations } from "@/lib/i18n/context"; import { LowStockCard } from "@/components/settings/low-stock-card"; import { MoodReminderCard } from "@/components/settings/mood-reminder-card"; +import { NotificationChannelsPanel } from "@/components/settings/integrations/notification-channels-panel"; /** * `` — Settings → Notifications ("Benachrichtigungen"). @@ -19,41 +21,77 @@ import { MoodReminderCard } from "@/components/settings/mood-reminder-card"; * (a toggleable fail-open module since D3, so a missing key reads as enabled * and a stale `/me` payload never blanks a card). * - * v1.18.1 (D5) — the page is intentionally lean: the section blurb, the - * channels / inbox cross-links, and the embedded Vorsorge (preventive-care) - * editor were all removed. Vorsorge has its own `/vorsorge` page, so embedding - * it here was a duplicate. Delivery CHANNELS (Telegram / ntfy / Web Push / - * Webhook / Email) live under Settings → Integrations → Channels. + * v1.25.3 — the standalone "Channels" entry folds back in here. The page is + * now one Notifications surface with two sequential, labelled groups, ordered + * the way a user reasons about it: **Reminders** ("what you receive") first, + * then **Delivery channels** ("where it's delivered") = the existing + * ``. This is two distinct-but-related concepts + * sequenced under one roof, not one concept split top/bottom — the + * channels group carries `id="channels"` so `/settings/channels` can + * 301-redirect to `/settings/notifications#channels`, and the per-channel + * anchors (`#telegram`, `#ntfy`, …) inside the panel keep working. * - * v1.18.6 (W9) — the visible heading + subtitle now come from the shared - * ``. The proactive-Coach nudge card moved to Settings → - * Coach (it is a Coach setting, not a generic notification), so this screen - * carries the mood + low-stock reminder cards only. + * v1.18.6 (W9) — the page heading + subtitle come from the shared + * ``. The proactive-Coach nudge card lives under + * Settings → Coach (it is a Coach setting, not a generic notification). */ export function NotificationsSection() { const { isAuthenticated, user } = useAuth(); + const { t } = useTranslations(); // v1.18.0 (S4) — module-gated per-type visibility. Fail OPEN (`!== false`) // so a stale `/me` payload without the module map keeps every card visible. const moodEnabled = user?.modules?.mood !== false; const medsEnabled = user?.modules?.medications !== false; + const anyReminder = moodEnabled || medsEnabled; return ( -
- {/* Mood check-in reminder — only when the mood module is enabled. */} - {moodEnabled ? ( -
- -
+
+ {/* Group 1 — Reminders ("what you receive"). Rendered only when at + least one reminder-type card is visible, so a fully-disabled + account never shows an empty heading. */} + {anyReminder ? ( +
+
+

+ {t("settings.sections.notifications.remindersHeading")} +

+

+ {t("settings.sections.notifications.remindersDescription")} +

+
+ + {/* Mood check-in reminder — only when the mood module is enabled. */} + {moodEnabled ? ( +
+ +
+ ) : null} + + {/* Medication low-stock runway — only when the medications module + is enabled (toggleable, fail-open, since D3). */} + {medsEnabled ? ( +
+ +
+ ) : null} +
) : null} - {/* Medication low-stock runway — only when the medications module is - enabled (toggleable, fail-open, since D3). */} - {medsEnabled ? ( -
- + {/* Group 2 — Delivery channels ("where it's delivered"). The existing + channels panel, unchanged. `id="channels"` anchors the group for the + `/settings/channels` redirect. */} +
+
+

+ {t("settings.sections.notifications.channelsHeading")} +

+

+ {t("settings.sections.notifications.channelsDescription")} +

- ) : null} + +
); } diff --git a/src/components/settings/section-placeholder.tsx b/src/components/settings/section-placeholder.tsx index 8b6b05c6b..ccac49cd3 100644 --- a/src/components/settings/section-placeholder.tsx +++ b/src/components/settings/section-placeholder.tsx @@ -29,7 +29,6 @@ import { Link2, Lock, Pill, - Radio, Settings2, Share2, ShieldCheck, @@ -51,7 +50,6 @@ const SLUG_ICON: Record = { security: ShieldCheck, modules: Blocks, integrations: Link2, - channels: Radio, sources: Layers, notifications: Bell, layout: LayoutDashboard, diff --git a/src/components/settings/section-slugs.ts b/src/components/settings/section-slugs.ts index 8483253ef..576964410 100644 --- a/src/components/settings/section-slugs.ts +++ b/src/components/settings/section-slugs.ts @@ -57,9 +57,10 @@ export const SETTINGS_SECTION_SLUGS = [ // single front door for enabling/disabling secondary tracking domains. "modules", "integrations", - // v1.18.1 (D4) — Kanäle (delivery channels) + Quellen (source weighting) - // split out of the Integrations tabs into their own left-side entries. - "channels", + // v1.18.1 (D4) — Quellen (source weighting) split out of the Integrations + // tabs into its own left-side entry. v1.25.3 — Kanäle (delivery channels) + // folded back into Notifications as an in-page group; `/settings/channels` + // 301-redirects to `/settings/notifications#channels` (next.config.ts). "sources", "notifications", "layout", diff --git a/src/components/settings/settings-shell.tsx b/src/components/settings/settings-shell.tsx index 37bc06cfc..cd20feaa5 100644 --- a/src/components/settings/settings-shell.tsx +++ b/src/components/settings/settings-shell.tsx @@ -30,12 +30,11 @@ import { Info, KeyRound, Plug, - LayoutDashboard, Layers, Link2, Lock, + Palette, Pill, - Radio, Settings2, Share2, ShieldCheck, @@ -123,13 +122,9 @@ export const SETTINGS_SECTIONS: readonly SettingsSection[] = [ titleKey: "settings.sections.integrations.title", icon: Link2, }, - // v1.18.1 (D4) — Kanäle (delivery channels) split out of the Integrations - // sub-tabs into its own entry. A channel is a delivery provider. - { - slug: "channels", - titleKey: "settings.sections.channels.title", - icon: Radio, - }, + // v1.25.3 — the standalone Kanäle (delivery channels) entry folded back + // into Notifications as a labelled in-page group; `/settings/channels` + // 301-redirects to `/settings/notifications#channels`. // v1.18.1 (D4) — Quellen (source weighting) split out into its own entry: // which connection wins when two report the same metric. { @@ -145,15 +140,17 @@ export const SETTINGS_SECTIONS: readonly SettingsSection[] = [ titleKey: "settings.sections.notifications.title", icon: Bell, }, - // v1.17.1 (F-2) — one "Layout & Personalization" nav entry replaces the - // four scattered Dashboard / Insights / Medications / Mood "arrange" - // entries. Those routes still resolve (deep links, page-header cogs, and - // the hub's own links all work); they are simply reached through the - // Layout hub now, so "how my app is laid out" reads as one home. + // v1.17.1 (F-2) — one nav entry replaces the four scattered Dashboard / + // Insights / Medications / Mood "arrange" entries. Those routes still + // resolve (deep links, page-header cogs, and the hub's own links all work); + // they are reached through this hub now, so "how my app looks" reads as one + // home. v1.25.3 — renamed to "Appearance"/"Darstellung" and widened into the + // index for every module's view/sort/order surface. The slug stays `layout` + // so every route + deep link is untouched. { slug: "layout", titleKey: "settings.sections.layout.title", - icon: LayoutDashboard, + icon: Palette, }, // v1.18.0 (S5) — Medikamente: medication-specific settings (list view + // order + injection sites). Was a hidden child of the Layout hub.