From 602f47582155884397be3148f5ddc6a3d9a967dd Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Wed, 30 Apr 2025 19:32:33 +0100 Subject: [PATCH 1/2] Added search to the queues page --- .../v3/QueueListPresenter.server.ts | 27 +++++++- .../route.tsx | 65 +++++++++++++++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/apps/webapp/app/presenters/v3/QueueListPresenter.server.ts b/apps/webapp/app/presenters/v3/QueueListPresenter.server.ts index 46ae950e8e..93531934bd 100644 --- a/apps/webapp/app/presenters/v3/QueueListPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/QueueListPresenter.server.ts @@ -16,17 +16,27 @@ export class QueueListPresenter extends BasePresenter { public async call({ environment, + query, page, }: { environment: AuthenticatedEnvironment; + query?: string; page: number; perPage?: number; }) { + const hasFilters = query !== undefined && query.length > 0; + // Get total count for pagination const totalQueues = await this._replica.taskQueue.count({ where: { runtimeEnvironmentId: environment.id, version: "V2", + name: query + ? { + contains: query, + mode: "insensitive", + } + : undefined, }, }); @@ -45,6 +55,7 @@ export class QueueListPresenter extends BasePresenter { success: false as const, code: "engine-version", totalQueues: 1, + hasFilters, }; } } @@ -53,26 +64,38 @@ export class QueueListPresenter extends BasePresenter { success: false as const, code: "engine-version", totalQueues, + hasFilters, }; } return { success: true as const, - queues: await this.getQueuesWithPagination(environment, page), + queues: await this.getQueuesWithPagination(environment, query, page), pagination: { currentPage: page, totalPages: Math.ceil(totalQueues / this.perPage), count: totalQueues, }, totalQueues, + hasFilters, }; } - private async getQueuesWithPagination(environment: AuthenticatedEnvironment, page: number) { + private async getQueuesWithPagination( + environment: AuthenticatedEnvironment, + query: string | undefined, + page: number + ) { const queues = await this._replica.taskQueue.findMany({ where: { runtimeEnvironmentId: environment.id, version: "V2", + name: query + ? { + contains: query, + mode: "insensitive", + } + : undefined, }, select: { friendlyId: true, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx index c48c45560b..c042f0ad74 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx @@ -2,12 +2,20 @@ import { ArrowUpCircleIcon, BookOpenIcon, ChatBubbleLeftEllipsisIcon, + MagnifyingGlassIcon, PauseIcon, PlayIcon, RectangleStackIcon, } from "@heroicons/react/20/solid"; import { DialogClose } from "@radix-ui/react-dialog"; -import { Form, useNavigation, useRevalidator, type MetaFunction } from "@remix-run/react"; +import { + Form, + useNavigate, + useNavigation, + useRevalidator, + useSearchParams, + type MetaFunction, +} from "@remix-run/react"; import { type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/server-runtime"; import { type RuntimeEnvironmentType } from "@trigger.dev/database"; import { useEffect, useState } from "react"; @@ -61,8 +69,11 @@ import { docsPath, EnvironmentParamSchema, v3BillingPath } from "~/utils/pathBui import { PauseEnvironmentService } from "~/v3/services/pauseEnvironment.server"; import { PauseQueueService } from "~/v3/services/pauseQueue.server"; import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; +import { Input } from "~/components/primitives/Input"; +import { useThrottle } from "~/hooks/useThrottle"; const SearchParamsSchema = z.object({ + query: z.string().optional(), page: z.coerce.number().min(1).default(1), }); @@ -79,7 +90,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params); const url = new URL(request.url); - const { page } = SearchParamsSchema.parse(Object.fromEntries(url.searchParams)); + const { page, query } = SearchParamsSchema.parse(Object.fromEntries(url.searchParams)); const project = await findProjectBySlug(organizationSlug, projectParam, userId); if (!project) { @@ -101,6 +112,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { const queueListPresenter = new QueueListPresenter(); const queues = await queueListPresenter.call({ environment, + query, page, }); @@ -198,7 +210,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { }; export default function Page() { - const { environment, queues, success, pagination, code, totalQueues } = + const { environment, queues, success, pagination, code, totalQueues, hasFilters } = useTypedLoaderData(); const organization = useOrganization(); @@ -285,10 +297,11 @@ export default function Page() { {success ? (
1 && "grid-rows-[1fr_auto]" + "grid max-h-full min-h-full grid-rows-[auto_1fr] overflow-x-auto", + pagination.totalPages > 1 && "grid-rows-[auto_1fr_auto]" )} > + @@ -407,7 +420,11 @@ export default function Page() {
- No queues found + + {hasFilters + ? "No queues found" + : "No queues found matching your filters"} +
@@ -663,3 +680,39 @@ export function isEnvironmentPauseResumeFormSubmission( formData.get("action") === "environment-resume") ); } + +export function QueueFilters() { + const [searchParams, setSearchParams] = useSearchParams(); + + const handleSearchChange = useThrottle((value: string) => { + if (value) { + setSearchParams((prev) => { + prev.set("query", value); + prev.delete("page"); + return prev; + }); + } else { + setSearchParams((prev) => { + prev.delete("query"); + prev.delete("page"); + return prev; + }); + } + }, 300); + + const search = searchParams.get("query") ?? ""; + + return ( +
+ handleSearchChange(e.target.value)} + /> +
+ ); +} From c5a2cb52a295a777742f690f7400f42b0e5276e9 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Wed, 30 Apr 2025 19:34:19 +0100 Subject: [PATCH 2/2] Fix for empty state copy --- .../route.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx index c042f0ad74..00ec1336ab 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx @@ -422,8 +422,8 @@ export default function Page() {
{hasFilters - ? "No queues found" - : "No queues found matching your filters"} + ? "No queues found matching your filters" + : "No queues found"}