-
-
-
-
-
-
- {/* Mini-chart pinned to the rec's window. mood + medication-
- compliance get dedicated wrappers; everything else routes
- through HealthChart with the metric-key vocabulary. */}
- {isMoodMetric(metricType) ? (
-
- ) : isComplianceMetric(metricType) ? null : chartTypes.length > 0 ? (
-
- ) : null}
-
- {showLowConfidenceCaption && (
-
- {t("insights.recommendation.confidenceLow")}
-
- )}
-
- {referenceId && (
-
-
-
- )}
-
- {/* v1.4.16 phase B5e — fills the rec-feedback-slot reserved by
- * B5c. The feedback row only appears when the rec carries the
- * full attribute set the API endpoint requires; legacy recs
- * without an id stay un-rateable (the empty slot keeps the DOM
- * stable for downstream tests). */}
-
- {feedbackProps && }
-
-
- );
-}
-
-/**
- * Validate the rec's metricSource.timeRange against the four-window
- * vocabulary the feedback endpoint accepts. Returns null when the
- * value is missing or out-of-vocabulary so the feedback slot stays
- * empty rather than rendering a feedback row that would 422 on
- * submit.
- */
-function asFeedbackTimeRange(
- value: string | undefined,
-): RecommendationFeedbackTimeRange | null {
- switch (value) {
- case "last7days":
- case "last30days":
- case "last90days":
- case "allTime":
- return value;
- default:
- return null;
- }
-}
-
-export function RecommendationCard({
- rec,
- index,
- initiallyExpanded = false,
-}: RecommendationCardProps) {
- const { t, locale } = useTranslations();
- const norm = normalise(rec);
- const expandable = norm.rationale !== undefined;
- const [expanded, setExpanded] = useState(initiallyExpanded);
-
- // Feedback row is only renderable when the rec carries every
- // attribute the API endpoint validates. Legacy / partial recs
- // (no id, no severity, or an unknown timeRange) leave the slot
- // empty so a thumbs-click can never produce a 422.
- const feedbackTimeRange = asFeedbackTimeRange(norm.metricSource?.timeRange);
- const feedbackProps =
- norm.id && norm.severity && norm.metricSource?.type && feedbackTimeRange
- ? {
- recId: norm.id,
- recText: norm.text,
- recSeverity: norm.severity,
- metricSourceType: norm.metricSource.type,
- metricSourceTimeRange: feedbackTimeRange,
- }
- : null;
-
- // v1.21.0 (C4 H2) — reverse-direction Coach hand-off. The opener
- // quotes the rec so the conversation lands on this exact suggestion;
- // when the rec carries a metric source, scope the snapshot to it.
- const coachScopeSource = scopeSourceFromMetricKey(norm.metricSource?.type);
- const coachQuestion = t("insights.coach.seed.recommendation", {
- text: stripChartTokens(norm.text),
- });
-
- return (
-
-
-
-
- {index + 1}.
-
-
-
- {norm.severity && (
-
- {norm.severity}
-
- )}
-
- {stripChartTokens(norm.text)}
-
-
-
-
-
-
- {typeof norm.confidence === "number" && (
-
- )}
-
- {expandable && (
-
- )}
-
-
-
- {expandable && expanded && norm.rationale && (
-
-
-
- )}
-
- {/* Inline-only citation footnote: a legacy rec (no rationale)
- * still surfaces its medical reference under the rec text.
- * Recs WITH rationale only render the footnote inside the
- * expanded RationaleCard so it doesn't double-print. */}
- {!expandable && norm.referenceId && (
-
-
-
- )}
-
- {/* v1.21.0 (C4 H2) — discreet "ask the Coach about this rec" link,
- aligned under the rec text (the number gutter is `ml-6`). */}
-
-
- );
-}
diff --git a/src/components/insights/recommendation-feedback.tsx b/src/components/insights/recommendation-feedback.tsx
deleted file mode 100644
index df8a73fcb..000000000
--- a/src/components/insights/recommendation-feedback.tsx
+++ /dev/null
@@ -1,291 +0,0 @@
-"use client";
-
-/**
- * v1.4.16 phase B5e — RecommendationFeedback.
- *
- * Two small icon buttons (thumb-up / thumb-down) under each rec card,
- * surfaced in the `data-slot="rec-feedback-slot"` slot reserved by
- * B5c's `