@@ -23,7 +23,7 @@ import {
2323} from "@trigger.dev/core/v3" ;
2424import type { RuntimeEnvironmentType } from "@trigger.dev/database" ;
2525import { motion } from "framer-motion" ;
26- import React , { useCallback , useEffect , useRef , useState } from "react" ;
26+ import React , { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
2727import { useHotkeys } from "react-hotkeys-hook" ;
2828import { redirect } from "remix-typedjson" ;
2929import { MoveToTopIcon } from "~/assets/icons/MoveToTopIcon" ;
@@ -105,6 +105,7 @@ import { $replica } from "~/db.server";
105105import { clickhouseClient } from "~/services/clickhouseInstance.server" ;
106106import { findProjectBySlug } from "~/models/project.server" ;
107107import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server" ;
108+ import { logger } from "~/services/logger.server" ;
108109
109110const resizableSettings = {
110111 parent : {
@@ -138,6 +139,101 @@ const resizableSettings = {
138139
139140type TraceEvent = NonNullable < SerializeFrom < typeof loader > [ "trace" ] > [ "events" ] [ 0 ] ;
140141
142+ type RunsListNavigation = {
143+ runs : Array < { friendlyId : string } > ;
144+ pagination : { next ?: string ; previous ?: string } ;
145+ prevPageLastRun ?: { friendlyId : string ; cursor : string } ;
146+ nextPageFirstRun ?: { friendlyId : string ; cursor : string } ;
147+ } ;
148+
149+ async function getRunsListFromTableState ( {
150+ tableStateParam,
151+ organizationSlug,
152+ projectParam,
153+ envParam,
154+ runParam,
155+ userId,
156+ } : {
157+ tableStateParam : string | null ;
158+ organizationSlug : string ;
159+ projectParam : string ;
160+ envParam : string ;
161+ runParam : string ;
162+ userId : string ;
163+ } ) : Promise < RunsListNavigation | null > {
164+ if ( ! tableStateParam ) {
165+ return null ;
166+ }
167+
168+ try {
169+ const tableStateSearchParams = new URLSearchParams ( decodeURIComponent ( tableStateParam ) ) ;
170+ const filters = getRunFiltersFromSearchParams ( tableStateSearchParams ) ;
171+
172+ const project = await findProjectBySlug ( organizationSlug , projectParam , userId ) ;
173+ const environment = await findEnvironmentBySlug ( project ?. id ?? "" , envParam , userId ) ;
174+
175+ if ( ! project || ! environment ) {
176+ return null ;
177+ }
178+
179+ const runsListPresenter = new NextRunListPresenter ( $replica , clickhouseClient ) ;
180+ const currentPageResult = await runsListPresenter . call ( project . organizationId , environment . id , {
181+ userId,
182+ projectId : project . id ,
183+ ...filters ,
184+ pageSize : 25 , // Load enough runs to provide navigation context
185+ } ) ;
186+
187+ const runsList : RunsListNavigation = {
188+ runs : currentPageResult . runs ,
189+ pagination : currentPageResult . pagination ,
190+ } ;
191+
192+ const currentRunIndex = currentPageResult . runs . findIndex ( ( r ) => r . friendlyId === runParam ) ;
193+
194+ if ( currentRunIndex === 0 && currentPageResult . pagination . previous ) {
195+ const prevPageResult = await runsListPresenter . call ( project . organizationId , environment . id , {
196+ userId,
197+ projectId : project . id ,
198+ ...filters ,
199+ cursor : currentPageResult . pagination . previous ,
200+ direction : "backward" ,
201+ pageSize : 1 , // We only need the last run from the previous page
202+ } ) ;
203+
204+ if ( prevPageResult . runs . length > 0 ) {
205+ runsList . prevPageLastRun = {
206+ friendlyId : prevPageResult . runs [ 0 ] . friendlyId ,
207+ cursor : currentPageResult . pagination . previous ,
208+ } ;
209+ }
210+ }
211+
212+ if ( currentRunIndex === currentPageResult . runs . length - 1 && currentPageResult . pagination . next ) {
213+ const nextPageResult = await runsListPresenter . call ( project . organizationId , environment . id , {
214+ userId,
215+ projectId : project . id ,
216+ ...filters ,
217+ cursor : currentPageResult . pagination . next ,
218+ direction : "forward" ,
219+ pageSize : 1 , // We only need the first run from the next page
220+ } ) ;
221+
222+ if ( nextPageResult . runs . length > 0 ) {
223+ runsList . nextPageFirstRun = {
224+ friendlyId : nextPageResult . runs [ 0 ] . friendlyId ,
225+ cursor : currentPageResult . pagination . next ,
226+ } ;
227+ }
228+ }
229+
230+ return runsList ;
231+ } catch ( error ) {
232+ logger . error ( "Error loading runs list from tableState:" , { error } ) ;
233+ return null ;
234+ }
235+ }
236+
141237export const loader = async ( { request, params } : LoaderFunctionArgs ) => {
142238 const userId = await requireUserId ( request ) ;
143239 const impersonationId = await getImpersonationId ( request ) ;
@@ -176,81 +272,14 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
176272 const parent = await getResizableSnapshot ( request , resizableSettings . parent . autosaveId ) ;
177273 const tree = await getResizableSnapshot ( request , resizableSettings . tree . autosaveId ) ;
178274
179- // Load runs list data from tableState if present
180- let runsList : {
181- runs : Array < { friendlyId : string } > ;
182- pagination : { next ?: string ; previous ?: string } ;
183- prevPageLastRun ?: { friendlyId : string ; cursor : string } ;
184- nextPageFirstRun ?: { friendlyId : string ; cursor : string } ;
185- } | null = null ;
186- const tableStateParam = url . searchParams . get ( "tableState" ) ;
187- if ( tableStateParam ) {
188- try {
189- const tableStateSearchParams = new URLSearchParams ( decodeURIComponent ( tableStateParam ) ) ;
190- const filters = getRunFiltersFromSearchParams ( tableStateSearchParams ) ;
191-
192- const project = await findProjectBySlug ( organizationSlug , projectParam , userId ) ;
193- const environment = await findEnvironmentBySlug ( project ?. id ?? "" , envParam , userId ) ;
194-
195- if ( project && environment ) {
196- const runsListPresenter = new NextRunListPresenter ( $replica , clickhouseClient ) ;
197- const currentPageResult = await runsListPresenter . call ( project . organizationId , environment . id , {
198- userId,
199- projectId : project . id ,
200- ...filters ,
201- pageSize : 25 , // Load enough runs to provide navigation context
202- } ) ;
203-
204- runsList = {
205- runs : currentPageResult . runs ,
206- pagination : currentPageResult . pagination ,
207- } ;
208-
209- // Check if the current run is at the boundary and preload adjacent page if needed
210- const currentRunIndex = currentPageResult . runs . findIndex ( ( r ) => r . friendlyId === runParam ) ;
211-
212- // If current run is first in list and there's a previous page, load the last run from prev page
213- if ( currentRunIndex === 0 && currentPageResult . pagination . previous ) {
214- const prevPageResult = await runsListPresenter . call ( project . organizationId , environment . id , {
215- userId,
216- projectId : project . id ,
217- ...filters ,
218- cursor : currentPageResult . pagination . previous ,
219- direction : "backward" ,
220- pageSize : 1 , // We only need the last run from the previous page
221- } ) ;
222- if ( prevPageResult . runs . length > 0 ) {
223- runsList . prevPageLastRun = {
224- friendlyId : prevPageResult . runs [ 0 ] . friendlyId ,
225- cursor : currentPageResult . pagination . previous ,
226- } ;
227- }
228- }
229-
230- // If current run is last in list and there's a next page, load the first run from next page
231- if ( currentRunIndex === currentPageResult . runs . length - 1 && currentPageResult . pagination . next ) {
232- const nextPageResult = await runsListPresenter . call ( project . organizationId , environment . id , {
233- userId,
234- projectId : project . id ,
235- ...filters ,
236- cursor : currentPageResult . pagination . next ,
237- direction : "forward" ,
238- pageSize : 1 , // We only need the first run from the next page
239- } ) ;
240- if ( nextPageResult . runs . length > 0 ) {
241- runsList . nextPageFirstRun = {
242- friendlyId : nextPageResult . runs [ 0 ] . friendlyId ,
243- cursor : currentPageResult . pagination . next ,
244- } ;
245- }
246- }
247- }
248- } catch ( error ) {
249- // If there's an error parsing or loading runs list, just ignore it
250- // and don't include the runsList in the response
251- console . error ( "Error loading runs list from tableState:" , error ) ;
252- }
253- }
275+ const runsList = await getRunsListFromTableState ( {
276+ tableStateParam : url . searchParams . get ( "tableState" ) ,
277+ organizationSlug,
278+ projectParam,
279+ envParam,
280+ runParam,
281+ userId,
282+ } ) ;
254283
255284 return json ( {
256285 run : result . run ,
@@ -372,24 +401,18 @@ export default function Page() {
372401 run = { run }
373402 trace = { trace }
374403 maximumLiveReloadingSetting = { maximumLiveReloadingSetting }
375- resizable = { resizable }
376- runsList = { runsList }
377404 />
378405 ) : (
379406 < NoLogsView
380407 run = { run }
381- trace = { trace }
382- maximumLiveReloadingSetting = { maximumLiveReloadingSetting }
383- resizable = { resizable }
384- runsList = { runsList }
385408 />
386409 ) }
387410 </ PageBody >
388411 </ >
389412 ) ;
390413}
391414
392- function TraceView ( { run, trace, maximumLiveReloadingSetting, resizable } : LoaderData ) {
415+ function TraceView ( { run, trace, maximumLiveReloadingSetting } : Pick < LoaderData , "run" | "trace" | "maximumLiveReloadingSetting" > ) {
393416 const organization = useOrganization ( ) ;
394417 const project = useProject ( ) ;
395418 const environment = useEnvironment ( ) ;
@@ -483,7 +506,7 @@ function TraceView({ run, trace, maximumLiveReloadingSetting, resizable }: Loade
483506 ) ;
484507}
485508
486- function NoLogsView ( { run, resizable } : LoaderData ) {
509+ function NoLogsView ( { run } : Pick < LoaderData , "run" > ) {
487510 const plan = useCurrentPlan ( ) ;
488511 const organization = useOrganization ( ) ;
489512
@@ -1649,14 +1672,9 @@ function useAdjacentRunPaths({
16491672 environment : { slug : string } ;
16501673 tableState : string ;
16511674 run : { friendlyId : string } ;
1652- runsList : {
1653- runs : Array < { friendlyId : string } > ;
1654- pagination : { next ?: string ; previous ?: string } ;
1655- prevPageLastRun ?: { friendlyId : string ; cursor : string } ;
1656- nextPageFirstRun ?: { friendlyId : string ; cursor : string } ;
1657- } | null ;
1675+ runsList : RunsListNavigation | null ;
16581676} ) : [ string | null , string | null ] {
1659- return React . useMemo ( ( ) => {
1677+ return useMemo ( ( ) => {
16601678 if ( ! runsList || runsList . runs . length === 0 ) {
16611679 return [ null , null ] ;
16621680 }
0 commit comments