diff --git a/apps/manager/src/features/coverage/coverage-report-client.tsx b/apps/manager/src/features/coverage/coverage-report-client.tsx index 2f6118692..f2a4f3e5e 100644 --- a/apps/manager/src/features/coverage/coverage-report-client.tsx +++ b/apps/manager/src/features/coverage/coverage-report-client.tsx @@ -1028,6 +1028,13 @@ export function CoverageReportClient({ ) const selectedVideoCount = selectedVideoIds.size const selectedLanguageCount = selectedLanguageIds.length + const selectedLanguageQuery = useMemo(() => { + if (selectedLanguageIds.length === 0) return "" + + const params = new URLSearchParams() + params.set("languageId", selectedLanguageIds.join(",")) + return params.toString() + }, [selectedLanguageIds]) const languageSelectionRequired = requiresLanguageSelectionForEnrich( selectedVideoCount, selectedLanguageCount, @@ -1731,6 +1738,7 @@ export function CoverageReportClient({ languageSelectionRequired={languageSelectionRequired} onCancel={handleCancelEnrichSelection} onEnrich={handleEnrichSelection} + reportQuery={selectedLanguageQuery} /> )} diff --git a/apps/manager/src/features/coverage/enrich-action-controls.test.ts b/apps/manager/src/features/coverage/enrich-action-controls.test.ts index d0c5f3f00..53b1d6959 100644 --- a/apps/manager/src/features/coverage/enrich-action-controls.test.ts +++ b/apps/manager/src/features/coverage/enrich-action-controls.test.ts @@ -41,11 +41,12 @@ describe("EnrichActionControls", () => { languageSelectionRequired: false, onCancel: vi.fn(), onEnrich: vi.fn(), + reportQuery: "languageId=529", }), ) expect(markup).toContain("1 enrichment job started.") - expect(markup).toContain('href="/dashboard/jobs/job-1"') + expect(markup).toContain('href="/dashboard/jobs/job-1?languageId=529"') expect(markup).toContain("Open job") }) }) diff --git a/apps/manager/src/features/coverage/enrich-action-controls.tsx b/apps/manager/src/features/coverage/enrich-action-controls.tsx index cc19c4173..dab1163b0 100644 --- a/apps/manager/src/features/coverage/enrich-action-controls.tsx +++ b/apps/manager/src/features/coverage/enrich-action-controls.tsx @@ -3,6 +3,7 @@ import React from "react" import type { EnrichFeedback } from "@/features/enrich-selection" +import { buildDashboardHrefWithReportQuery } from "@/features/nav/dashboard-nav-model" type EnrichActionControlsProps = { enrichActionReady: boolean @@ -11,6 +12,7 @@ type EnrichActionControlsProps = { languageSelectionRequired: boolean onCancel: () => void onEnrich: () => void | Promise + reportQuery?: string } export function EnrichActionControls({ @@ -20,8 +22,12 @@ export function EnrichActionControls({ languageSelectionRequired, onCancel, onEnrich, + reportQuery = "", }: EnrichActionControlsProps) { const actionDisabled = !enrichActionReady || isEnrichSubmitting + const feedbackActionHref = enrichFeedback?.action + ? buildDashboardHrefWithReportQuery(enrichFeedback.action.href, reportQuery) + : null return (
@@ -87,7 +93,7 @@ export function EnrichActionControls({ {" "} {enrichFeedback.action.label} diff --git a/apps/manager/src/features/nav/dashboard-nav-model.test.ts b/apps/manager/src/features/nav/dashboard-nav-model.test.ts new file mode 100644 index 000000000..8cebe59f0 --- /dev/null +++ b/apps/manager/src/features/nav/dashboard-nav-model.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from "vitest" + +import { + buildDashboardHrefWithReportQuery, + buildDashboardNavHref, +} from "./dashboard-nav-model" + +describe("dashboard nav model", () => { + it("carries the canonical report language query across dashboard tabs", () => { + expect(buildDashboardNavHref("/dashboard/jobs", "languageId=529")).toBe( + "/dashboard/jobs?languageId=529", + ) + + expect(buildDashboardNavHref("/dashboard/coverage", "languageId=529")).toBe( + "/dashboard/coverage?languageId=529", + ) + + expect(buildDashboardNavHref("/dashboard/agents", "languageId=529")).toBe( + "/dashboard/agents?languageId=529", + ) + }) + + it("canonicalizes legacy languageIds query params", () => { + expect( + buildDashboardNavHref("/dashboard/agents", "languageIds=529,21028"), + ).toBe("/dashboard/agents?languageId=529%2C21028") + }) + + it("carries the report language query to dashboard job detail handoffs", () => { + expect( + buildDashboardHrefWithReportQuery( + "/dashboard/jobs/job-1", + "languageIds=529,21028", + ), + ).toBe("/dashboard/jobs/job-1?languageId=529%2C21028") + }) + + it("drops unsupported query params instead of carrying hidden dashboard state", () => { + expect( + buildDashboardNavHref( + "/dashboard/coverage", + "languageId=529&refresh=1&status=failed", + ), + ).toBe("/dashboard/coverage?languageId=529") + + expect(buildDashboardNavHref("/dashboard/jobs", "status=failed")).toBe( + "/dashboard/jobs", + ) + }) + + it("returns bare tab paths when no report language is selected", () => { + expect(buildDashboardNavHref("/dashboard/coverage", "")).toBe( + "/dashboard/coverage", + ) + + expect(buildDashboardNavHref("/dashboard/jobs", "languageId=")).toBe( + "/dashboard/jobs", + ) + }) +}) diff --git a/apps/manager/src/features/nav/dashboard-nav-model.ts b/apps/manager/src/features/nav/dashboard-nav-model.ts new file mode 100644 index 000000000..8c54b5e5f --- /dev/null +++ b/apps/manager/src/features/nav/dashboard-nav-model.ts @@ -0,0 +1,37 @@ +import type { Route } from "next" +import { resolveRequestedLanguageIds } from "@/features/coverage/language-selection" + +export type DashboardNavPath = + | "/dashboard/coverage" + | "/dashboard/jobs" + | "/dashboard/agents" + +export type DashboardReportQueryPath = + | DashboardNavPath + | `/dashboard/jobs/${string}` + +export function buildDashboardHrefWithReportQuery< + TPath extends DashboardReportQueryPath, +>(pathname: TPath, currentQuery: string): Route { + const params = new URLSearchParams(currentQuery) + const languageIds = resolveRequestedLanguageIds({ + languageId: params.get("languageId") ?? undefined, + languageIds: params.get("languageIds") ?? undefined, + }) + + if (languageIds.length === 0) { + return pathname as Route + } + + const nextParams = new URLSearchParams() + nextParams.set("languageId", languageIds.join(",")) + + return `${pathname}?${nextParams.toString()}` as Route +} + +export function buildDashboardNavHref( + pathname: TPath, + currentQuery: string, +): Route { + return buildDashboardHrefWithReportQuery(pathname, currentQuery) +} diff --git a/apps/manager/src/features/nav/dashboard-nav.tsx b/apps/manager/src/features/nav/dashboard-nav.tsx index d67a15dce..c733f4848 100644 --- a/apps/manager/src/features/nav/dashboard-nav.tsx +++ b/apps/manager/src/features/nav/dashboard-nav.tsx @@ -1,10 +1,11 @@ "use client" import Link from "next/link" -import { usePathname, useRouter } from "next/navigation" +import { usePathname, useRouter, useSearchParams } from "next/navigation" import { useCallback, useEffect, useRef, useState } from "react" import { BarChart2, Bot, ListChecks, LogOut } from "lucide-react" import { apiFetch } from "@/lib/api-fetch" +import { buildDashboardNavHref } from "./dashboard-nav-model" type NavUser = { username: string; email: string } @@ -20,6 +21,7 @@ function getInitials(username: string): string { export function DashboardNav({ user }: { user: NavUser }) { const router = useRouter() const pathname = usePathname() + const searchParams = useSearchParams() const [queueCount, setQueueCount] = useState(null) const [menuOpen, setMenuOpen] = useState(false) const menuRef = useRef(null) @@ -28,6 +30,13 @@ export function DashboardNav({ user }: { user: NavUser }) { const isJobs = pathname.startsWith("/dashboard/jobs") || pathname === "/dashboard" const isAgents = pathname.startsWith("/dashboard/agents") + const currentQuery = searchParams.toString() + const coverageHref = buildDashboardNavHref( + "/dashboard/coverage", + currentQuery, + ) + const jobsHref = buildDashboardNavHref("/dashboard/jobs", currentQuery) + const agentsHref = buildDashboardNavHref("/dashboard/agents", currentQuery) useEffect(() => { let cancelled = false @@ -72,7 +81,7 @@ export function DashboardNav({ user }: { user: NavUser }) { return (