diff --git a/src/app/insights/page-client.tsx b/src/app/insights/page-client.tsx
index 936a33c09..d161b00e6 100644
--- a/src/app/insights/page-client.tsx
+++ b/src/app/insights/page-client.tsx
@@ -74,6 +74,9 @@ function BlockSkeleton({
);
}
+/** Stable reference so the trends row does not re-select on every render. */
+const HIDDEN_MOOD = ["mood"] as const;
+
const DailyBriefing = dynamic(
() =>
import("@/components/insights/daily-briefing").then((mod) => ({
@@ -513,6 +516,9 @@ export default function InsightsPageClient() {
trends: (
diff --git a/src/components/insights/trends-row.tsx b/src/components/insights/trends-row.tsx
index 7ed4e976d..85d8255a8 100644
--- a/src/components/insights/trends-row.tsx
+++ b/src/components/insights/trends-row.tsx
@@ -13,6 +13,7 @@ import {
selectTrendCharts,
type TrendAnnotationKey,
type TrendChartConfig,
+ type SelectTrendChartsOptions,
} from "@/lib/insights/trend-chart-select";
import {
TrendAnnotation,
@@ -65,6 +66,11 @@ const MoodChart = dynamic(
);
interface TrendsRowProps {
+ /**
+ * Metrics to leave out because their module is switched off (for
+ * example `mood`). Passed straight to `selectTrendCharts`.
+ */
+ hiddenMetrics?: SelectTrendChartsOptions["hiddenMetrics"];
/**
* Daily briefing payload. Drives the chart set: the row charts the
* metrics the briefing flags, in order, deduped + capped. `null` /
@@ -132,13 +138,14 @@ export function TrendsRow({
annotations,
confidence,
loading = false,
+ hiddenMetrics,
}: TrendsRowProps) {
const { t } = useTranslations();
// v1.8.5 — derive the chart set from the briefing. No new fetch: the
// briefing payload is already on the page (advisor cache), so this is
// a pure read that respects the v1.8.3 anti-freeze contract.
- const charts = selectTrendCharts(briefing);
+ const charts = selectTrendCharts(briefing, { hiddenMetrics });
// v1.4.36 W2 T3 — derive the tri-state status per metric from the
// advisor's loading flag + the annotation presence. Pending wins
diff --git a/src/lib/insights/__tests__/trend-chart-select.test.ts b/src/lib/insights/__tests__/trend-chart-select.test.ts
index 209422697..eb812dddb 100644
--- a/src/lib/insights/__tests__/trend-chart-select.test.ts
+++ b/src/lib/insights/__tests__/trend-chart-select.test.ts
@@ -157,3 +157,24 @@ describe("selectTrendCharts", () => {
expect(TREND_CHART_CONFIG.steps?.detailHref).toBe("/insights/steps");
});
});
+
+describe("selectTrendCharts — hidden metrics", () => {
+ it("drops mood from the fallback triple when the mood module is off", () => {
+ const charts = selectTrendCharts(null, { hiddenMetrics: ["mood"] });
+ expect(charts.map((c) => c.metric)).not.toContain("mood");
+ expect(charts).toHaveLength(2);
+ });
+
+ it("skips a hidden metric the briefing flags", () => {
+ const charts = selectTrendCharts(briefing(["mood", "weight"]), {
+ hiddenMetrics: ["mood"],
+ });
+ expect(charts.map((c) => c.metric)).not.toContain("mood");
+ expect(charts).toHaveLength(1);
+ });
+
+ it("keeps mood when nothing is hidden", () => {
+ const charts = selectTrendCharts(null, { hiddenMetrics: [] });
+ expect(charts.map((c) => c.metric)).toContain("mood");
+ });
+});
diff --git a/src/lib/insights/trend-chart-select.ts b/src/lib/insights/trend-chart-select.ts
index 31e04a6aa..2c3114ecb 100644
--- a/src/lib/insights/trend-chart-select.ts
+++ b/src/lib/insights/trend-chart-select.ts
@@ -267,16 +267,24 @@ export const DEFAULT_TREND_CHART_CAP = 3;
export interface SelectTrendChartsOptions {
/** Max number of charts to return. Defaults to {@link DEFAULT_TREND_CHART_CAP}. */
cap?: number;
+ /**
+ * Metrics whose module is switched off. They are skipped on both the
+ * briefing path and the fallback triple, so a disabled module never
+ * charts on the overview.
+ */
+ hiddenMetrics?: ReadonlyArray;
}
function configsFor(
metrics: ReadonlyArray,
cap: number,
+ hidden: ReadonlySet,
): TrendChartConfig[] {
const seen = new Set();
const out: TrendChartConfig[] = [];
for (const metric of metrics) {
if (out.length >= cap) break;
+ if (hidden.has(metric)) continue; // module switched off
const config = TREND_CHART_CONFIG[metric];
if (!config) continue; // metric has no standalone trend chart
if (seen.has(config.metric)) continue; // dedupe
@@ -301,14 +309,15 @@ export function selectTrendCharts(
options: SelectTrendChartsOptions = {},
): TrendChartConfig[] {
const cap = Math.max(1, options.cap ?? DEFAULT_TREND_CHART_CAP);
+ const hidden = new Set(options.hiddenMetrics ?? []);
const findingMetrics =
briefing?.keyFindings?.map((f) => f.sourceMetric) ?? [];
- const fromBriefing = configsFor(findingMetrics, cap);
+ const fromBriefing = configsFor(findingMetrics, cap, hidden);
if (fromBriefing.length > 0) {
return fromBriefing;
}
- return configsFor(DEFAULT_TREND_METRICS, cap);
+ return configsFor(DEFAULT_TREND_METRICS, cap, hidden);
}