mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
feat(settings): make Benachrichtigungen the single module-gated reminder-types home
Consolidate every reminder TYPE onto the Notifications screen and drop the redundant stacked breadcrumb for one clean heading. Each reminder-type card is shown only when its module is enabled, read from the resolved useAuth().user.modules map (mood card on mood, Coach nudge on coach); the gate fails open so a stale /me payload never blanks a card. Low-stock maps to the medications CORE domain and Vorsorge (preventive care) is not a toggleable module, so both stay shown — the Vorsorge editor embeds in place. The delivery channels keep a single concise pointer to Integrations rather than a duplicated section. Reword the heading copy and drop the now-unused settings.sections.reminders.* keys across all six locales.
This commit is contained in:
1 parent
fccdcb378e
commit
604778f166
9 files changed
+180
-246
No files matched your search
+3
-32
@@ -3871,9 +3871,10 @@
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Benachrichtigungen",
|
||||
"description": "Welche Erinnerungen ausgelöst werden. Die Zustellkanäle liegen unter Integrationen.",
|
||||
"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"
|
||||
"channelsHintLink": "Integrationen → Kanäle",
|
||||
"inboxLink": "Posteingang öffnen"
|
||||
},
|
||||
"layout": {
|
||||
"title": "Layout & Personalisierung",
|
||||
@@ -4106,36 +4107,6 @@
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"title": "Module",
|
||||
"saved": "Gespeichert.",
|
||||
|
||||
+3
-32
@@ -3871,9 +3871,10 @@
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notifications",
|
||||
"description": "Which reminders fire. The delivery channels live under Integrations.",
|
||||
"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"
|
||||
"channelsHintLink": "Integrations → Channels",
|
||||
"inboxLink": "Open the inbox"
|
||||
},
|
||||
"layout": {
|
||||
"title": "Layout & Personalization",
|
||||
@@ -4106,36 +4107,6 @@
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"title": "Modules",
|
||||
"saved": "Saved.",
|
||||
|
||||
+3
-32
@@ -3871,9 +3871,10 @@
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notificaciones",
|
||||
"description": "Qué recordatorios se activan. Los canales de entrega están en Integraciones.",
|
||||
"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"
|
||||
"channelsHintLink": "Integraciones → Canales",
|
||||
"inboxLink": "Abrir la bandeja de entrada"
|
||||
},
|
||||
"layout": {
|
||||
"title": "Diseño y personalización",
|
||||
@@ -4106,36 +4107,6 @@
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"title": "Módulos",
|
||||
"saved": "Guardado.",
|
||||
|
||||
+3
-32
@@ -3871,9 +3871,10 @@
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notifications",
|
||||
"description": "Quels rappels se déclenchent. Les canaux de diffusion se trouvent dans Intégrations.",
|
||||
"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"
|
||||
"channelsHintLink": "Intégrations → Canaux",
|
||||
"inboxLink": "Ouvrir la boîte de réception"
|
||||
},
|
||||
"layout": {
|
||||
"title": "Disposition et personnalisation",
|
||||
@@ -4106,36 +4107,6 @@
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"title": "Modules",
|
||||
"saved": "Enregistré.",
|
||||
|
||||
+3
-32
@@ -3871,9 +3871,10 @@
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notifiche",
|
||||
"description": "Quali promemoria vengono attivati. I canali di recapito si trovano in Integrazioni.",
|
||||
"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"
|
||||
"channelsHintLink": "Integrazioni → Canali",
|
||||
"inboxLink": "Apri la posta in arrivo"
|
||||
},
|
||||
"layout": {
|
||||
"title": "Layout e personalizzazione",
|
||||
@@ -4106,36 +4107,6 @@
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"title": "Moduli",
|
||||
"saved": "Salvato.",
|
||||
|
||||
+3
-32
@@ -3871,9 +3871,10 @@
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Powiadomienia",
|
||||
"description": "Które przypomnienia są uruchamiane. Kanały dostarczania znajdują się w Integracjach.",
|
||||
"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"
|
||||
"channelsHintLink": "Integracje → Kanały",
|
||||
"inboxLink": "Otwórz skrzynkę odbiorczą"
|
||||
},
|
||||
"layout": {
|
||||
"title": "Układ i personalizacja",
|
||||
@@ -4106,36 +4107,6 @@
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"title": "Moduły",
|
||||
"saved": "Zapisano.",
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* v1.18.0 (S4) — module-gated per-type visibility on the consolidated
|
||||
* "Benachrichtigungen" reminder-types home.
|
||||
*
|
||||
* The mood reminder shows only when the `mood` module is enabled; the Coach
|
||||
* nudge only when `coach` is enabled. Low-stock maps to the medications CORE
|
||||
* domain (always shown). The gate fails OPEN, so a `/me` payload without a
|
||||
* module map keeps every card visible.
|
||||
*/
|
||||
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/notifications",
|
||||
useSearchParams: () => new URLSearchParams(),
|
||||
}));
|
||||
|
||||
vi.mock("@tanstack/react-query", () => ({
|
||||
useQuery: () => ({ data: null, isLoading: false, refetch: vi.fn() }),
|
||||
useQueryClient: () => ({ invalidateQueries: vi.fn() }),
|
||||
useMutation: () => ({ mutate: vi.fn(), isPending: false }),
|
||||
}));
|
||||
|
||||
// The auth mock is driven per-test via a mutable ref so each render can pick
|
||||
// a different module map without re-mocking the module.
|
||||
const authRef: { modules?: Record<string, boolean> } = {};
|
||||
vi.mock("@/hooks/use-auth", () => ({
|
||||
useAuth: () => ({
|
||||
user: { id: "u1", username: "t", role: "USER", modules: authRef.modules },
|
||||
isAuthenticated: true,
|
||||
isLoading: false,
|
||||
refetch: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
import { I18nProvider } from "@/lib/i18n/context";
|
||||
import { NotificationsSection } from "../notifications-section";
|
||||
|
||||
function render(modules?: Record<string, boolean>) {
|
||||
authRef.modules = modules;
|
||||
return renderToStaticMarkup(
|
||||
<I18nProvider initialLocale="en">
|
||||
<NotificationsSection />
|
||||
</I18nProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe("<NotificationsSection> module-gated reminder types", () => {
|
||||
it("shows mood + coach cards when no module map (fails open)", () => {
|
||||
const html = render(undefined);
|
||||
expect(html).toContain('id="mood-reminder"');
|
||||
expect(html).toContain('id="coach-nudge"');
|
||||
expect(html).toContain('id="low-stock"');
|
||||
});
|
||||
|
||||
it("hides the mood card when the mood module is disabled", () => {
|
||||
const html = render({ mood: false });
|
||||
expect(html).not.toContain('id="mood-reminder"');
|
||||
// Coach + low-stock are unaffected.
|
||||
expect(html).toContain('id="coach-nudge"');
|
||||
expect(html).toContain('id="low-stock"');
|
||||
});
|
||||
|
||||
it("hides the coach card when the coach module is disabled", () => {
|
||||
const html = render({ coach: false });
|
||||
expect(html).not.toContain('id="coach-nudge"');
|
||||
expect(html).toContain('id="mood-reminder"');
|
||||
expect(html).toContain('id="low-stock"');
|
||||
});
|
||||
|
||||
it("always shows low-stock (medications is a CORE domain)", () => {
|
||||
const html = render({ mood: false, coach: false });
|
||||
expect(html).toContain('id="low-stock"');
|
||||
expect(html).not.toContain('id="mood-reminder"');
|
||||
expect(html).not.toContain('id="coach-nudge"');
|
||||
});
|
||||
|
||||
it("shows the channels pointer (delivery channels live under Integrations)", () => {
|
||||
const html = render({});
|
||||
expect(html).toContain('data-slot="notifications-channels-cross-link"');
|
||||
expect(html).toContain('href="/settings/integrations"');
|
||||
});
|
||||
});
|
||||
@@ -182,12 +182,24 @@ describe("settings sections — SSR smoke", () => {
|
||||
expect(html).toContain("Withings");
|
||||
});
|
||||
|
||||
it("<NotificationsSection> renders heading", () => {
|
||||
it("<NotificationsSection> renders the single reminder-types home", () => {
|
||||
const html = render(<NotificationsSection />);
|
||||
// v1.4.33 IW7 — section renamed from "Notifications" to
|
||||
// "Notification channels" so it doesn't collide with the inbox at
|
||||
// `/notifications` ("Notifications").
|
||||
expect(html).toContain("Notification channels");
|
||||
// v1.18.0 (S4) — one clean heading (sr-only h1 + visible description),
|
||||
// no stacked breadcrumb. The screen is the single module-gated home for
|
||||
// reminder TYPES.
|
||||
expect(html).toContain("settings-section-notifications-title");
|
||||
// The old stacked breadcrumb middle crumb is gone.
|
||||
expect(html).not.toContain("Notification channels");
|
||||
// A single concise pointer to the channels (now under Integrations).
|
||||
expect(html).toContain('href="/settings/integrations"');
|
||||
expect(html).toContain('data-slot="notifications-channels-cross-link"');
|
||||
// The mood + coach cards fail OPEN (mock user carries no module map),
|
||||
// so both reminder-type cards render.
|
||||
expect(html).toContain('id="mood-reminder"');
|
||||
expect(html).toContain('id="coach-nudge"');
|
||||
expect(html).toContain('id="low-stock"');
|
||||
// No raw i18n key leaks past the provider.
|
||||
expect(html).not.toContain("settings.sections.notifications.");
|
||||
});
|
||||
|
||||
it("<DashboardSection> renders heading and embeds the layout customizer", () => {
|
||||
|
||||
@@ -1,72 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { CoachNudgeCard } from "@/components/settings/coach-nudge-card";
|
||||
import { LowStockCard } from "@/components/settings/low-stock-card";
|
||||
import { MoodReminderCard } from "@/components/settings/mood-reminder-card";
|
||||
import { VorsorgeSection } from "@/components/measurement-reminders/vorsorge-section";
|
||||
|
||||
/**
|
||||
* `<NotificationsSection>` — Settings → Notifications.
|
||||
* `<NotificationsSection>` — Settings → Notifications ("Benachrichtigungen").
|
||||
*
|
||||
* v1.18.0 (S3) — the delivery CHANNELS (Telegram / ntfy / Web Push / Webhook /
|
||||
* Email + the live per-channel status surface) moved to Settings →
|
||||
* Integrations → Channels: a channel is a delivery provider, the same family as
|
||||
* a connected device, not a notification preference. What stays here is the
|
||||
* reminder-TYPE content (which events fire): mood reminder, medication
|
||||
* low-stock runway, proactive Coach nudge. S4 will consolidate these onto the
|
||||
* Reminders hub.
|
||||
* v1.18.0 (S4) — the single module-gated home for reminder TYPES. Earlier the
|
||||
* settings carried two near-identical entries: this Notifications screen and a
|
||||
* separate "Erinnerungen" hub that only deep-linked into the canonical editors
|
||||
* ("doppelt gemoppelt"). The hub is gone (its route 301-redirects here); the
|
||||
* reminder TYPES now live centrally on this one screen.
|
||||
*
|
||||
* Each reminder-type card is shown ONLY when its module is enabled, read from
|
||||
* the resolved `useAuth().user.modules` map (the same `/auth/me` map nav and
|
||||
* Insights pills gate off). Mood → `mood`, Coach nudge → `coach`. Low-stock
|
||||
* runway maps to the medications CORE domain, so it is always shown. Vorsorge
|
||||
* (preventive-care) reminders are not a toggleable module, so they are always
|
||||
* shown. The gate fails OPEN: a missing key reads as enabled, so a stale
|
||||
* `/me` payload never blanks a card.
|
||||
*
|
||||
* Delivery CHANNELS (Telegram / ntfy / Web Push / Webhook / Email) are a
|
||||
* different concept — a channel is a delivery provider, the same family as a
|
||||
* connected device — and live under Settings → Integrations → Channels (moved
|
||||
* there in S3). A single concise pointer below the heading links to them.
|
||||
*/
|
||||
export function NotificationsSection() {
|
||||
const { t } = useTranslations();
|
||||
const { isAuthenticated } = useAuth();
|
||||
const { isAuthenticated, user } = useAuth();
|
||||
|
||||
// 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 coachEnabled = user?.modules?.coach !== false;
|
||||
|
||||
return (
|
||||
<section
|
||||
aria-labelledby="settings-section-notifications-title"
|
||||
className="space-y-6"
|
||||
>
|
||||
{/* v1.4.33 IW7 — disambiguate this settings screen from the inbox at
|
||||
`/notifications`. The crumb spells out where the user is and offers a
|
||||
one-tap jump to the inbox. */}
|
||||
<nav
|
||||
aria-label={t("nav.breadcrumb")}
|
||||
className="text-muted-foreground text-xs"
|
||||
>
|
||||
<ol className="flex items-center gap-1">
|
||||
<li>{t("notifications.breadcrumbSettings")}</li>
|
||||
<li aria-hidden="true">
|
||||
<ChevronRight className="h-3 w-3" />
|
||||
</li>
|
||||
<li className="text-foreground font-medium">
|
||||
{t("notifications.breadcrumbChannels")}
|
||||
</li>
|
||||
<li aria-hidden="true">
|
||||
<ChevronRight className="h-3 w-3" />
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/notifications"
|
||||
className="hover:text-foreground underline-offset-2 hover:underline"
|
||||
>
|
||||
{t("notifications.breadcrumbInbox")}
|
||||
</Link>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<header className="space-y-1">
|
||||
{/* sr-only h1 — the visible section title is painted by the settings
|
||||
shell nav; the in-page heading mirrors every other settings
|
||||
section's scaffold so the screen reads as one of the family. */}
|
||||
<h1 id="settings-section-notifications-title" className="sr-only">
|
||||
{t("settings.sections.notifications.title")}
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("settings.sections.notifications.description")}
|
||||
</p>
|
||||
{/* v1.18.0 (S3) — the delivery channels now live under Integrations.
|
||||
Cross-link so a user looking for "where do I connect Telegram"
|
||||
finds them in one tap. */}
|
||||
{/* One concise pointer to the delivery channels (now under
|
||||
Integrations) and one to the notification inbox — not a duplicated
|
||||
section, just where-to-go links. */}
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("settings.sections.notifications.channelsHint")}{" "}
|
||||
<Link
|
||||
@@ -76,20 +67,41 @@ export function NotificationsSection() {
|
||||
>
|
||||
{t("settings.sections.notifications.channelsHintLink")}
|
||||
</Link>
|
||||
{" · "}
|
||||
<Link
|
||||
href="/notifications"
|
||||
className="text-primary underline underline-offset-2"
|
||||
data-slot="notifications-inbox-cross-link"
|
||||
>
|
||||
{t("settings.sections.notifications.inboxLink")}
|
||||
</Link>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div id="mood-reminder" className="scroll-mt-28">
|
||||
<MoodReminderCard isAuthenticated={isAuthenticated} />
|
||||
</div>
|
||||
{/* v1.16.11 — medication low-stock runway threshold. */}
|
||||
{/* Vorsorge (preventive-care) reminders — always shown; not a
|
||||
toggleable module. The canonical in-place editor, embedded here so
|
||||
"what gets reminded and when" reads as one screen. */}
|
||||
<VorsorgeSection enabled={isAuthenticated} />
|
||||
|
||||
{/* 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 — medications is a CORE domain, so this
|
||||
is always shown. */}
|
||||
<div id="low-stock" className="scroll-mt-28">
|
||||
<LowStockCard isAuthenticated={isAuthenticated} />
|
||||
</div>
|
||||
{/* v1.15.20 — proactive Coach nudge opt-out. */}
|
||||
<div id="coach-nudge" className="scroll-mt-28">
|
||||
<CoachNudgeCard isAuthenticated={isAuthenticated} />
|
||||
</div>
|
||||
|
||||
{/* Proactive Coach nudge — only when the coach module is enabled. */}
|
||||
{coachEnabled ? (
|
||||
<div id="coach-nudge" className="scroll-mt-28">
|
||||
<CoachNudgeCard isAuthenticated={isAuthenticated} />
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user