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:
Marc-André Bombeck committed 2026-05-16 17:15:10 +02:00
1 parent b07143b07c
commit 7b78a4bf39
3 files changed
+23 -8

No files matched your search

@@ -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}
/>