Merge v1.25.3: channels into notifications + Darstellung appearance hub

This commit is contained in:
Marc-André Bombeck committed 2026-06-29 15:49:39 +02:00
commit 7eca388f56
18 files changed
+347 -188

No files matched your search

+26 -13
View File
@@ -4036,15 +4036,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."
@@ -4053,7 +4055,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",
@@ -4311,11 +4329,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."
+26 -13
View File
@@ -4036,15 +4036,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."
@@ -4053,7 +4055,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",
@@ -4311,11 +4329,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."
+26 -13
View File
@@ -4036,15 +4036,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."
@@ -4053,7 +4055,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",
@@ -4311,11 +4329,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."
+26 -13
View File
@@ -4036,15 +4036,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."
@@ -4053,7 +4055,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",
@@ -4311,11 +4329,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."
+26 -13
View File
@@ -4036,15 +4036,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."
@@ -4053,7 +4055,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",
@@ -4311,11 +4329,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."
+26 -13
View File
@@ -4036,15 +4036,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."
@@ -4053,7 +4055,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",
@@ -4311,11 +4329,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."
+11
View File
@@ -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
+2 -4
View File
@@ -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,
@@ -63,7 +63,10 @@ export function VorsorgeSettings() {
// `<section>` blocks with `text-sm` headings.
return (
<div className="space-y-6">
<SettingsCard>
{/* 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. */}
<SettingsCard id="vorsorge-view" className="scroll-mt-28">
<SettingsCardHeader
icon={LayoutGrid}
title={t("moduleList.viewHeading")}
@@ -1,9 +1,14 @@
/**
* v1.17.1 (F-2) — the "Layout & Personalization" hub.
* v1.17.1 (F-2) — the "Appearance" hub (slug stays `layout`).
*
* The hub is the single front door for the four personalization editors.
* It must link to each of them with consistent framing so the concept
* reads as one home rather than four scattered settings sections.
* The hub is the single front door for every view/arrangement surface. It must
* link to each with consistent framing so "how my app looks" reads as one home
* rather than scattered settings sections.
*
* v1.25.3 — the hub widened from 2 links (dashboard + insights) to 6: it now
* also deep-links to the view/sort/order cards of medications, labs, the
* illness journal, and checkups. Those cards stay on their own module pages;
* the hub only indexes them via anchor deep-links.
*/
import { describe, expect, it } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
@@ -20,26 +25,34 @@ function render(locale: "en" | "de" = "en") {
}
describe("<LayoutSection>", () => {
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 `<a href="/settings/…">`.
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.");
});
});
@@ -188,19 +188,25 @@ describe("settings sections — SSR smoke", () => {
expect(html).toContain("Withings");
});
it("<NotificationsSection> renders the single reminder-types home", () => {
it("<NotificationsSection> renders the reminders + delivery-channels groups", () => {
const html = renderFramed("notifications", <NotificationsSection />);
// 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.");
});
@@ -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("<SettingsShell>", () => {
// (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</a>");
// v1.18.1 (D4) — Channels + Sources are their own left-side entries.
expect(html).toContain('href="/settings/channels"');
expect(html).toContain(">Appearance</a>");
// 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("<SettingsShell>", () => {
// 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</a>");
// 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</a>");
// 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"');
@@ -1,20 +0,0 @@
"use client";
/**
* `<ChannelsSection>` — 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 `<NotificationChannelsPanel>`. v1.18.6 (W9) —
* the visible heading + subtitle now come from the shared
* `<SettingsSectionFrame>` in the route; this wrapper is the panel only.
*/
import { NotificationChannelsPanel } from "@/components/settings/integrations/notification-channels-panel";
export function ChannelsSection() {
return <NotificationChannelsPanel />;
}
+47 -13
View File
@@ -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<LayoutLink> = [
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() {
@@ -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";
/**
* `<NotificationsSection>` — 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
* `<NotificationChannelsPanel>`. 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
* `<SettingsSectionFrame>`. 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
* `<SettingsSectionFrame>`. 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 (
<div className="space-y-6">
{/* Mood check-in reminder — only when the mood module is enabled. */}
{moodEnabled ? (
<div id="mood-reminder" className="scroll-mt-28">
<MoodReminderCard isAuthenticated={isAuthenticated} />
</div>
<div className="space-y-10">
{/* 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 ? (
<section className="space-y-4">
<div className="space-y-0.5">
<h2 className="text-foreground text-lg font-semibold">
{t("settings.sections.notifications.remindersHeading")}
</h2>
<p className="text-muted-foreground text-sm">
{t("settings.sections.notifications.remindersDescription")}
</p>
</div>
{/* Mood check-in reminder — only when the mood module is enabled. */}
{moodEnabled ? (
<div id="mood-reminder" className="scroll-mt-28">
<MoodReminderCard isAuthenticated={isAuthenticated} />
</div>
) : null}
{/* Medication low-stock runway — only when the medications module
is enabled (toggleable, fail-open, since D3). */}
{medsEnabled ? (
<div id="low-stock" className="scroll-mt-28">
<LowStockCard isAuthenticated={isAuthenticated} />
</div>
) : null}
</section>
) : null}
{/* Medication low-stock runway — only when the medications module is
enabled (toggleable, fail-open, since D3). */}
{medsEnabled ? (
<div id="low-stock" className="scroll-mt-28">
<LowStockCard isAuthenticated={isAuthenticated} />
{/* Group 2 — Delivery channels ("where it's delivered"). The existing
channels panel, unchanged. `id="channels"` anchors the group for the
`/settings/channels` redirect. */}
<section id="channels" className="scroll-mt-28 space-y-4">
<div className="space-y-0.5">
<h2 className="text-foreground text-lg font-semibold">
{t("settings.sections.notifications.channelsHeading")}
</h2>
<p className="text-muted-foreground text-sm">
{t("settings.sections.notifications.channelsDescription")}
</p>
</div>
) : null}
<NotificationChannelsPanel />
</section>
</div>
);
}
@@ -29,7 +29,6 @@ import {
Link2,
Lock,
Pill,
Radio,
Settings2,
Share2,
ShieldCheck,
@@ -51,7 +50,6 @@ const SLUG_ICON: Record<SettingsSectionSlug, LucideIcon> = {
security: ShieldCheck,
modules: Blocks,
integrations: Link2,
channels: Radio,
sources: Layers,
notifications: Bell,
layout: LayoutDashboard,
+4 -3
View File
@@ -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",
+12 -15
View File
@@ -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.