fix(settings): collapse Notifications redundancy onto one status surface

Telegram, ntfy, and Web Push cards used to paint their own
"Configured · Disabled / Active" badge pair in the top-right
header. The `<NotificationStatusCard>` rendered above the same
cards already shows a live channel pill ("Active · 12 min ago" /
"Auto-disabled — web_push_410_gone"), so the per-card badges were
the third (sometimes fourth) surface for the same state.

Drop the dual-Badge slot from all three cards. The section header
pill is the single source of truth, the per-card surface now
reads as identity + description + form, and the in-app vocabulary
no longer mixes "Konfiguriert", "Aktiv", and "Verbunden" for the
same fact.

Copy pass:
* Settings → Notifications description: "Live-Übersicht aller
  konfigurierten Kanäle." (was a 13-word sentence repeating "Kanal"
  three times).
* `notificationStatus.title` renames to "Zustellstatus" /
  "Delivery status" so the page header and the first card don't
  both anchor on "Kanal".
* `notificationStatus.description` compresses the "Auto-disabled"
  definition.
* Telegram card title drops "-Benachrichtigungen" — the section is
  Notifications, so saying it again is wasted shelf space.
* `telegramDescription` shortens to "Medikamenten-Erinnerungen
  per Bot." / "Medication reminders via bot."
* `moodLogDescription` drops the second "moodLog" mention.

Notification-status SSR test updated to the new copy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-16 17:29:37 +02:00
1 parent af17db5dd5
commit ff06b0ced2
6 files changed
+36 -73

No files matched your search

