import { StringParam, useQueryParam, withDefault } from "use-query-params";
import { PublishTraceSwitch } from "@/src/components/publish-object-switch";
import { DetailPageNav } from "@/src/features/navigate-detail-pages/DetailPageNav";
import { useRouter } from "next/router";
import { api } from "@/src/utils/api";
import { StarTraceDetailsToggle } from "@/src/components/star-toggle";
import { ErrorPage } from "@/src/components/error-page";
import { DeleteTraceButton } from "@/src/components/deleteButton";
import Page from "@/src/components/layouts/page";
import { Trace } from "@/src/components/trace2/Trace";
import { useSession } from "next-auth/react";
import { useIsAuthenticatedAndProjectMember } from "@/src/features/auth/hooks";
import { Button } from "@/src/components/ui/button";
import Link from "next/link";
import { stripBasePath } from "@/src/utils/redirect";
import { Badge } from "@/src/components/ui/badge";
import { useV4Beta } from "@/src/features/events/hooks/useV4Beta";
import { useEventsTraceData } from "@/src/features/events/hooks/useEventsTraceData";
export function TracePage({
traceId,
timestamp,
}: {
traceId: string;
timestamp?: Date;
}) {
const router = useRouter();
const session = useSession();
const routeProjectId = (router.query.projectId as string) ?? "";
const { isBetaEnabled } = useV4Beta();
// Old path: fetch from traces table (beta OFF)
const tracesQuery = api.traces.byIdWithObservationsAndScores.useQuery(
{
traceId,
timestamp,
projectId: routeProjectId,
},
{
enabled: !isBetaEnabled,
retry(failureCount, error) {
if (
error.data?.code === "UNAUTHORIZED" ||
error.data?.code === "NOT_FOUND"
)
return false;
return failureCount < 3;
},
},
);
// New path: fetch from events table (beta ON)
const eventsData = useEventsTraceData({
projectId: routeProjectId,
traceId,
timestamp,
enabled: isBetaEnabled,
});
// Use the appropriate data source based on beta toggle
const trace = isBetaEnabled
? {
data: eventsData.data,
isLoading: eventsData.isLoading,
error: eventsData.error as typeof tracesQuery.error,
}
: tracesQuery;
const projectIdForAccessCheck = trace.data?.projectId ?? routeProjectId;
const hasProjectAccess = useIsAuthenticatedAndProjectMember(
projectIdForAccessCheck,
);
const [selectedTab, setSelectedTab] = useQueryParam(
"display",
withDefault(StringParam, "details"),
);
// Handle errors - for events path, we check if there's no data after loading
if (!isBetaEnabled && tracesQuery.error?.data?.code === "UNAUTHORIZED")
return ;
if (!isBetaEnabled && tracesQuery.error?.data?.code === "NOT_FOUND")
return (
void window.location.reload(),
}}
/>
);
// For events path: show not found if no observations found after loading
if (isBetaEnabled && !eventsData.isLoading && !eventsData.data)
return (
void window.location.reload(),
}}
/>
);
if (!trace.data) return Loading...
;
const isSharedTrace = trace.data.public;
const showPublicIndicators = isSharedTrace && !hasProjectAccess;
const encodedTargetPath = encodeURIComponent(
stripBasePath(router.asPath || "/"),
);
const leadingControl = showPublicIndicators ? (
session.status === "authenticated" ? (
) : (
)
) : undefined;
const sharedBadge = showPublicIndicators ? (
Public
) : undefined;
return (
),
actionButtonsRight: (
<>
{
const { view, display, projectId } = router.query;
const queryParams = new URLSearchParams({
...(typeof view === "string" ? { view } : {}),
...(typeof display === "string" ? { display } : {}),
});
const timestamp =
entry.params && entry.params.timestamp
? encodeURIComponent(entry.params.timestamp)
: undefined;
if (timestamp) {
queryParams.set("timestamp", timestamp);
}
const finalQueryString = queryParams.size
? `?${queryParams.toString()}`
: "";
return `/project/${projectId as string}/traces/${entry.id}${finalQueryString}`;
}}
listKey="traces"
/>
>
),
}}
>
);
}