From a9bec5edd259b0e7861d51cfe5d2478122651c48 Mon Sep 17 00:00:00 2001 From: rsnetworkinginc Date: Sun, 26 Jul 2026 07:37:59 +0300 Subject: [PATCH] fix(ui): resolve the slop/duplicate trend legends' latest week per series latestWeekWithSignal found the single most recent week with ANY signal and the card reused that one week for both legends. The two rates are independently nullable, so when the newest signal-bearing week had one series null, that legend silently showed no band / an em-dash even when an earlier week held real data for it. The lookup is now parameterized by series and each legend resolves its own latest signal-bearing week; lockstep-null data (today's backend shape) renders identically. Closes #8667 --- .../slop-duplicate-trend-card-model.ts | 7 +- .../slop-duplicate-trend-card.test.tsx | 108 +++++++++++++++++- .../app-panels/slop-duplicate-trend-card.tsx | 15 ++- 3 files changed, 122 insertions(+), 8 deletions(-) diff --git a/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card-model.ts b/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card-model.ts index a4bc515a89..dffdf1352c 100644 --- a/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card-model.ts +++ b/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card-model.ts @@ -45,13 +45,18 @@ export function trendHasAnySignal(weeks: SlopDuplicateTrendWeek[]): boolean { return seriesHasSignal(weeks, "slop") || seriesHasSignal(weeks, "duplicate"); } +/** Latest week bearing a signal for ONE series. The two rates are independently nullable, so each + * legend must resolve its own latest week — a single shared "any signal" lookup would let one + * series' null in the newest signal-bearing week hide an older real value for the other (#8667). */ export function latestWeekWithSignal( weeks: SlopDuplicateTrendWeek[], + series: "slop" | "duplicate", ): SlopDuplicateTrendWeek | null { for (let index = weeks.length - 1; index >= 0; index -= 1) { const week = weeks[index]; if (!week) continue; - if (week.slopFlagRatePct !== null || week.duplicateFlagRatePct !== null) return week; + const value = series === "slop" ? week.slopFlagRatePct : week.duplicateFlagRatePct; + if (value !== null) return week; } return null; } diff --git a/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card.test.tsx b/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card.test.tsx index 0b1417a0f2..50c1daf246 100644 --- a/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card.test.tsx +++ b/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card.test.tsx @@ -2,7 +2,11 @@ import { render, screen } from "@testing-library/react"; import { describe, expect, it } from "vitest"; import { SlopDuplicateTrendCard } from "@/components/site/app-panels/slop-duplicate-trend-card"; -import type { MaintainerSlopDuplicateTrend } from "@/components/site/app-panels/slop-duplicate-trend-card-model"; +import { + latestWeekWithSignal, + type MaintainerSlopDuplicateTrend, + type SlopDuplicateTrendWeek, +} from "@/components/site/app-panels/slop-duplicate-trend-card-model"; function trend( overrides: Partial = {}, @@ -86,4 +90,106 @@ describe("SlopDuplicateTrendCard", () => { render(); expect(screen.getByText(/stale snapshot/i)).toBeTruthy(); }); + + // The two series are independently nullable (#8667): each legend must resolve its OWN latest + // signal-bearing week, not share the single most-recent week that has *any* signal. + it("keeps the slop legend on its own latest signal week when the newest signal week only has duplicate data", () => { + render( + , + ); + // Slop legend reflects the EARLIER week's real band — not "latest: —" from the newest week's null. + expect(screen.getByText(/latest band: elevated/i)).toBeTruthy(); + expect(screen.queryByText("latest: —")).toBeNull(); + // Duplicate legend independently reflects the newest week's own value. + expect(screen.getByText(/latest: 12\.5%/i)).toBeTruthy(); + }); + + it("keeps the duplicate legend on its own latest signal week when the newest signal week only has slop data", () => { + render( + , + ); + // Duplicate legend reflects the EARLIER week's real value — not the newest week's null. + expect(screen.getByText(/latest: 25%/i)).toBeTruthy(); + expect(screen.queryByText("latest: —")).toBeNull(); + // Slop legend independently reflects the newest week's own band. + expect(screen.getByText(/latest band: low/i)).toBeTruthy(); + }); +}); + +describe("latestWeekWithSignal", () => { + const week = ( + weekStart: string, + slopFlagRatePct: number | null, + duplicateFlagRatePct: number | null, + ): SlopDuplicateTrendWeek => ({ + weekStart, + slopFlagRatePct, + slopBandLabel: slopFlagRatePct === null ? null : "low", + duplicateFlagRatePct, + }); + + it("resolves a different latest week per series when the two series' nulls diverge", () => { + const weeks = [week("2026-06-02", 40, null), week("2026-06-09", null, 12.5)]; + expect(latestWeekWithSignal(weeks, "slop")?.weekStart).toBe("2026-06-02"); + expect(latestWeekWithSignal(weeks, "duplicate")?.weekStart).toBe("2026-06-09"); + }); + + it("returns the same (newest) week for both series when null-ness stays in lockstep", () => { + const weeks = [ + week("2026-06-02", 30, 20), + week("2026-06-09", null, null), + week("2026-06-16", 10, 5), + ]; + expect(latestWeekWithSignal(weeks, "slop")?.weekStart).toBe("2026-06-16"); + expect(latestWeekWithSignal(weeks, "duplicate")?.weekStart).toBe("2026-06-16"); + }); + + it("returns null for a series with no signal in any week, independently of the other series", () => { + const weeks = [week("2026-06-02", 40, null), week("2026-06-09", 10, null)]; + expect(latestWeekWithSignal(weeks, "slop")?.weekStart).toBe("2026-06-09"); + expect(latestWeekWithSignal(weeks, "duplicate")).toBeNull(); + expect(latestWeekWithSignal([], "slop")).toBeNull(); + }); + + it("skips sparse holes while scanning backwards", () => { + const weeks: SlopDuplicateTrendWeek[] = []; + weeks[0] = week("2026-06-02", 40, 25); + weeks.length = 2; // trailing hole — scanned first, must be skipped, not crashed on + expect(latestWeekWithSignal(weeks, "slop")?.weekStart).toBe("2026-06-02"); + expect(latestWeekWithSignal(weeks, "duplicate")?.weekStart).toBe("2026-06-02"); + }); }); diff --git a/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card.tsx b/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card.tsx index 7e01136f94..f16975bd30 100644 --- a/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card.tsx +++ b/apps/loopover-ui/src/components/site/app-panels/slop-duplicate-trend-card.tsx @@ -26,7 +26,10 @@ export function SlopDuplicateTrendCard({ trend }: { trend: MaintainerSlopDuplica const hasSignal = trendHasAnySignal(trend.weeks); const hasSlop = seriesHasSignal(trend.weeks, "slop"); const hasDuplicate = seriesHasSignal(trend.weeks, "duplicate"); - const latest = latestWeekWithSignal(trend.weeks); + // Each legend resolves its own latest signal-bearing week — the two series are independently + // nullable, so a shared lookup could surface one series' null over the other's real data (#8667). + const latestSlop = latestWeekWithSignal(trend.weeks, "slop"); + const latestDuplicate = latestWeekWithSignal(trend.weeks, "duplicate"); return (