@@ -1494,7 +1603,7 @@ function NumberShortcuts({ toggleLevel }: { toggleLevel: (depth: number) => void
return (
0
-
–
+
–
9
Toggle level
@@ -1526,3 +1635,116 @@ function SearchField({ onChange }: { onChange: (value: string) => void }) {
/>
);
}
+
+function useAdjacentRunPaths({
+ organization,
+ project,
+ environment,
+ tableState,
+ run,
+ runsList,
+}: {
+ organization: { slug: string };
+ project: { slug: string };
+ environment: { slug: string };
+ tableState: string;
+ run: { friendlyId: string };
+ runsList: {
+ runs: Array<{ friendlyId: string }>;
+ pagination: { next?: string; previous?: string };
+ prevPageLastRun?: { friendlyId: string; cursor: string };
+ nextPageFirstRun?: { friendlyId: string; cursor: string };
+ } | null;
+}): [string | null, string | null] {
+ return React.useMemo(() => {
+ if (!runsList || runsList.runs.length === 0) {
+ return [null, null];
+ }
+
+ const currentIndex = runsList.runs.findIndex((r) => r.friendlyId === run.friendlyId);
+
+ if (currentIndex === -1) {
+ return [null, null];
+ }
+
+ // Determine previous run: use prevPageLastRun if at first position, otherwise use previous run in list
+ let previousRun: { friendlyId: string } | null = null;
+ const previousRunTableState = new URLSearchParams(tableState);
+ if (currentIndex > 0) {
+ previousRun = runsList.runs[currentIndex - 1];
+ } else if (runsList.prevPageLastRun) {
+ previousRun = runsList.prevPageLastRun;
+ // Update tableState with the new cursor for the previous page
+ previousRunTableState.set("cursor", runsList.prevPageLastRun.cursor);
+ previousRunTableState.set("direction", "backward");
+ }
+
+ // Determine next run: use nextPageFirstRun if at last position, otherwise use next run in list
+ let nextRun: { friendlyId: string } | null = null;
+ const nextRunTableState = new URLSearchParams(tableState);
+ if (currentIndex < runsList.runs.length - 1) {
+ nextRun = runsList.runs[currentIndex + 1];
+ } else if (runsList.nextPageFirstRun) {
+ nextRun = runsList.nextPageFirstRun;
+ // Update tableState with the new cursor for the next page
+ nextRunTableState.set("cursor", runsList.nextPageFirstRun.cursor);
+ nextRunTableState.set("direction", "forward");
+ }
+
+ const previousURLSearchParams = new URLSearchParams();
+ previousURLSearchParams.set("tableState", previousRunTableState.toString());
+ const previousRunPath = previousRun
+ ? v3RunPath(organization, project, environment, previousRun, previousURLSearchParams)
+ : null;
+
+ const nextURLSearchParams = new URLSearchParams();
+ nextURLSearchParams.set("tableState", nextRunTableState.toString());
+ const nextRunPath = nextRun
+ ? v3RunPath(organization, project, environment, nextRun, nextURLSearchParams)
+ : null;
+
+ return [previousRunPath, nextRunPath];
+ }, [organization, project, environment, tableState, run.friendlyId, runsList]);
+}
+
+
+function PreviousRunButton({ to }: { to: string | null }) {
+ return (
+
+ !to && e.preventDefault()}
+ shortcut={{ key: "[" }}
+ tooltip="Previous Run"
+ disabled={!to}
+ />
+
+ );
+}
+
+function NextRunButton({ to }: { to: string | null }) {
+ return (
+
+ !to && e.preventDefault()}
+ shortcut={{ key: "]" }}
+ tooltip="Next Run"
+ disabled={!to}
+ />
+
+ );
+}
+
diff --git a/apps/webapp/app/utils/pathBuilder.ts b/apps/webapp/app/utils/pathBuilder.ts
index f82165ae9d..3061082ed9 100644
--- a/apps/webapp/app/utils/pathBuilder.ts
+++ b/apps/webapp/app/utils/pathBuilder.ts
@@ -288,15 +288,17 @@ export function v3RunPath(
organization: OrgForPath,
project: ProjectForPath,
environment: EnvironmentForPath,
- run: v3RunForPath
+ run: v3RunForPath,
+ searchParams?: URLSearchParams
) {
- return `${v3RunsPath(organization, project, environment)}/${run.friendlyId}`;
+ const query = searchParams ? `?${searchParams.toString()}` : "";
+ return `${v3RunsPath(organization, project, environment)}/${run.friendlyId}${query}`;
}
export function v3RunRedirectPath(
organization: OrgForPath,
project: ProjectForPath,
- run: v3RunForPath
+ run: v3RunForPath,
) {
return `${v3ProjectPath(organization, project)}/runs/${run.friendlyId}`;
}
@@ -310,9 +312,12 @@ export function v3RunSpanPath(
project: ProjectForPath,
environment: EnvironmentForPath,
run: v3RunForPath,
- span: v3SpanForPath
+ span: v3SpanForPath,
+ searchParams?: URLSearchParams
) {
- return `${v3RunPath(organization, project, environment, run)}?span=${span.spanId}`;
+ searchParams = searchParams ?? new URLSearchParams();
+ searchParams.set("span", span.spanId);
+ return `${v3RunPath(organization, project, environment, run, searchParams)}`;
}
export function v3RunStreamingPath(