diff --git a/src/components/insights/insight-status-card.tsx b/src/components/insights/insight-status-card.tsx index a7f31f60c..6718a4178 100644 --- a/src/components/insights/insight-status-card.tsx +++ b/src/components/insights/insight-status-card.tsx @@ -17,6 +17,7 @@ import { cn } from "@/lib/utils"; import { useFeatureFlags } from "@/hooks/use-feature-flags"; import { AskCoachIconButton } from "@/components/insights/ask-coach-action"; import type { CoachLaunchScope } from "@/lib/insights/coach-launch-context"; +import type { AssessmentStatus } from "@/lib/insights/status-shared"; // ─── Types ──────────────────────────────────────────────── @@ -62,6 +63,12 @@ interface InsightStatusCardProps { * card is being assembled, not stuck. The client polls until text lands. */ preparing?: boolean; + /** + * Explicit producer/hook state. Legacy scalar props remain supported for + * direct mounts while routes roll forward, but this discriminator owns + * branch ordering whenever it is present. + */ + assessment?: AssessmentStatus; /** * v1.21.0 (C4 H2) — opt-in "Ask the Coach about this assessment" hand-off. * When `coachQuestion` is set (the metric-aware caller supplies it), the @@ -88,6 +95,7 @@ export function InsightStatusCard({ updatedAt, loading = false, preparing = false, + assessment, coachQuestion, coachScope, coachAutoSend, @@ -99,12 +107,22 @@ export function InsightStatusCard({ // suppressed in full so the layout collapses naturally. if (!flags.insightStatus) return null; + const displayText = + assessment?.kind === "generated" || + assessment?.kind === "screened-fallback" || + assessment?.kind === "revalidating" + ? assessment.text + : text; + const isPreparing = + assessment?.kind === "preparing" || + (!assessment && preparing && hasProvider && !displayText); + // v1.8.3 — preparing: the read-only route enqueued a generation and the // client is polling. Render the loading skeleton geometry with a // preparing caption so the card reads as "assembling", not stuck. Only // shown when a provider is configured (the route returns the no-key // fallback otherwise). - if (preparing && hasProvider && !text) { + if (isPreparing) { return ( + + + + +

{message}

+
+
+ ); + } + + const isNoProvider = + assessment?.kind === "no-provider" || + (!assessment && !hasProvider && !displayText); + if (isNoProvider) { return ( @@ -187,7 +238,7 @@ export function InsightStatusCard({ ); } - if (!text) { + if (!displayText) { return ( @@ -255,7 +306,7 @@ export function InsightStatusCard({ token surfaces verbatim. The producer side now strips too; this consumer-side wrap keeps existing caches clean while they roll forward. */} - + diff --git a/src/components/insights/metric-status-card.tsx b/src/components/insights/metric-status-card.tsx index 7a6a30956..ef7532727 100644 --- a/src/components/insights/metric-status-card.tsx +++ b/src/components/insights/metric-status-card.tsx @@ -66,6 +66,7 @@ export function MetricStatusCard({ text={status?.text ?? null} hasProvider={status?.hasProvider ?? false} updatedAt={status?.updatedAt ?? null} + assessment={status?.assessment} coachQuestion={coachQuestion} coachScope={coachSource ? { metric: coachSource } : undefined} coachAutoSend diff --git a/src/components/insights/slug-insight-status-card.tsx b/src/components/insights/slug-insight-status-card.tsx index d73bd2080..f64bcca14 100644 --- a/src/components/insights/slug-insight-status-card.tsx +++ b/src/components/insights/slug-insight-status-card.tsx @@ -71,6 +71,7 @@ export function SlugInsightStatusCard({ text={status?.text ?? null} hasProvider={status?.hasProvider ?? false} updatedAt={status?.updatedAt ?? null} + assessment={status?.assessment} coachQuestion={t("insights.coach.assessmentPrompt", { metric: t(coach.labelKey), })}