diff --git a/messages/de.json b/messages/de.json
index 8142d82d1..1d1ba962d 100644
--- a/messages/de.json
+++ b/messages/de.json
@@ -4087,6 +4087,36 @@
"mood": {
"title": "Stimmungs-Tags",
"description": "Gruppen, Tags und Reihenfolge der Tag-Auswahl."
+ },
+ "reminders": {
+ "title": "Erinnerungen & Benachrichtigungen",
+ "description": "Ein Ort für jede Erinnerung — woran und wann erinnert wird — und die Kanäle, die sie zustellen.",
+ "categoriesGroup": "Erinnerungen",
+ "channelsGroup": "Zustellkanäle",
+ "medication": {
+ "title": "Medikamenten-Erinnerungen",
+ "description": "Einnahmezeiten und Zeitfenster, je Medikament eingestellt."
+ },
+ "vorsorge": {
+ "title": "Vorsorge",
+ "description": "Wiederkehrende Untersuchungen und Messungen, inklusive Ort."
+ },
+ "mood": {
+ "title": "Stimmungs-Check-in",
+ "description": "Eine tägliche Erinnerung, dein Befinden zu erfassen."
+ },
+ "lowStock": {
+ "title": "Reichweite bei Restbestand",
+ "description": "Werde gewarnt, bevor ein Medikament zur Neige geht."
+ },
+ "coach": {
+ "title": "Coach-Hinweis",
+ "description": "Proaktive Impulse vom KI-Coach."
+ },
+ "channels": {
+ "title": "Benachrichtigungskanäle",
+ "description": "APNs, Telegram, ntfy, Web Push, Webhook und E-Mail."
+ }
}
},
"profile": "Profil",
@@ -4773,7 +4803,8 @@
"token": "Der Token-Austausch mit Oura ist fehlgeschlagen. Bitte starte die Verbindung erneut.",
"rate_limited": "Zu viele Verbindungsversuche. Warte einen Moment und versuche es erneut.",
"generic": "Unbekannter Fehler bei der Verbindung. Bitte starte die Verbindung erneut."
- }
+ },
+ "integrationSetupGuide": "Einrichtungsanleitung"
},
"admin": {
"title": "Administration",
diff --git a/messages/en.json b/messages/en.json
index 1e7058c04..3a8b08b12 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -4087,6 +4087,36 @@
"mood": {
"title": "Mood tags",
"description": "Groups, tags, and order of the mood picker."
+ },
+ "reminders": {
+ "title": "Reminders & Notifications",
+ "description": "One home for every reminder — what gets reminded and when — plus the channels that deliver them.",
+ "categoriesGroup": "Reminders",
+ "channelsGroup": "Delivery channels",
+ "medication": {
+ "title": "Medication reminders",
+ "description": "Dose times and windows, configured on each medication."
+ },
+ "vorsorge": {
+ "title": "Preventive care",
+ "description": "Recurring check-ups and measurements, with where to go."
+ },
+ "mood": {
+ "title": "Mood check-in",
+ "description": "A daily nudge to log how you feel."
+ },
+ "lowStock": {
+ "title": "Low-stock runway",
+ "description": "Get warned before a medication runs out."
+ },
+ "coach": {
+ "title": "Coach nudge",
+ "description": "Proactive prompts from the AI coach."
+ },
+ "channels": {
+ "title": "Notification channels",
+ "description": "APNs, Telegram, ntfy, Web Push, Webhook, and Email."
+ }
}
},
"profile": "Profile",
@@ -4773,7 +4803,8 @@
"token": "The token exchange with Oura failed. Please start the connection again.",
"rate_limited": "Too many connection attempts. Wait a moment and try again.",
"generic": "Unknown error while connecting. Please start the connection again."
- }
+ },
+ "integrationSetupGuide": "Setup guide"
},
"admin": {
"title": "Administration",
diff --git a/messages/es.json b/messages/es.json
index 4988d8cd5..210c603ad 100644
--- a/messages/es.json
+++ b/messages/es.json
@@ -4087,6 +4087,36 @@
"mood": {
"title": "Etiquetas de ánimo",
"description": "Grupos, etiquetas y orden del selector de ánimo."
+ },
+ "reminders": {
+ "title": "Recordatorios y notificaciones",
+ "description": "Un único lugar para cada recordatorio — qué se recuerda y cuándo — y los canales que lo entregan.",
+ "categoriesGroup": "Recordatorios",
+ "channelsGroup": "Canales de entrega",
+ "medication": {
+ "title": "Recordatorios de medicación",
+ "description": "Horas y ventanas de dosis, configuradas en cada medicamento."
+ },
+ "vorsorge": {
+ "title": "Cuidado preventivo",
+ "description": "Revisiones y mediciones periódicas, con el lugar al que acudir."
+ },
+ "mood": {
+ "title": "Registro de ánimo",
+ "description": "Un aviso diario para registrar cómo te sientes."
+ },
+ "lowStock": {
+ "title": "Margen por bajo stock",
+ "description": "Recibe un aviso antes de que se acabe un medicamento."
+ },
+ "coach": {
+ "title": "Sugerencia del coach",
+ "description": "Indicaciones proactivas del coach de IA."
+ },
+ "channels": {
+ "title": "Canales de notificación",
+ "description": "APNs, Telegram, ntfy, Web Push, Webhook y correo."
+ }
}
},
"profile": "Perfil",
@@ -4773,7 +4803,8 @@
"token": "El intercambio de token con Oura falló. Inicia la conexión de nuevo.",
"rate_limited": "Demasiados intentos de conexión. Espera un momento e inténtalo de nuevo.",
"generic": "Error desconocido al conectar. Inicia la conexión de nuevo."
- }
+ },
+ "integrationSetupGuide": "Guía de configuración"
},
"admin": {
"title": "Administration",
diff --git a/messages/fr.json b/messages/fr.json
index c47bf151d..e73f56d89 100644
--- a/messages/fr.json
+++ b/messages/fr.json
@@ -4087,6 +4087,36 @@
"mood": {
"title": "Étiquettes d’humeur",
"description": "Groupes, étiquettes et ordre du sélecteur d’humeur."
+ },
+ "reminders": {
+ "title": "Rappels et notifications",
+ "description": "Un seul endroit pour chaque rappel — quoi et quand — ainsi que les canaux qui les délivrent.",
+ "categoriesGroup": "Rappels",
+ "channelsGroup": "Canaux de diffusion",
+ "medication": {
+ "title": "Rappels de médication",
+ "description": "Heures et fenêtres de prise, configurées sur chaque médicament."
+ },
+ "vorsorge": {
+ "title": "Prévention",
+ "description": "Bilans et mesures récurrents, avec le lieu où se rendre."
+ },
+ "mood": {
+ "title": "Suivi de l'humeur",
+ "description": "Un rappel quotidien pour noter votre ressenti."
+ },
+ "lowStock": {
+ "title": "Marge de stock faible",
+ "description": "Soyez averti avant qu'un médicament ne s'épuise."
+ },
+ "coach": {
+ "title": "Suggestion du coach",
+ "description": "Invitations proactives du coach IA."
+ },
+ "channels": {
+ "title": "Canaux de notification",
+ "description": "APNs, Telegram, ntfy, Web Push, Webhook et e-mail."
+ }
}
},
"profile": "Profil",
@@ -4773,7 +4803,8 @@
"token": "L'échange de jeton avec Oura a échoué. Veuillez relancer la connexion.",
"rate_limited": "Trop de tentatives de connexion. Patientez un instant et réessayez.",
"generic": "Erreur inconnue lors de la connexion. Veuillez relancer la connexion."
- }
+ },
+ "integrationSetupGuide": "Guide de configuration"
},
"admin": {
"title": "Administration",
diff --git a/messages/it.json b/messages/it.json
index cd103de51..5e20abdb5 100644
--- a/messages/it.json
+++ b/messages/it.json
@@ -4087,6 +4087,36 @@
"mood": {
"title": "Tag dell’umore",
"description": "Gruppi, tag e ordine del selettore dell’umore."
+ },
+ "reminders": {
+ "title": "Promemoria e notifiche",
+ "description": "Un'unica sede per ogni promemoria — cosa ricordare e quando — e i canali che li recapitano.",
+ "categoriesGroup": "Promemoria",
+ "channelsGroup": "Canali di consegna",
+ "medication": {
+ "title": "Promemoria dei farmaci",
+ "description": "Orari e finestre delle dosi, impostati su ogni farmaco."
+ },
+ "vorsorge": {
+ "title": "Prevenzione",
+ "description": "Controlli e misurazioni ricorrenti, con il luogo dove recarsi."
+ },
+ "mood": {
+ "title": "Check-in dell'umore",
+ "description": "Un promemoria quotidiano per registrare come ti senti."
+ },
+ "lowStock": {
+ "title": "Autonomia scorte",
+ "description": "Ricevi un avviso prima che un farmaco finisca."
+ },
+ "coach": {
+ "title": "Suggerimento del coach",
+ "description": "Stimoli proattivi dal coach IA."
+ },
+ "channels": {
+ "title": "Canali di notifica",
+ "description": "APNs, Telegram, ntfy, Web Push, Webhook ed e-mail."
+ }
}
},
"profile": "Profilo",
@@ -4773,7 +4803,8 @@
"token": "Lo scambio del token con Oura è fallito. Riavvia la connessione.",
"rate_limited": "Troppi tentativi di connessione. Attendi un momento e riprova.",
"generic": "Errore sconosciuto durante la connessione. Riavvia la connessione."
- }
+ },
+ "integrationSetupGuide": "Guida alla configurazione"
},
"admin": {
"title": "Administration",
diff --git a/messages/pl.json b/messages/pl.json
index 19cfeb26e..f62178eb5 100644
--- a/messages/pl.json
+++ b/messages/pl.json
@@ -4087,6 +4087,36 @@
"mood": {
"title": "Tagi nastroju",
"description": "Grupy, tagi i kolejność w wyborze nastroju."
+ },
+ "reminders": {
+ "title": "Przypomnienia i powiadomienia",
+ "description": "Jedno miejsce dla każdego przypomnienia — o czym i kiedy — oraz kanały, które je dostarczają.",
+ "categoriesGroup": "Przypomnienia",
+ "channelsGroup": "Kanały dostarczania",
+ "medication": {
+ "title": "Przypomnienia o lekach",
+ "description": "Godziny i okna dawek, ustawiane przy każdym leku."
+ },
+ "vorsorge": {
+ "title": "Profilaktyka",
+ "description": "Cykliczne badania i pomiary wraz z miejscem, gdzie się udać."
+ },
+ "mood": {
+ "title": "Zapis nastroju",
+ "description": "Codzienne przypomnienie, by zapisać samopoczucie."
+ },
+ "lowStock": {
+ "title": "Zapas na wyczerpaniu",
+ "description": "Otrzymaj ostrzeżenie, zanim lek się skończy."
+ },
+ "coach": {
+ "title": "Podpowiedź trenera",
+ "description": "Proaktywne wskazówki od trenera AI."
+ },
+ "channels": {
+ "title": "Kanały powiadomień",
+ "description": "APNs, Telegram, ntfy, Web Push, Webhook i e-mail."
+ }
}
},
"profile": "Profil",
@@ -4773,7 +4803,8 @@
"token": "Wymiana tokena z Oura nie powiodła się. Rozpocznij połączenie ponownie.",
"rate_limited": "Zbyt wiele prób połączenia. Poczekaj chwilę i spróbuj ponownie.",
"generic": "Nieznany błąd podczas łączenia. Rozpocznij połączenie ponownie."
- }
+ },
+ "integrationSetupGuide": "Przewodnik konfiguracji"
},
"admin": {
"title": "Administracja",
diff --git a/src/app/settings/[section]/page.tsx b/src/app/settings/[section]/page.tsx
index 20f1d2d25..87b97f90f 100644
--- a/src/app/settings/[section]/page.tsx
+++ b/src/app/settings/[section]/page.tsx
@@ -14,6 +14,7 @@ import { LayoutSection } from "@/components/settings/layout-section";
import { MedicationsSection } from "@/components/settings/medications-section";
import { MoodSection } from "@/components/settings/mood-section";
import { NotificationsSection } from "@/components/settings/notifications-section";
+import { RemindersSection } from "@/components/settings/reminders-section";
import { SectionPlaceholder } from "@/components/settings/section-placeholder";
// v1.8.7.1 — `thresholds` (Targets) and `sources` (Sources) are two
// separate sections again. `ThresholdsSection` renders the target-range
@@ -52,6 +53,7 @@ const SECTION_COMPONENTS: Record<
ai: AiSection,
integrations: IntegrationsSection,
notifications: NotificationsSection,
+ reminders: RemindersSection,
layout: LayoutSection,
dashboard: DashboardSection,
insights: InsightsSection,
diff --git a/src/components/settings/__tests__/integrations-section.test.tsx b/src/components/settings/__tests__/integrations-section.test.tsx
index 8381da09e..69b49d1fb 100644
--- a/src/components/settings/__tests__/integrations-section.test.tsx
+++ b/src/components/settings/__tests__/integrations-section.test.tsx
@@ -303,4 +303,43 @@ describe("IntegrationsSection — single-status-display contract (A5)", () => {
// Fitbit / Google Health). The moodLog integration was removed.
expect(count(html, 'data-testid="integration-card-divider"')).toBe(3);
});
+
+ // v1.17.1 — every integration card carries the same discreet "Setup guide"
+ // doc-link pointing at its runbook under the single shared docs base. The
+ // affordance is one family across all six providers.
+ it("every integration card carries the shared Setup-guide doc-link", () => {
+ setIntegrationStatus({
+ threshold: 3,
+ integrations: [
+ {
+ integration: "withings",
+ state: "connected",
+ lastSuccessAt: "2026-05-09T18:00:00.000Z",
+ lastAttemptAt: "2026-05-09T18:00:00.000Z",
+ lastError: null,
+ connected: true,
+ configured: true,
+ legacyLastSyncedAt: "2026-05-09T18:00:00.000Z",
+ hasActivityScope: true,
+ },
+ ],
+ });
+
+ const html = render();
+ // One setup-guide link per card → six providers.
+ expect(count(html, 'data-slot="integration-setup-guide"')).toBe(6);
+ for (const provider of [
+ "withings",
+ "whoop",
+ "fitbit",
+ "polar",
+ "oura",
+ "nightscout",
+ ]) {
+ expect(html).toContain(`data-testid="${provider}-setup-guide"`);
+ expect(html).toContain(
+ `href="https://docs.healthlog.dev/integrations/${provider}"`,
+ );
+ }
+ });
});
diff --git a/src/components/settings/__tests__/reminders-section.test.tsx b/src/components/settings/__tests__/reminders-section.test.tsx
new file mode 100644
index 000000000..3305f95d1
--- /dev/null
+++ b/src/components/settings/__tests__/reminders-section.test.tsx
@@ -0,0 +1,67 @@
+/**
+ * v1.17.1 — the "Reminders & Notifications" hub.
+ *
+ * The hub is the single front door for every reminder concept. It groups the
+ * reminder CATEGORIES (medication / Vorsorge / mood / low-stock / coach) and
+ * links to the notification CHANNELS, with each link deep-linking into the
+ * canonical editor so nothing is rewritten — only gathered in one place.
+ */
+import { describe, expect, it } from "vitest";
+import { renderToStaticMarkup } from "react-dom/server";
+
+import { I18nProvider } from "@/lib/i18n/context";
+import { RemindersSection } from "../reminders-section";
+
+function render(locale: "en" | "de" = "en") {
+ return renderToStaticMarkup(
+
{t(link.titleKey)}
++ {t(link.descriptionKey)} +
++ {t("settings.sections.reminders.description")} +
+