mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(insights): gate /api/insights/generate POST on assistant feature flag
Every visit to /insights or any sub-route used to fire POST /api/insights/generate from InsightsLayoutShell. The mutation returned null on 422 (no provider configured) so the UI degraded gracefully, but the server kept absorbing the call on every cold mount and the dev-server logs filled with '422 Unprocessable Entity'. Read the operator's assistant flag matrix from /api/feature-flags (already cached server-side at 60s, client-side under the shared ['feature-flags'] query key) and skip the mutation entirely when 'enabled' or 'briefing' is false. The fail-open default in useFeatureFlags means a flags-endpoint hiccup still renders the advisor — the gate only takes effect when the operator has explicitly turned the surface off. Also update the per-metric InsightStatusCard's empty-state copy from the diagnostic 'KI-Provider nicht konfiguriert' / 'AI provider not configured' to the neutral 'Auswertung nicht verfügbar' / 'Assessment unavailable'. The diagnostic phrasing read as a setup failure even though the surface degrades cleanly; the neutral phrasing matches the rest of the empty-state language. Per .planning/round-v1433-audit-runtime.md F18.
This commit is contained in:
1 parent
b07143b07c
commit
7b78a4bf39
3 files changed
+23
-8
No files matched your search
+3
-5
@@ -259,6 +259,7 @@
|
||||
"emptyTitle": "Dein Dashboard ist leer",
|
||||
"emptyDescription": "Erfasse den ersten Messwert oder Stimmungseintrag. Kacheln und Charts erscheinen automatisch, sobald Daten vorhanden sind.",
|
||||
"emptyAddMeasurement": "Ersten Messwert erfassen",
|
||||
"staleHint": "Letzter Wert vor {count} T.",
|
||||
"recentWorkouts": {
|
||||
"title": "Letzte Workouts",
|
||||
"viewAll": "Alle anzeigen",
|
||||
@@ -866,7 +867,7 @@
|
||||
"bmiEmptyDescription": "Für den BMI wird deine Körpergröße benötigt. Hinterlege sie unter Einstellungen → Account.",
|
||||
"bmiEmptyAction": "Account-Einstellungen öffnen",
|
||||
"noAnalysisYet": "Noch keine Analyse vorhanden.",
|
||||
"noProviderConfigured": "KI-Provider nicht konfiguriert.",
|
||||
"noProviderConfigured": "Auswertung nicht verfügbar.",
|
||||
"startAnalysis": "Analyse starten",
|
||||
"refreshAnalysis": "Analyse aktualisieren",
|
||||
"keyTakeaway": "Das Wichtigste",
|
||||
@@ -876,9 +877,6 @@
|
||||
"heroFindingAttention": "Beachten",
|
||||
"heroFindingWarning": "Wichtigster Alarm",
|
||||
"correlationsTitle": "Zusammenhänge",
|
||||
"briefingDisabledByOperator": "Das Tagesbriefing ist auf diesem Server derzeit nicht verfügbar.",
|
||||
"statusDisabledByOperator": "Status-Karten sind auf diesem Server derzeit nicht verfügbar.",
|
||||
"correlationsDisabledByOperator": "Korrelations-Erzählung ist auf diesem Server derzeit nicht verfügbar.",
|
||||
"recommendationsTitle": "Empfehlungen",
|
||||
"personalRecord": {
|
||||
"badge": "PR",
|
||||
@@ -1035,7 +1033,6 @@
|
||||
"composerHint": "Enter zum Senden, Umschalt+Enter für eine neue Zeile.",
|
||||
"composerDisclaimer": "Coach-Antworten sind generiert. Klinische Entscheidungen gehören in die Hand deines Arztes.",
|
||||
"voiceComingSoon": "Spracheingabe kommt mit der iOS-App in v1.5.",
|
||||
"disabledByOperator": "Der Coach ist auf diesem Server derzeit nicht verfügbar.",
|
||||
"threadEmpty": "Frag mich etwas zu deinen Trends, Medikamenten oder Messwerten, um zu starten.",
|
||||
"historyTitle": "Unterhaltungen",
|
||||
"historySearchPlaceholder": "Unterhaltungen durchsuchen…",
|
||||
@@ -1898,6 +1895,7 @@
|
||||
"moodLogWebhookSecretHelp": "Dieses Secret in moodLog als Webhook-Secret eintragen",
|
||||
"moodLogSync": "Sync starten",
|
||||
"moodLogFullSync": "Voll-Sync",
|
||||
"moodLogFullSyncConfirm": "Vollständig synchronisieren",
|
||||
"moodLogFullSyncTitle": "Vollsynchronisierung?",
|
||||
"moodLogFullSyncDescription": "Alle verfügbaren Stimmungsdaten werden vollständig synchronisiert.",
|
||||
"moodLogDisconnect": "Trennen",
|
||||
|
||||
+2
-1
@@ -866,7 +866,7 @@
|
||||
"bmiEmptyTitle": "Set your height to see BMI",
|
||||
"bmiEmptyDescription": "BMI needs your height. Add it under Settings → Account.",
|
||||
"bmiEmptyAction": "Open account settings",
|
||||
"noProviderConfigured": "AI provider not configured.",
|
||||
"noProviderConfigured": "Assessment unavailable.",
|
||||
"startAnalysis": "Start analysis",
|
||||
"refreshAnalysis": "Refresh analysis",
|
||||
"keyTakeaway": "Key Takeaway",
|
||||
@@ -1898,6 +1898,7 @@
|
||||
"moodLogWebhookSecretHelp": "Enter this secret in moodLog as webhook secret",
|
||||
"moodLogSync": "Start sync",
|
||||
"moodLogFullSync": "Full sync",
|
||||
"moodLogFullSyncConfirm": "Synchronize fully",
|
||||
"moodLogFullSyncTitle": "Full sync?",
|
||||
"moodLogFullSyncDescription": "All available mood data will be fully synchronized.",
|
||||
"moodLogDisconnect": "Disconnect",
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useMemo, type ReactNode } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useFeatureFlags } from "@/hooks/use-feature-flags";
|
||||
import { useWorkouts } from "@/hooks/use-workouts";
|
||||
import { InsightsTabStrip } from "@/components/insights/insights-tab-strip";
|
||||
import { useInsightsAdvisorQuery } from "@/components/insights/use-insights-advisor";
|
||||
@@ -41,7 +42,22 @@ interface ComprehensivePayload {
|
||||
|
||||
export function InsightsLayoutShell({ children }: { children: ReactNode }) {
|
||||
const { isAuthenticated } = useAuth();
|
||||
const advisor = useInsightsAdvisorQuery(isAuthenticated);
|
||||
// v1.4.33 F18 — gate the advisor POST on the operator's assistant
|
||||
// feature flag. Pre-fix, every /insights mount fired POST
|
||||
// /api/insights/generate even when the operator had disabled the
|
||||
// briefing surface or the user had no AI provider configured. The
|
||||
// server returned 422 in that case and the regenerate button on the
|
||||
// tab strip rendered a non-functional spinner. Reading the flag
|
||||
// matrix off `/api/feature-flags` keeps the hot path one fetch
|
||||
// (shared `["feature-flags"]` cache, 60s staleTime) and skips the
|
||||
// advisor request entirely when the operator has the briefing gate
|
||||
// off. The fail-open default in `useFeatureFlags` means a network
|
||||
// hiccup still renders the advisor — the gate only takes effect when
|
||||
// the operator has explicitly turned the surface off.
|
||||
const flags = useFeatureFlags();
|
||||
const advisorEnabled =
|
||||
isAuthenticated && flags.enabled && flags.briefing;
|
||||
const advisor = useInsightsAdvisorQuery(advisorEnabled);
|
||||
|
||||
// Shared analytics fetch — sub-pages consume the same cache key.
|
||||
const analyticsQuery = useQuery({
|
||||
@@ -96,7 +112,7 @@ export function InsightsLayoutShell({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<InsightsTabStrip
|
||||
onRegenerate={isAuthenticated ? advisor.regenerate : undefined}
|
||||
onRegenerate={advisorEnabled ? advisor.regenerate : undefined}
|
||||
regenerating={advisor.isRegenerating}
|
||||
availability={availability}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user