Skip to content

Commit 1e42c4e

Browse files
committed
Batches, and fix for blank state
1 parent c3c79a4 commit 1e42c4e

7 files changed

Lines changed: 84 additions & 43 deletions

File tree

apps/webapp/app/components/runs/v3/BatchFilters.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ export const BatchListFilters = z.object({
4848
BatchStatus.array().optional()
4949
),
5050
id: z.string().optional(),
51+
period: z.preprocess((value) => (value === "all" ? undefined : value), z.string().optional()),
52+
from: z.coerce.number().optional(),
53+
to: z.coerce.number().optional(),
5154
});
5255

5356
export type BatchListFilters = z.infer<typeof BatchListFilters>;

apps/webapp/app/components/runs/v3/RunFilters.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ export const TaskRunListSearchFilters = z.object({
7979
(value) => (typeof value === "string" ? [value] : value),
8080
z.string().array().optional()
8181
),
82-
period: z.preprocess((value) => (value === "all" ? undefined : value), z.string().optional()),
8382
bulkId: z.string().optional(),
83+
period: z.preprocess((value) => (value === "all" ? undefined : value), z.string().optional()),
8484
from: z.coerce.number().optional(),
8585
to: z.coerce.number().optional(),
8686
rootOnly: z.coerce.boolean().optional(),

apps/webapp/app/components/runs/v3/SharedFilters.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,38 +106,42 @@ export const timeFilters = ({
106106
to,
107107
}: {
108108
period?: string;
109-
from?: string;
110-
to?: string;
111-
}): { period?: string; from?: Date; to?: Date } => {
109+
from?: string | number;
110+
to?: string | number;
111+
}): { period?: string; from?: Date; to?: Date; isDefault: boolean } => {
112112
if (period) {
113-
return { period };
113+
return { period, isDefault: period === defaultPeriod };
114114
}
115115

116116
if (from && to) {
117117
return {
118-
from: dateFromString(from),
119-
to: dateFromString(to),
118+
from: typeof from === "string" ? dateFromString(from) : new Date(from),
119+
to: typeof to === "string" ? dateFromString(to) : new Date(to),
120+
isDefault: false,
120121
};
121122
}
122123

123124
if (from) {
124-
const fromDate = dateFromString(from);
125+
const fromDate = typeof from === "string" ? dateFromString(from) : new Date(from);
125126

126127
return {
127128
from: fromDate,
129+
isDefault: false,
128130
};
129131
}
130132

131133
if (to) {
132-
const toDate = dateFromString(to);
134+
const toDate = typeof to === "string" ? dateFromString(to) : new Date(to);
133135

134136
return {
135137
to: toDate,
138+
isDefault: false,
136139
};
137140
}
138141

139142
return {
140143
period: defaultPeriod,
144+
isDefault: true,
141145
};
142146
};
143147

apps/webapp/app/presenters/v3/BatchListPresenter.server.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { sqlDatabaseSchema } from "~/db.server";
44
import { displayableEnvironment } from "~/models/runtimeEnvironment.server";
55
import { BasePresenter } from "./basePresenter.server";
66
import { type Direction } from "~/components/ListPagination";
7+
import { timeFilters } from "~/components/runs/v3/SharedFilters";
78