+6 -6
View File
@@ -1506,7 +1506,7 @@
},
"notifications": {
"title": "Benachrichtigungen",
"description": "Zustellung pro Kanal und die Live-Übersicht der Kanalzuverlässigkeit für jeden konfigurierten Kanal."
"description": "Live-Übersicht aller konfigurierten Kanäle."
},
"dashboard": {
"title": "Dashboard",
@@ -1669,8 +1669,8 @@
"singleDevice": "Einzelgerät",
"multiDevice": "Multi-Gerät",
"backedUp": "gesichert",
"telegram": "Telegram-Benachrichtigungen",
"telegramDescription": "Erhalte Erinnerungen bei vergessenen Medikamenten-Einnahmen per Telegram.",
"telegram": "Telegram",
"telegramDescription": "Medikamenten-Erinnerungen per Bot.",
"telegramSaved": "Telegram-Einstellungen gespeichert",
"botToken": "Bot-Token",
"chatId": "Chat-ID",
@@ -1883,7 +1883,7 @@
"saved": "Gespeichert",
"savingError": "Fehler beim Speichern",
"moodLogTitle": "moodLog",
"moodLogDescription": "Stimmungsdaten aus moodLog importieren",
"moodLogDescription": "Stimmungseinträge übernehmen.",
"moodLogUrl": "moodLog URL",
"moodLogUrlPlaceholder": "https://mood.example.com",
"moodLogApiKey": "API-Key",
@@ -1944,8 +1944,8 @@
}
},
"notificationStatus": {
"title": "Kanalzuverlässigkeit",
"description": "Live-Status jedes Benachrichtigungskanals — \"Automatisch deaktiviert\" bedeutet, HealthLog hat nach einem dauerhaften Fehler aufgehört, weiterzusenden.",
"title": "Zustellstatus",
"description": "Pro Kanal aktueller Status; \"Automatisch deaktiviert\" stoppt nach dauerhaften Fehlern.",
"emptyDescription": "Noch keine Kanäle konfiguriert. Lege unten einen Kanal an, um die Zustellung zu überwachen.",
"stateActive": "Aktiv",
"stateAutoDisabled": "Automatisch deaktiviert",
+6 -6
View File
@@ -1510,7 +1510,7 @@
},
"notifications": {
"title": "Notifications",
"description": "Per-channel delivery and the live reliability board for every configured channel."
"description": "Live overview of every configured channel."
},
"dashboard": {
"title": "Dashboard",
@@ -1676,8 +1676,8 @@
"singleDevice": "Single device",
"multiDevice": "Multi-device",
"backedUp": "backed up",
"telegram": "Telegram Notifications",
"telegramDescription": "Get reminders for missed medication intake via Telegram.",
"telegram": "Telegram",
"telegramDescription": "Medication reminders via bot.",
"telegramSaved": "Telegram settings saved",
"botToken": "Bot Token",
"chatId": "Chat ID",
@@ -1890,7 +1890,7 @@
"saved": "Saved",
"savingError": "Error saving",
"moodLogTitle": "moodLog",
"moodLogDescription": "Import mood data from moodLog",
"moodLogDescription": "Import mood entries.",
"moodLogUrl": "moodLog URL",
"moodLogUrlPlaceholder": "https://mood.example.com",
"moodLogApiKey": "API Key",
@@ -1951,8 +1951,8 @@
}
},
"notificationStatus": {
"title": "Channel reliability",
"description": "Live status of every notification channel — Auto-disabled means HealthLog stopped retrying after a permanent error.",
"title": "Delivery status",
"description": "Per-channel state; \"Auto-disabled\" stops retries after permanent errors.",
"emptyDescription": "No channels configured yet. Add a channel below to start tracking its delivery health.",
"stateActive": "Active",
"stateAutoDisabled": "Auto-disabled",
@@ -66,7 +66,7 @@ describe("<NotificationStatusCard />", () => {
it("renders empty state when no channels exist", () => {
setChannels([]);
const html = render(<NotificationStatusCard />);
expect(html).toContain("Channel reliability");
expect(html).toContain("Delivery status");
expect(html).toContain("No channels configured yet");
expect(html).not.toContain('data-testid="notification-status-list"');
});
@@ -189,7 +189,7 @@ describe("<NotificationStatusCard />", () => {
},
]);
const html = render(<NotificationStatusCard />, "de");
expect(html).toContain("Kanalzuverlässigkeit");
expect(html).toContain("Zustellstatus");
expect(html).toContain("Automatisch deaktiviert");
expect(html).toContain("Wieder aktivieren");
});
+6 -18
View File
@@ -4,7 +4,6 @@ import { useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Bell, Loader2, Send } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -101,23 +100,12 @@ export function NtfyCard({ isAuthenticated }: { isAuthenticated: boolean }) {
return (
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex flex-wrap items-start justify-between gap-2">
<div className="flex items-center gap-2">
<Bell className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("settings.ntfy")}</h2>
</div>
<div className="flex flex-wrap items-center gap-2">
{settings?.serverUrl && settings?.topic && (
<Badge className="border-dracula-green/30 bg-dracula-green/15 text-dracula-green">
{t("settings.configured")}
</Badge>
)}
{settings?.enabled && (
<Badge variant="outline" className="text-xs">
{t("common.enabled")}
</Badge>
)}
</div>
{/* v1.4.33 — drop the per-card status badge pair; the channel
pill in `<NotificationStatusCard>` above already shows
live ntfy state. See `telegram-card.tsx` for the same fix. */}
<div className="flex items-center gap-2">
<Bell className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("settings.ntfy")}</h2>
</div>
<p className="text-muted-foreground mt-1 text-xs">
{t("settings.ntfyDescription")}
+9 -23
View File
@@ -4,7 +4,6 @@ import { useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Loader2, MessageCircle, Save, Send } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -106,28 +105,15 @@ export function TelegramCard({
return (
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex flex-wrap items-start justify-between gap-2">
<div className="flex items-center gap-2">
<MessageCircle className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("settings.telegram")}</h2>
</div>
<div className="flex flex-wrap items-center gap-2">
{/* F-16 (v1.4.19): show one unified status pill instead of
the previous "Configured" + "Enabled" pair, which had no
visual hierarchy and made it unclear how the two states
differed. The new pill collapses both bits of info:
green when active, neutral when configured-but-paused. */}
{settings?.hasBotToken && settings?.chatId && settings?.enabled && (
<Badge className="border-dracula-green/30 bg-dracula-green/15 text-dracula-green">
{t("common.active")}
</Badge>
)}
{settings?.hasBotToken && settings?.chatId && !settings?.enabled && (
<Badge variant="outline" className="text-xs">
{t("settings.configured")} · {t("common.disabled")}
</Badge>
)}
</div>
{/* v1.4.33 — drop the per-card status badge pair. The
`<NotificationStatusCard>` above already paints the live
channel pill for Telegram; surfacing the same fact a
second time inside this card produced overlapping status
surfaces (page header + card pill + dual Badge + the
enable Switch) for one bit of state. */}
<div className="flex items-center gap-2">
<MessageCircle className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("settings.telegram")}</h2>
</div>
<p className="text-muted-foreground mt-1 text-xs">
{t("settings.telegramDescription")}
+7 -18
View File
@@ -3,7 +3,6 @@
import { useEffect, useState } from "react";
import { BellRing, Loader2 } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { TestConnectionButton } from "@/components/settings/test-connection-button";
import { useTranslations } from "@/lib/i18n/context";
@@ -157,23 +156,13 @@ export function WebPushCard() {
return (
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex flex-wrap items-start justify-between gap-2">
<div className="flex items-center gap-2">
<BellRing className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("settings.webPush")}</h2>
</div>
<div className="flex flex-wrap items-center gap-2">
{!loading && isSubscribed && (
<Badge className="border-dracula-green/30 bg-dracula-green/15 text-dracula-green">
{t("settings.configured")}
</Badge>
)}
{!loading && isSubscribed && (
<Badge variant="outline" className="text-xs">
{t("settings.webPushActive")}
</Badge>
)}
</div>
{/* v1.4.33 — drop the per-card status badge pair; the channel
pill in `<NotificationStatusCard>` above already shows
live Web Push state. See `telegram-card.tsx` for the same
fix. */}
<div className="flex items-center gap-2">
<BellRing className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("settings.webPush")}</h2>
</div>
<p className="text-muted-foreground mt-1 text-xs">
{t("settings.webPushDescription")}