diff --git a/messages/de.json b/messages/de.json index 146bbed3a..9307de5f8 100644 --- a/messages/de.json +++ b/messages/de.json @@ -6107,7 +6107,10 @@ "frequencyAria": "Häufigkeit der Coach-Hinweise wählen", "enabledToast": "Proaktive Coach-Hinweise aktiviert.", "disabledToast": "Proaktive Coach-Hinweise deaktiviert.", - "saveError": "Speichern fehlgeschlagen. Bitte später erneut versuchen." + "saveError": "Speichern fehlgeschlagen. Bitte später erneut versuchen.", + "suggestionsLabel": "Beispielvorschläge", + "suggestionsDesc": "Zeigt auf dem Coach-Bildschirm gelegentlich eine fertige Beispielfrage.", + "suggestionsAria": "Beispiel-Coach-Vorschläge umschalten" }, "lowStock": { "title": "Vorratswarnung", diff --git a/messages/en.json b/messages/en.json index 23ae4f2b7..716477da3 100644 --- a/messages/en.json +++ b/messages/en.json @@ -6107,7 +6107,10 @@ "frequencyAria": "Select nudge frequency", "enabledToast": "Proactive Coach nudges enabled.", "disabledToast": "Proactive Coach nudges disabled.", - "saveError": "Could not save. Please try again later." + "saveError": "Could not save. Please try again later.", + "suggestionsLabel": "Example suggestions", + "suggestionsDesc": "Show the occasional ready-made example question on the Coach screen.", + "suggestionsAria": "Toggle example Coach suggestions" }, "lowStock": { "title": "Low-stock alert", diff --git a/messages/es.json b/messages/es.json index e2f12b186..fe9e716ea 100644 --- a/messages/es.json +++ b/messages/es.json @@ -6107,7 +6107,10 @@ "statusOff": "Desactivado", "enabledToast": "Avisos proactivos del Coach activados.", "disabledToast": "Avisos proactivos del Coach desactivados.", - "saveError": "No se pudo guardar. Inténtalo de nuevo más tarde." + "saveError": "No se pudo guardar. Inténtalo de nuevo más tarde.", + "suggestionsLabel": "Sugerencias de ejemplo", + "suggestionsDesc": "Muestra de vez en cuando una pregunta de ejemplo en la pantalla del Coach.", + "suggestionsAria": "Activar o desactivar las sugerencias de ejemplo del Coach" }, "lowStock": { "title": "Aviso de existencias bajas", diff --git a/messages/fr.json b/messages/fr.json index 5b953e533..46874950c 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -6107,7 +6107,10 @@ "statusOff": "Désactivé", "enabledToast": "Rappels proactifs du Coach activés.", "disabledToast": "Rappels proactifs du Coach désactivés.", - "saveError": "Impossible d'enregistrer. Veuillez réessayer plus tard." + "saveError": "Impossible d'enregistrer. Veuillez réessayer plus tard.", + "suggestionsLabel": "Suggestions d'exemples", + "suggestionsDesc": "Affiche de temps en temps une question d'exemple prête à l'emploi sur l'écran du Coach.", + "suggestionsAria": "Activer ou désactiver les suggestions d'exemples du Coach" }, "lowStock": { "title": "Alerte de stock faible", diff --git a/messages/it.json b/messages/it.json index 1c446bda3..8eda2e620 100644 --- a/messages/it.json +++ b/messages/it.json @@ -6107,7 +6107,10 @@ "statusOff": "Disattivato", "enabledToast": "Avvisi proattivi del Coach attivati.", "disabledToast": "Avvisi proattivi del Coach disattivati.", - "saveError": "Impossibile salvare. Riprova più tardi." + "saveError": "Impossibile salvare. Riprova più tardi.", + "suggestionsLabel": "Suggerimenti di esempio", + "suggestionsDesc": "Mostra di tanto in tanto una domanda di esempio pronta all'uso nella schermata del Coach.", + "suggestionsAria": "Attiva o disattiva i suggerimenti di esempio del Coach" }, "lowStock": { "title": "Avviso scorte basse", diff --git a/messages/pl.json b/messages/pl.json index cb6fd0d43..9bbc6dc27 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -6107,7 +6107,10 @@ "statusOff": "Wyłączone", "enabledToast": "Proaktywne podpowiedzi Coacha włączone.", "disabledToast": "Proaktywne podpowiedzi Coacha wyłączone.", - "saveError": "Nie udało się zapisać. Spróbuj ponownie później." + "saveError": "Nie udało się zapisać. Spróbuj ponownie później.", + "suggestionsLabel": "Przykładowe sugestie", + "suggestionsDesc": "Czasami pokazuje gotowe przykładowe pytanie na ekranie Coacha.", + "suggestionsAria": "Przełącz przykładowe sugestie Coacha" }, "lowStock": { "title": "Alert niskiego zapasu", diff --git a/src/app/api/auth/me/notification-prefs/__tests__/route.test.ts b/src/app/api/auth/me/notification-prefs/__tests__/route.test.ts index 46dbd045d..77824fcd8 100644 --- a/src/app/api/auth/me/notification-prefs/__tests__/route.test.ts +++ b/src/app/api/auth/me/notification-prefs/__tests__/route.test.ts @@ -102,6 +102,7 @@ describe("GET /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, @@ -137,6 +138,7 @@ describe("GET /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, @@ -174,6 +176,7 @@ describe("GET /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, @@ -222,6 +225,7 @@ describe("PATCH /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, @@ -246,6 +250,7 @@ describe("PATCH /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, @@ -274,6 +279,7 @@ describe("PATCH /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, @@ -294,6 +300,7 @@ describe("PATCH /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, @@ -367,6 +374,7 @@ describe("PATCH /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, @@ -405,6 +413,7 @@ describe("PATCH /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, @@ -429,6 +438,7 @@ describe("PATCH /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, @@ -472,6 +482,7 @@ describe("PATCH /api/auth/me/notification-prefs", () => { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }, measurementReminder: { clientManaged: false, diff --git a/src/components/insights/coach-panel/coach-conversation.tsx b/src/components/insights/coach-panel/coach-conversation.tsx index 0432e6a77..1457ed0fc 100644 --- a/src/components/insights/coach-panel/coach-conversation.tsx +++ b/src/components/insights/coach-panel/coach-conversation.tsx @@ -40,6 +40,7 @@ import { MobileRailTray } from "./mobile-rail-tray"; import { SelfContextAdoptOffer } from "./self-context-adopt-offer"; import { SourcesRail } from "./sources-rail"; import { useResettableValue } from "./use-resettable-value"; +import { useCoachAmbientSuggestionsEnabled } from "@/hooks/use-coach-ambient-suggestions"; import { useCoachConversation, useCoachConversations, @@ -516,8 +517,15 @@ export function CoachConversation({ return false; } }); + // v1.25.0 — the per-user opt-out for proactive ambient suggestions gates the + // seeded opener (and skips its fetch when off). The A2 launch scope below is + // NOT ambient — it reflects how the chat was opened — so it stays unaffected. + const ambientSuggestionsEnabled = useCoachAmbientSuggestionsEnabled(); const seededEnabled = - heroActive && a2Metric === null && !seededDismissedToday; + heroActive && + a2Metric === null && + !seededDismissedToday && + ambientSuggestionsEnabled; const { data: seeded } = useQuery({ queryKey: queryKeys.coachSeededQuestion(), queryFn: async () => @@ -574,7 +582,11 @@ export function CoachConversation({ onSeed={seedComposer} /> ); - } else if (seeded?.signal && !seededDismissedToday) { + } else if ( + seeded?.signal && + !seededDismissedToday && + ambientSuggestionsEnabled + ) { // A3 — the notable derived signal. The label + opener are keyed on the // signal's sourceMetric (`readiness` / `recovery`); an unknown sentinel // (future detector additions) skips the opener rather than guessing. diff --git a/src/components/settings/coach-nudge-card.tsx b/src/components/settings/coach-nudge-card.tsx index 61e2fc8d1..9cc6705d1 100644 --- a/src/components/settings/coach-nudge-card.tsx +++ b/src/components/settings/coach-nudge-card.tsx @@ -33,6 +33,7 @@ interface CoachPrefsShape { nudgeVitals: boolean; nudgeRoutine: boolean; nudgeFrequency: "weekly" | "biweekly"; + ambientSuggestions: boolean; } interface NotificationPrefsShape { @@ -45,6 +46,7 @@ const COACH_PREF_DEFAULTS: CoachPrefsShape = { nudgeVitals: true, nudgeRoutine: true, nudgeFrequency: "weekly", + ambientSuggestions: true, }; const GROUP_FIELDS = [ @@ -254,6 +256,28 @@ export function CoachNudgeCard({ )} + {/* v1.25.0 — proactive ambient SUGGESTIONS opt-out. Independent of the + push-nudge master switch above (a user can keep nudges but silence + the example prompts), so it sits outside the `enabled` gate and rides + the same `coach` prefs blob. */} +
+ {t("notifications.coachNudge.suggestionsLabel")} +
++ {t("notifications.coachNudge.suggestionsDesc")} +
+
+ apiGet