Skip to content

Commit cb47200

Browse files
authored
Added search to the Queues page (#2000)
* Added search to the queues page * Fix for empty state copy
1 parent 98fd804 commit cb47200

File tree

2 files changed

+84
-8
lines changed

2 files changed

+84
-8
lines changed

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,27 @@ export class QueueListPresenter extends BasePresenter {
1616

1717
public async call({
1818
environment,
19+
query,
1920
page,
2021
}: {
2122
environment: AuthenticatedEnvironment;
23+
query?: string;
2224
page: number;
2325
perPage?: number;
2426
}) {
27+
const hasFilters = query !== undefined && query.length > 0;
28+
2529
// Get total count for pagination
2630
const totalQueues = await this._replica.taskQueue.count({
2731
where: {
2832
runtimeEnvironmentId: environment.id,
2933
version: "V2",
34+
name: query
35+
? {
36+
contains: query,
37+
mode: "insensitive",
38+
}
39+
: undefined,
3040
},
3141
});
3242

@@ -45,6 +55,7 @@ export class QueueListPresenter extends BasePresenter {
4555
success: false as const,
4656
code: "engine-version",
4757
totalQueues: 1,
58+
hasFilters,
4859
};
4960
}
5061
}
@@ -53,26 +64,38 @@ export class QueueListPresenter extends BasePresenter {
5364
success: false as const,
5465
code: "engine-version",
5566
totalQueues,
67+
hasFilters,
5668
};
5769
}
5870

5971
return {
6072
success: true as const,
61-
queues: await this.getQueuesWithPagination(environment, page),
73+
queues: await this.getQueuesWithPagination(environment, query, page),
6274
pagination: {
6375
currentPage: page,
6476
totalPages: Math.ceil(totalQueues / this.perPage),
6577
count: totalQueues,
6678
},
6779
totalQueues,
80+
hasFilters,
6881
};
6982
}
7083

71-
private async getQueuesWithPagination(environment: AuthenticatedEnvironment, page: number) {
84+
private async getQueuesWithPagination(
85+
environment: AuthenticatedEnvironment,
86+
query: string | undefined,
87+
page: number
88+
) {
7289
const queues = await this._replica.taskQueue.findMany({
7390
where: {
7491
runtimeEnvironmentId: environment.id,
7592
version: "V2",
93+
name: query
94+
? {
95+
contains: query,
96+
mode: "insensitive",
97+
}
98+
: undefined,
7699
},
77100
select: {
78101
friendlyId: true,

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

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@ import {
22
ArrowUpCircleIcon,
33
BookOpenIcon,
44
ChatBubbleLeftEllipsisIcon,
5+
MagnifyingGlassIcon,
56
PauseIcon,
67
PlayIcon,
78
RectangleStackIcon,
89
} from "@heroicons/react/20/solid";
910
import { DialogClose } from "@radix-ui/react-dialog";
10-
import { Form, useNavigation, useRevalidator, type MetaFunction } from "@remix-run/react";
11+
import {
12+
Form,
13+
useNavigate,
14+
useNavigation,
15+
useRevalidator,
16+
useSearchParams,
17+
type MetaFunction,
18+
} from "@remix-run/react";
1119
import { type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/server-runtime";
1220
import { type RuntimeEnvironmentType } from "@trigger.dev/database";
1321
import { useEffect, useState } from "react";
@@ -61,8 +69,11 @@ import { docsPath, EnvironmentParamSchema, v3BillingPath } from "~/utils/pathBui
6169
import { PauseEnvironmentService } from "~/v3/services/pauseEnvironment.server";
6270
import { PauseQueueService } from "~/v3/services/pauseQueue.server";
6371
import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route";
72+
import { Input } from "~/components/primitives/Input";
73+
import { useThrottle } from "~/hooks/useThrottle";
6474

6575
const SearchParamsSchema = z.object({
76+
query: z.string().optional(),
6677
page: z.coerce.number().min(1).default(1),
6778
});
6879

@@ -79,7 +90,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
7990
const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params);
8091

8192
const url = new URL(request.url);
82-
const { page } = SearchParamsSchema.parse(Object.fromEntries(url.searchParams));
93+
const { page, query } = SearchParamsSchema.parse(Object.fromEntries(url.searchParams));
8394

8495
const project = await findProjectBySlug(organizationSlug, projectParam, userId);
8596
if (!project) {
@@ -101,6 +112,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
101112
const queueListPresenter = new QueueListPresenter();
102113
const queues = await queueListPresenter.call({
103114
environment,
115+
query,
104116
page,
105117
});
106118

@@ -198,7 +210,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
198210
};
199211

200212
export default function Page() {
201-
const { environment, queues, success, pagination, code, totalQueues } =
213+
const { environment, queues, success, pagination, code, totalQueues, hasFilters } =
202214
useTypedLoaderData<typeof loader>();
203215

204216
const organization = useOrganization();
@@ -285,10 +297,11 @@ export default function Page() {
285297
{success ? (
286298
<div
287299
className={cn(
288-
"grid max-h-full min-h-full grid-rows-[1fr] overflow-x-auto",
289-
pagination.totalPages > 1 && "grid-rows-[1fr_auto]"
300+
"grid max-h-full min-h-full grid-rows-[auto_1fr] overflow-x-auto",
301+
pagination.totalPages > 1 && "grid-rows-[auto_1fr_auto]"
290302
)}
291303
>
304+
<QueueFilters />
292305
<Table containerClassName="border-t">
293306
<TableHeader>
294307
<TableRow>
@@ -407,7 +420,11 @@ export default function Page() {
407420
<TableRow>
408421
<TableCell colSpan={6}>
409422
<div className="grid place-items-center py-6 text-text-dimmed">
410-
<Paragraph>No queues found</Paragraph>
423+
<Paragraph>
424+
{hasFilters
425+
? "No queues found matching your filters"
426+
: "No queues found"}
427+
</Paragraph>
411428
</div>
412429
</TableCell>
413430
</TableRow>
@@ -663,3 +680,39 @@ export function isEnvironmentPauseResumeFormSubmission(
663680
formData.get("action") === "environment-resume")
664681
);
665682
}
683+
684+
export function QueueFilters() {
685+
const [searchParams, setSearchParams] = useSearchParams();
686+
687+
const handleSearchChange = useThrottle((value: string) => {
688+
if (value) {
689+
setSearchParams((prev) => {
690+
prev.set("query", value);
691+
prev.delete("page");
692+
return prev;
693+
});
694+
} else {
695+
setSearchParams((prev) => {
696+
prev.delete("query");
697+
prev.delete("page");
698+
return prev;
699+
});
700+
}
701+
}, 300);
702+
703+
const search = searchParams.get("query") ?? "";
704+
705+
return (
706+
<div className="flex w-full px-3 pb-3">
707+
<Input
708+
name="search"
709+
placeholder="Search queue name"
710+
icon={MagnifyingGlassIcon}
711+
variant="tertiary"
712+
className="grow"
713+
defaultValue={search}
714+
onChange={(e) => handleSearchChange(e.target.value)}
715+
/>
716+
</div>
717+
);
718+
}

0 commit comments

Comments
 (0)