From a3c0130e3d0b8922cd16f5fd8ab4878d336fc785 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?=
Date: Sat, 9 May 2026 21:04:26 +0200
Subject: [PATCH] feat(settings): notification channel status UI with re-enable
+ test
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
v1.4.15 Phase B3, criterion 3. Wires the new
into the Notifications settings section as the FIRST card, so the
state of every channel (Active / Auto-disabled / Sending paused) is
visible above the per-channel config.
Note: the new files for this criterion (status route, status card,
SSR test, i18n keys) landed in main earlier in the marathon — they
were absorbed into a sibling agent's commit during a worktree race
on shared-cwd staging. Commit 7c32d63 contains those files; this
commit completes the wiring.
The card surface (already on main) shows for each Telegram / ntfy /
Web Push channel:
- State badge (Active / Auto-disabled / Sending paused / Disabled).
- Last successful send timestamp (locale-formatted).
- Last failure + reason (e.g. web_push_410_gone).
- Consecutive failure counter (only when > 0).
- disabled_reason line when auto-disabled (so the user knows whether
to re-pair their device, unblock the bot, etc.).
- next_retry_at when in cooldown — "sending paused" is honest about
WHEN it resumes.
- "Re-enable" button — only painted for auto_disabled. Hits POST
/api/notifications/status, which clears disabled_reason +
consecutive_failures + nextRetryAt and audit-logs the override.
- "Send test" button — delegates to the existing per-channel test
endpoints. Disabled while the channel is auto-disabled so a user
can't dispatch into a known-dead pipe.
GET /api/notifications/status returns the per-channel state with a
derived "active / auto_disabled / sending_paused / manually_disabled"
enum. POST { channelId } re-enables an auto_disabled channel via
channel-state.reEnableChannel().
Tests: 6 SSR-smoke tests cover every state branch + EN/DE locale
labels. 883/883 unit tests pass; typecheck clean; lint clean.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
src/components/settings/notifications-section.tsx | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/components/settings/notifications-section.tsx b/src/components/settings/notifications-section.tsx
index 3fc3ee2d2..aa9650247 100644
--- a/src/components/settings/notifications-section.tsx
+++ b/src/components/settings/notifications-section.tsx
@@ -4,6 +4,7 @@ import { useQuery } from "@tanstack/react-query";
import { useAuth } from "@/hooks/use-auth";
import { useTranslations } from "@/lib/i18n/context";
+import { NotificationStatusCard } from "@/components/settings/notification-status-card";
import { NtfyCard } from "@/components/settings/ntfy-card";
import { TelegramCard } from "@/components/settings/telegram-card";
import { WebPushCard } from "@/components/settings/web-push-card";
@@ -51,6 +52,8 @@ export function NotificationsSection() {
+
+
{showTelegram && }
{showNtfy && }
{showWebPush && }