89
export type BatchListOptions = {
910
userId?: string;
@@ -41,14 +42,16 @@ export class BatchListPresenter extends BasePresenter {
4142
cursor,
4243
pageSize = DEFAULT_PAGE_SIZE,
4344
}: BatchListOptions) {
45+
//get the time values from the raw values (including a default period)
46+
const time = timeFilters({
47+
period,
48+
from,
49+
to,
50+
});
51+
4452
const hasStatusFilters = statuses && statuses.length > 0;
4553

46-
const hasFilters =
47-
hasStatusFilters ||
48-
(period !== undefined && period !== "all") ||
49-
friendlyId !== undefined ||
50-
from !== undefined ||
51-
to !== undefined;
54+
const hasFilters = hasStatusFilters || friendlyId !== undefined || !time.isDefault;
5255

5356
// Find the project scoped to the organization
5457
const project = await this._replica.project.findFirstOrThrow({
@@ -88,7 +91,7 @@ export class BatchListPresenter extends BasePresenter {
8891
throw new Error("No matching environments found for the project");
8992
}
9093

91-
const periodMs = period ? parse(period) : undefined;
94+
const periodMs = time.period ? parse(time.period) : undefined;
9295

9396
//get the batches
9497
const batches = await this._replica.$queryRaw<
@@ -142,11 +145,11 @@ WHERE
142145
: Prisma.empty
143146
}
144147
${
145-
from
146-
? Prisma.sql`AND b."createdAt" >= ${new Date(from).toISOString()}::timestamp`
148+
time.from
149+
? Prisma.sql`AND b."createdAt" >= ${time.from.toISOString()}::timestamp`
147150
: Prisma.empty
148151
}
149-
${to ? Prisma.sql`AND b."createdAt" <= ${new Date(to).toISOString()}::timestamp` : Prisma.empty}
152+
${time.to ? Prisma.sql`AND b."createdAt" <= ${time.to.toISOString()}::timestamp` : Prisma.empty}
150153
ORDER BY
151154
${direction === "forward" ? Prisma.sql`b.id DESC` : Prisma.sql`b.id ASC`}
152155
LIMIT ${pageSize + 1}`;
@@ -179,6 +182,21 @@ WHERE
179182
? batches.slice(1, pageSize + 1)
180183
: batches.slice(0, pageSize);
181184

185+
let hasAnyBatches = batchesToReturn.length > 0;
186+
if (!hasAnyBatches) {
187+
const firstBatch = await this._replica.batchTaskRun.findFirst({
188+
where: {
189+
runtimeEnvironmentId: {
190+
in: environmentIds,
191+
},
192+
},
193+
});
194+
195+
if (firstBatch) {
196+
hasAnyBatches = true;
197+
}
198+
}
199+
182200
return {
183201
batches: batchesToReturn.map((batch) => {
184202
const environment = project.environments.find(
@@ -216,10 +234,9 @@ WHERE
216234
friendlyId,
217235
statuses: statuses || [],
218236
environments: environments || [],
219-
from,
220-
to,
221237
},
222238
hasFilters,
239+
hasAnyBatches,
223240
};
224241
}
225242
}

apps/webapp/app/presenters/v3/RunListPresenter.server.ts

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { isCancellableRunStatus, isFinalRunStatus, isPendingRunStatus } from "~/
66
import { BasePresenter } from "./basePresenter.server";
77
import { getAllTaskIdentifiers } from "~/models/task.server";
88
import { type Direction } from "~/components/ListPagination";
9+
import { timeFilters } from "~/components/runs/v3/SharedFilters";
910

1011
export type RunListOptions = {
1112
userId?: string;
@@ -59,22 +60,27 @@ export class RunListPresenter extends BasePresenter {
5960
cursor,
6061
pageSize = DEFAULT_PAGE_SIZE,
6162
}: RunListOptions) {
63+
//get the time values from the raw values (including a default period)
64+
const time = timeFilters({
65+
period,
66+
from,
67+
to,
68+
});
69+
6270
const hasStatusFilters = statuses && statuses.length > 0;
6371

6472
const hasFilters =
6573
(tasks !== undefined && tasks.length > 0) ||
6674
(versions !== undefined && versions.length > 0) ||
6775
hasStatusFilters ||
68-
(period !== undefined && period !== "all") ||
6976
(bulkId !== undefined && bulkId !== "") ||
70-
from !== undefined ||
71-
to !== undefined ||
7277
(scheduleId !== undefined && scheduleId !== "") ||
7378
(tags !== undefined && tags.length > 0) ||
7479
batchId !== undefined ||
7580
(runIds !== undefined && runIds.length > 0) ||
7681
typeof isTest === "boolean" ||
77-
rootOnly === true;
82+
rootOnly === true ||
83+
!time.isDefault;
7884

7985
// Find the project scoped to the organization
8086
const project = await this._replica.project.findFirstOrThrow({
@@ -186,7 +192,7 @@ export class RunListPresenter extends BasePresenter {
186192
rootOnly = false;
187193
}
188194

189-
const periodMs = period ? parse(period) : undefined;
195+
const periodMs = time.period ? parse(time.period) : undefined;
190196

191197
//get the runs
192198
const runs = await this._replica.$queryRaw<
@@ -293,12 +299,12 @@ WHERE
293299
: Prisma.empty
294300
}
295301
${
296-
from
297-
? Prisma.sql`AND tr."createdAt" >= ${new Date(from).toISOString()}::timestamp`
302+
time.from
303+
? Prisma.sql`AND tr."createdAt" >= ${time.from.toISOString()}::timestamp`
298304
: Prisma.empty
299305
}
300306
${
301-
to ? Prisma.sql`AND tr."createdAt" <= ${new Date(to).toISOString()}::timestamp` : Prisma.empty
307+
time.to ? Prisma.sql`AND tr."createdAt" <= ${time.to.toISOString()}::timestamp` : Prisma.empty
302308
}
303309
${
304310
tags && tags.length > 0
@@ -338,6 +344,24 @@ WHERE
338344
const runsToReturn =
339345
direction === "backward" && hasMore ? runs.slice(1, pageSize + 1) : runs.slice(0, pageSize);
340346

347+
let hasAnyRuns = runsToReturn.length > 0;
348+
if (!hasAnyRuns) {
349+
const firstRun = await this._replica.taskRun.findFirst({
350+
where: {
351+
projectId: project.id,
352+
runtimeEnvironmentId: environments
353+
? {
354+
in: environments,
355+
}
356+
: undefined,
357+
},
358+
});
359+
360+
if (firstRun) {
361+
hasAnyRuns = true;
362+
}
363+
}
364+
341365
return {
342366
runs: runsToReturn.map((run) => {
343367
const environment = project.environments.find((env) => env.id === run.runtimeEnvironmentId);
@@ -401,10 +425,9 @@ WHERE
401425
versions: versions || [],
402426
statuses: statuses || [],
403427
environments: environments || [],
404-
from,
405-
to,
406428
},
407429
hasFilters,
430+
hasAnyRuns,
408431
};
409432
}
410433
}

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.batches/route.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
9999
};
100100

101101
export default function Page() {
102-
const { batches, hasFilters, filters, pagination } = useTypedLoaderData<typeof loader>();
102+
const { batches, hasFilters, hasAnyBatches, filters, pagination } =
103+
useTypedLoaderData<typeof loader>();
103104

104105
return (
105106
<PageContainer>
@@ -117,7 +118,7 @@ export default function Page() {
117118
</PageAccessories>
118119
</NavBar>
119120
<PageBody scrollable={false}>
120-
{!hasFilters && batches.length === 0 ? (
121+
{!hasAnyBatches ? (
121122
<MainCenteredContainer className="max-w-md">
122123
<BatchesNone />
123124
</MainCenteredContainer>
@@ -135,6 +136,7 @@ export default function Page() {
135136
filters={filters}
136137
hasFilters={hasFilters}
137138
pagination={pagination}
139+
hasAnyBatches={hasAnyBatches}
138140
/>
139141
</div>
140142
)}
@@ -186,15 +188,7 @@ function BatchesTable({ batches, hasFilters, filters }: BatchList) {
186188
</TableRow>
187189
</TableHeader>
188190
<TableBody>
189-
{batches.length === 0 && !hasFilters ? (
190-
<TableBlankRow colSpan={8}>
191-
{!isLoading && (
192-
<div className="flex items-center justify-center">
193-
<Paragraph className="w-auto">No batches</Paragraph>
194-
</div>
195-
)}
196-
</TableBlankRow>
197-
) : batches.length === 0 ? (
191+
{batches.length === 0 ? (
198192
<TableBlankRow colSpan={8}>
199193
<div className="flex items-center justify-center">
200194
<Paragraph className="w-auto">No batches match these filters</Paragraph>

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export default function Page() {
201201
<TypedAwait resolve={data}>
202202
{(list) => (
203203
<>
204-
{list.runs.length === 0 && !list.hasFilters ? (
204+
{list.runs.length === 0 && !list.hasAnyRuns ? (
205205
list.possibleTasks.length === 0 ? (
206206
<CreateFirstTaskInstructions />
207207
) : (

0 commit comments

Comments
 (0)