Skip to content

Commit d4b2b84

Browse files
committed
improve: pagination
1 parent b4a2a90 commit d4b2b84

File tree

4 files changed

+57
-16
lines changed

4 files changed

+57
-16
lines changed

src/features/dashboard/templates/builds/table-cells.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ function CopyableCell({
3737
copy(value)
3838
}}
3939
className={cn(
40-
'text-fg-tertiary transition-colors',
40+
'text-fg-tertiary transition-colors cursor-copy',
4141
'hover:text-fg-secondary',
4242
wasCopied && 'text-accent-main',
4343
className

src/server/api/middlewares/telemetry.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ export const endTelemetryMiddleware = t.middleware(
169169
const durationMs = Math.round(duration * 1000) / 1000
170170

171171
const contextAttrs: Record<string, string | undefined> = {
172-
template_id: flattenClientInputValue(rawInput, 'template_id'),
173-
sandbox_id: flattenClientInputValue(rawInput, 'sandbox_id'),
172+
template_id: flattenClientInputValue(rawInput, 'templateId'),
173+
sandbox_id: flattenClientInputValue(rawInput, 'sandboxId'),
174174
}
175175

176176
// set span attributes for input inferred parameters

src/server/api/repositories/builds.repository.ts

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,18 @@ const TEMPLATE_BUILD_TIMEOUT_MS =
4646
interface ListBuildsPaginationOptions {
4747
limit?: number
4848
cursor?: string
49+
direction?: 'forward' | 'backward'
50+
}
51+
52+
interface PaginationCursor {
53+
timestamp: string
54+
direction: 'forward' | 'backward'
4955
}
5056

5157
interface ListBuildsPaginatedResult<T> {
5258
data: T[]
53-
nextCursor: string | null
54-
previousCursor: string | null
59+
nextCursor: PaginationCursor | null
60+
previousCursor: PaginationCursor | null
5561
hasMore: boolean
5662
}
5763

@@ -111,7 +117,12 @@ export async function listBuilds(
111117
const isBuildUUID = isUUID(buildIdOrTemplate)
112118

113119
if (!resolvedEnvId && !isBuildUUID) {
114-
return { data: [], nextCursor: null, previousCursor: null, hasMore: false }
120+
return {
121+
data: [],
122+
nextCursor: null,
123+
previousCursor: null,
124+
hasMore: false,
125+
}
115126
}
116127

117128
if (resolvedEnvId && isBuildUUID) {
@@ -123,8 +134,14 @@ export async function listBuilds(
123134
}
124135
}
125136

137+
const isBackward = options.direction === 'backward'
138+
126139
if (options.cursor) {
127-
query = query.lt('created_at', options.cursor)
140+
if (isBackward) {
141+
query = query.gt('created_at', options.cursor)
142+
} else {
143+
query = query.lt('created_at', options.cursor)
144+
}
128145
}
129146

130147
const { data: rawBuilds, error } = await query
@@ -134,17 +151,35 @@ export async function listBuilds(
134151
}
135152

136153
if (!rawBuilds || rawBuilds.length === 0) {
137-
return { data: [], nextCursor: null, previousCursor: null, hasMore: false }
154+
return {
155+
data: [],
156+
nextCursor: null,
157+
previousCursor: null,
158+
hasMore: false,
159+
}
138160
}
139161

140-
const builds = rawBuilds.map(mapDatabaseBuildToListedBuildDTO)
162+
// for backward queries, we need to reverse to maintain chronological order (newest first)
163+
const orderedBuilds = isBackward ? [...rawBuilds].reverse() : rawBuilds
164+
165+
const builds = orderedBuilds.map(mapDatabaseBuildToListedBuildDTO)
141166
const hasMore = builds.length > limit
142167
const data = hasMore ? builds.slice(0, limit) : builds
143-
const firstRawBuild = rawBuilds[0]
144-
const lastRawBuild = rawBuilds[data.length - 1]
145-
const nextCursor = hasMore && lastRawBuild ? lastRawBuild.created_at : null
146-
const previousCursor =
147-
options.cursor && firstRawBuild ? firstRawBuild.created_at : null
168+
const firstRawBuild = orderedBuilds[0]
169+
const lastRawBuild = orderedBuilds[data.length - 1]
170+
171+
// nextCursor: for fetching older builds (forward direction)
172+
const nextCursor: PaginationCursor | null =
173+
hasMore && lastRawBuild
174+
? { timestamp: lastRawBuild.created_at, direction: 'forward' }
175+
: null
176+
177+
// previousCursor: for fetching newer builds (backward direction)
178+
// only available when we've navigated away from the first page
179+
const previousCursor: PaginationCursor | null =
180+
options.cursor && firstRawBuild
181+
? { timestamp: firstRawBuild.created_at, direction: 'backward' }
182+
: null
148183

149184
return {
150185
data,

src/server/api/routers/builds.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ export const buildsRouter = createTRPCRouter({
2121
buildIdOrTemplate: z.string().optional(),
2222
statuses: z.array(BuildStatusDTOSchema),
2323
limit: z.number().min(1).max(100).default(50),
24-
cursor: z.string().optional(),
24+
cursor: z
25+
.object({
26+
timestamp: z.string(),
27+
direction: z.enum(['forward', 'backward']),
28+
})
29+
.optional(),
2530
})
2631
)
2732
.query(async ({ ctx, input }) => {
@@ -38,7 +43,8 @@ export const buildsRouter = createTRPCRouter({
3843
dbStatuses,
3944
{
4045
limit,
41-
cursor,
46+
cursor: cursor?.timestamp,
47+
direction: cursor?.direction,
4248
}
4349
)
4450
}),

0 commit comments

Comments
 (0)