diff --git a/messages/de.json b/messages/de.json
index da188c5c9..0e16cf538 100644
--- a/messages/de.json
+++ b/messages/de.json
@@ -4570,6 +4570,7 @@
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
"nightscoutDescription": "Verbinde deine eigene Nightscout-Seite, um kontinuierliche Glukosewerte für das Glukose-Panel zu synchronisieren.",
+ "nightscoutViewData": "Glukose ansehen",
"nightscoutHelp": "Richte HealthLog auf deine Nightscout-Instanz aus. Werte werden stündlich im Hintergrund abgerufen.",
"nightscoutUrl": "Nightscout-URL",
"nightscoutUrlSavedPlaceholder": "Gespeichert — neue URL eingeben zum Ersetzen",
@@ -4588,6 +4589,7 @@
"nightscoutDisconnectDescription": "Künftige Synchronisierungen stoppen. Bereits importierte Glukosewerte bleiben in deinem Verlauf.",
"polar": "Polar",
"polarDescription": "Verbinde dein Polar-Konto, um Nightly-Recharge-Erholung, Schlafphasen und Tagesaktivität zu synchronisieren.",
+ "polarViewData": "Schlaf & Erholung ansehen",
"polarHelp": "Melde dich bei Polar an, um den Zugriff zu erlauben. Daten werden stündlich im Hintergrund abgerufen.",
"polarUnavailable": "Polar ist auf diesem Server nicht konfiguriert. Bitte deinen Administrator, die Polar-OAuth-Zugangsdaten zu hinterlegen.",
"polarConnect": "Polar verbinden",
@@ -4608,6 +4610,7 @@
},
"oura": "Oura",
"ouraDescription": "Verbinde dein Oura-Konto, um Readiness, Schlafphasen, HRV und Tagesaktivität zu synchronisieren.",
+ "ouraViewData": "Schlaf & Erholung ansehen",
"ouraHelp": "Melde dich bei Oura an, um den Zugriff zu erlauben. Daten werden stündlich im Hintergrund abgerufen.",
"ouraUnavailable": "Oura ist auf diesem Server nicht konfiguriert. Bitte deinen Administrator, die Oura-OAuth-Zugangsdaten zu hinterlegen.",
"ouraConnect": "Oura verbinden",
diff --git a/messages/en.json b/messages/en.json
index 162ac5504..68afa4acc 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -4570,6 +4570,7 @@
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
"nightscoutDescription": "Connect your own Nightscout site to sync continuous glucose readings for the glucose panel.",
+ "nightscoutViewData": "View your glucose",
"nightscoutHelp": "Point HealthLog at your Nightscout instance. Readings are pulled in the background once an hour.",
"nightscoutUrl": "Nightscout URL",
"nightscoutUrlSavedPlaceholder": "Saved — enter a new URL to replace it",
@@ -4588,6 +4589,7 @@
"nightscoutDisconnectDescription": "Future syncs stop. Glucose readings already imported stay in your history.",
"polar": "Polar",
"polarDescription": "Connect your Polar account to sync Nightly Recharge recovery, sleep stages, and daily activity.",
+ "polarViewData": "View your sleep & recovery",
"polarHelp": "Sign in with Polar to grant access. Data is pulled in the background once an hour.",
"polarUnavailable": "Polar is not configured on this server. Ask your administrator to add the Polar OAuth credentials.",
"polarConnect": "Connect Polar",
@@ -4608,6 +4610,7 @@
},
"oura": "Oura",
"ouraDescription": "Connect your Oura account to sync readiness, sleep stages, HRV, and daily activity.",
+ "ouraViewData": "View your sleep & recovery",
"ouraHelp": "Sign in with Oura to grant access. Data is pulled in the background once an hour.",
"ouraUnavailable": "Oura is not configured on this server. Ask your administrator to add the Oura OAuth credentials.",
"ouraConnect": "Connect Oura",
diff --git a/messages/es.json b/messages/es.json
index ac43c3815..ac2bf14e3 100644
--- a/messages/es.json
+++ b/messages/es.json
@@ -4570,6 +4570,7 @@
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
"nightscoutDescription": "Conecta tu propio sitio Nightscout para sincronizar lecturas continuas de glucosa en el panel de glucosa.",
+ "nightscoutViewData": "Ver tu glucosa",
"nightscoutHelp": "Apunta HealthLog a tu instancia de Nightscout. Las lecturas se obtienen en segundo plano una vez por hora.",
"nightscoutUrl": "URL de Nightscout",
"nightscoutUrlSavedPlaceholder": "Guardado: introduce una URL nueva para reemplazarla",
@@ -4588,6 +4589,7 @@
"nightscoutDisconnectDescription": "Las sincronizaciones futuras se detienen. Las lecturas de glucosa ya importadas permanecen en tu historial.",
"polar": "Polar",
"polarDescription": "Conecta tu cuenta de Polar para sincronizar la recuperación Nightly Recharge, las fases de sueño y la actividad diaria.",
+ "polarViewData": "Ver tu sueño y recuperación",
"polarHelp": "Inicia sesión en Polar para conceder acceso. Los datos se obtienen en segundo plano una vez por hora.",
"polarUnavailable": "Polar no está configurado en este servidor. Pide a tu administrador que añada las credenciales OAuth de Polar.",
"polarConnect": "Conectar Polar",
@@ -4608,6 +4610,7 @@
},
"oura": "Oura",
"ouraDescription": "Conecta tu cuenta de Oura para sincronizar readiness, fases de sueño, VFC y actividad diaria.",
+ "ouraViewData": "Ver tu sueño y recuperación",
"ouraHelp": "Inicia sesión en Oura para conceder acceso. Los datos se obtienen en segundo plano una vez por hora.",
"ouraUnavailable": "Oura no está configurado en este servidor. Pide a tu administrador que añada las credenciales OAuth de Oura.",
"ouraConnect": "Conectar Oura",
diff --git a/messages/fr.json b/messages/fr.json
index 1f8eaa98e..182ec080b 100644
--- a/messages/fr.json
+++ b/messages/fr.json
@@ -4570,6 +4570,7 @@
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
"nightscoutDescription": "Connectez votre propre site Nightscout pour synchroniser les mesures de glycémie en continu dans le panneau glucose.",
+ "nightscoutViewData": "Voir votre glycémie",
"nightscoutHelp": "Pointez HealthLog vers votre instance Nightscout. Les mesures sont récupérées en arrière-plan une fois par heure.",
"nightscoutUrl": "URL Nightscout",
"nightscoutUrlSavedPlaceholder": "Enregistré — saisissez une nouvelle URL pour la remplacer",
@@ -4588,6 +4589,7 @@
"nightscoutDisconnectDescription": "Les synchronisations futures s'arrêtent. Les mesures de glycémie déjà importées restent dans votre historique.",
"polar": "Polar",
"polarDescription": "Connectez votre compte Polar pour synchroniser la récupération Nightly Recharge, les phases de sommeil et l'activité quotidienne.",
+ "polarViewData": "Voir votre sommeil et récupération",
"polarHelp": "Connectez-vous à Polar pour accorder l'accès. Les données sont récupérées en arrière-plan une fois par heure.",
"polarUnavailable": "Polar n'est pas configuré sur ce serveur. Demandez à votre administrateur d'ajouter les identifiants OAuth Polar.",
"polarConnect": "Connecter Polar",
@@ -4608,6 +4610,7 @@
},
"oura": "Oura",
"ouraDescription": "Connectez votre compte Oura pour synchroniser le readiness, les phases de sommeil, la VFC et l'activité quotidienne.",
+ "ouraViewData": "Voir votre sommeil et récupération",
"ouraHelp": "Connectez-vous à Oura pour accorder l'accès. Les données sont récupérées en arrière-plan une fois par heure.",
"ouraUnavailable": "Oura n'est pas configuré sur ce serveur. Demandez à votre administrateur d'ajouter les identifiants OAuth Oura.",
"ouraConnect": "Connecter Oura",
diff --git a/messages/it.json b/messages/it.json
index bc5a81c1c..4d833acfb 100644
--- a/messages/it.json
+++ b/messages/it.json
@@ -4570,6 +4570,7 @@
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
"nightscoutDescription": "Collega il tuo sito Nightscout per sincronizzare le letture continue della glicemia nel pannello glicemia.",
+ "nightscoutViewData": "Vedi la tua glicemia",
"nightscoutHelp": "Punta HealthLog alla tua istanza Nightscout. Le letture vengono recuperate in background una volta all'ora.",
"nightscoutUrl": "URL Nightscout",
"nightscoutUrlSavedPlaceholder": "Salvato — inserisci un nuovo URL per sostituirlo",
@@ -4588,6 +4589,7 @@
"nightscoutDisconnectDescription": "Le sincronizzazioni future si interrompono. Le letture della glicemia già importate restano nella tua cronologia.",
"polar": "Polar",
"polarDescription": "Collega il tuo account Polar per sincronizzare il recupero Nightly Recharge, le fasi del sonno e l'attività giornaliera.",
+ "polarViewData": "Vedi sonno e recupero",
"polarHelp": "Accedi a Polar per concedere l'accesso. I dati vengono recuperati in background una volta all'ora.",
"polarUnavailable": "Polar non è configurato su questo server. Chiedi al tuo amministratore di aggiungere le credenziali OAuth di Polar.",
"polarConnect": "Collega Polar",
@@ -4608,6 +4610,7 @@
},
"oura": "Oura",
"ouraDescription": "Collega il tuo account Oura per sincronizzare readiness, fasi del sonno, HRV e attività giornaliera.",
+ "ouraViewData": "Vedi sonno e recupero",
"ouraHelp": "Accedi a Oura per concedere l'accesso. I dati vengono recuperati in background una volta all'ora.",
"ouraUnavailable": "Oura non è configurato su questo server. Chiedi al tuo amministratore di aggiungere le credenziali OAuth di Oura.",
"ouraConnect": "Collega Oura",
diff --git a/messages/pl.json b/messages/pl.json
index e66363b37..13147adea 100644
--- a/messages/pl.json
+++ b/messages/pl.json
@@ -4570,6 +4570,7 @@
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
"nightscoutDescription": "Połącz własną witrynę Nightscout, aby synchronizować ciągłe odczyty glukozy w panelu glukozy.",
+ "nightscoutViewData": "Zobacz swoją glukozę",
"nightscoutHelp": "Wskaż HealthLog na swoją instancję Nightscout. Odczyty są pobierane w tle raz na godzinę.",
"nightscoutUrl": "Adres URL Nightscout",
"nightscoutUrlSavedPlaceholder": "Zapisano — wpisz nowy adres URL, aby go zastąpić",
@@ -4588,6 +4589,7 @@
"nightscoutDisconnectDescription": "Przyszłe synchronizacje zostaną zatrzymane. Już zaimportowane odczyty glukozy pozostaną w historii.",
"polar": "Polar",
"polarDescription": "Połącz konto Polar, aby synchronizować regenerację Nightly Recharge, fazy snu i aktywność dzienną.",
+ "polarViewData": "Zobacz sen i regenerację",
"polarHelp": "Zaloguj się w Polar, aby przyznać dostęp. Dane są pobierane w tle raz na godzinę.",
"polarUnavailable": "Polar nie jest skonfigurowany na tym serwerze. Poproś administratora o dodanie danych OAuth Polar.",
"polarConnect": "Połącz Polar",
@@ -4608,6 +4610,7 @@
},
"oura": "Oura",
"ouraDescription": "Połącz konto Oura, aby synchronizować readiness, fazy snu, HRV i aktywność dzienną.",
+ "ouraViewData": "Zobacz sen i regenerację",
"ouraHelp": "Zaloguj się w Oura, aby przyznać dostęp. Dane są pobierane w tle raz na godzinę.",
"ouraUnavailable": "Oura nie jest skonfigurowana na tym serwerze. Poproś administratora o dodanie danych OAuth Oura.",
"ouraConnect": "Połącz Oura",
diff --git a/src/components/settings/integrations/__tests__/nightscout-card.test.tsx b/src/components/settings/integrations/__tests__/nightscout-card.test.tsx
new file mode 100644
index 000000000..6b5a6b0ac
--- /dev/null
+++ b/src/components/settings/integrations/__tests__/nightscout-card.test.tsx
@@ -0,0 +1,85 @@
+/**
+ * v1.17.0 — interaction-parity + correctness checks for the Nightscout card.
+ *
+ * 1. A `parked` status renders the warning banner + reconnect button
+ * (matching the WHOOP card's parked treatment, byte-for-byte classes).
+ * 2. A `connected` status renders the shared TestConnectionButton AND the
+ * connect→data link to /insights/blood-glucose.
+ * 3. The disconnect mutation invalidates `nightscoutStatus()` — the key the
+ * status query actually reads — so the pill refreshes after disconnect.
+ */
+import { describe, expect, it, vi } from "vitest";
+import { renderToStaticMarkup } from "react-dom/server";
+
+import { queryKeys } from "@/lib/query-keys";
+
+// Capture the disconnect mutation's onSuccess so the test can assert which
+// query keys it invalidates. Status is driven off a per-test payload.
+let statusPayload: unknown = null;
+const invalidateSpy = vi.fn();
+type OnSuccess = () => void;
+const capturedDisconnectOnSuccess: { fn: OnSuccess | null } = { fn: null };
+
+vi.mock("@tanstack/react-query", () => ({
+ useQuery: () => ({ data: statusPayload, isLoading: false }),
+ useMutation: ({ onSuccess }: { onSuccess?: () => void }) => {
+ // The Nightscout card declares exactly one mutation (disconnect).
+ capturedDisconnectOnSuccess.fn = onSuccess ?? null;
+ return { mutate: vi.fn(), isPending: false };
+ },
+ useQueryClient: () => ({ invalidateQueries: invalidateSpy }),
+}));
+
+import { I18nProvider } from "@/lib/i18n/context";
+import { NightscoutCard } from "../nightscout-card";
+
+function render() {
+ return renderToStaticMarkup(
+
{t("settings.nightscoutHelp")}
{t(`${i18nPrefix}Help`)}
@@ -154,40 +192,57 @@ export function OAuthProviderCard({ )} {status?.connected ? ( -