diff --git a/ui/src/pages/Inbox.tsx b/ui/src/pages/Inbox.tsx index cc77e0f0..0a3ede64 100644 --- a/ui/src/pages/Inbox.tsx +++ b/ui/src/pages/Inbox.tsx @@ -11,6 +11,7 @@ import { heartbeatsApi } from "../api/heartbeats"; import { useCompany } from "../context/CompanyContext"; import { useBreadcrumbs } from "../context/BreadcrumbContext"; import { queryKeys } from "../lib/queryKeys"; +import { createIssueDetailLocationState } from "../lib/issueDetailBreadcrumb"; import { StatusIcon } from "../components/StatusIcon"; import { PriorityIcon } from "../components/PriorityIcon"; import { EmptyState } from "../components/EmptyState"; @@ -171,11 +172,13 @@ function FailedRunCard({ run, issueById, agentName: linkedAgentName, + issueLinkState, onDismiss, }: { run: HeartbeatRun; issueById: Map; agentName: string | null; + issueLinkState: unknown; onDismiss: () => void; }) { const queryClient = useQueryClient(); @@ -227,6 +230,7 @@ function FailedRunCard({ {issue ? ( @@ -315,6 +319,14 @@ export function Inbox() { const pathSegment = location.pathname.split("/").pop() ?? "new"; const tab: InboxTab = pathSegment === "all" ? "all" : "new"; + const issueLinkState = useMemo( + () => + createIssueDetailLocationState( + "Inbox", + `${location.pathname}${location.search}${location.hash}`, + ), + [location.pathname, location.search, location.hash], + ); const { data: agents } = useQuery({ queryKey: queryKeys.agents.list(selectedCompanyId!), @@ -749,6 +761,7 @@ export function Inbox() { run={run} issueById={issueById} agentName={agentName(run.agentId)} + issueLinkState={issueLinkState} onDismiss={() => dismiss(`run:${run.id}`)} /> ))} @@ -890,6 +903,7 @@ export function Inbox() { {/* Status icon - left column on mobile, inline on desktop */} diff --git a/ui/src/pages/IssueDetail.tsx b/ui/src/pages/IssueDetail.tsx index d9e0a7ef..d10a8114 100644 --- a/ui/src/pages/IssueDetail.tsx +++ b/ui/src/pages/IssueDetail.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useRef, useState, type ChangeEvent } from "react"; -import { useParams, Link, useNavigate } from "react-router-dom"; +import { Link, useLocation, useNavigate, useParams } from "@/lib/router"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { issuesApi } from "../api/issues"; import { activityApi } from "../api/activity"; @@ -11,6 +11,7 @@ import { useCompany } from "../context/CompanyContext"; import { usePanel } from "../context/PanelContext"; import { useBreadcrumbs } from "../context/BreadcrumbContext"; import { queryKeys } from "../lib/queryKeys"; +import { readIssueDetailBreadcrumb } from "../lib/issueDetailBreadcrumb"; import { useProjectOrder } from "../hooks/useProjectOrder"; import { relativeTime, cn, formatTokens } from "../lib/utils"; import { InlineEditor } from "../components/InlineEditor"; @@ -150,6 +151,7 @@ export function IssueDetail() { const { setBreadcrumbs } = useBreadcrumbs(); const queryClient = useQueryClient(); const navigate = useNavigate(); + const location = useLocation(); const [moreOpen, setMoreOpen] = useState(false); const [mobilePropsOpen, setMobilePropsOpen] = useState(false); const [detailTab, setDetailTab] = useState("comments"); @@ -213,6 +215,10 @@ export function IssueDetail() { }); const hasLiveRuns = (liveRuns ?? []).length > 0 || !!activeRun; + const sourceBreadcrumb = useMemo( + () => readIssueDetailBreadcrumb(location.state) ?? { label: "Issues", href: "/issues" }, + [location.state], + ); // Filter out runs already shown by the live widget to avoid duplication const timelineRuns = useMemo(() => { @@ -468,17 +474,17 @@ export function IssueDetail() { useEffect(() => { const titleLabel = issue?.title ?? issueId ?? "Issue"; setBreadcrumbs([ - { label: "Issues", href: "/issues" }, + sourceBreadcrumb, { label: hasLiveRuns ? `🔵 ${titleLabel}` : titleLabel }, ]); - }, [setBreadcrumbs, issue, issueId, hasLiveRuns]); + }, [setBreadcrumbs, sourceBreadcrumb, issue, issueId, hasLiveRuns]); // Redirect to identifier-based URL if navigated via UUID useEffect(() => { if (issue?.identifier && issueId !== issue.identifier) { - navigate(`/issues/${issue.identifier}`, { replace: true }); + navigate(`/issues/${issue.identifier}`, { replace: true, state: location.state }); } - }, [issue, issueId, navigate]); + }, [issue, issueId, navigate, location.state]); useEffect(() => { if (!issue?.id) return; @@ -524,6 +530,7 @@ export function IssueDetail() { {i > 0 && } @@ -800,6 +807,7 @@ export function IssueDetail() {