import { BookOpenIcon } from "@heroicons/react/20/solid"; import { Outlet, useParams, type MetaFunction } from "@remix-run/react"; import { type LoaderFunctionArgs } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { AdminDebugTooltip } from "~/components/admin/debugTooltip"; import { NoWaitpointTokens } from "~/components/BlankStatePanels"; import { MainCenteredContainer, PageBody, PageContainer } from "~/components/layout/AppLayout"; import { ListPagination } from "~/components/ListPagination"; import { LinkButton } from "~/components/primitives/Buttons"; import { ClipboardField } from "~/components/primitives/ClipboardField"; import { CopyableText } from "~/components/primitives/CopyableText"; import { DateTime } from "~/components/primitives/DateTime"; import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader"; import { Paragraph } from "~/components/primitives/Paragraph"; import { RESIZABLE_PANEL_ANIMATION, ResizableHandle, ResizablePanel, ResizablePanelGroup, collapsibleHandleClassName, } from "~/components/primitives/Resizable"; import { Table, TableBody, TableCell, TableHeader, TableHeaderCell, TableRow, } from "~/components/primitives/Table"; import { SimpleTooltip } from "~/components/primitives/Tooltip"; import { RunTag } from "~/components/runs/v3/RunTag"; import { WaitpointStatusCombo } from "~/components/runs/v3/WaitpointStatus"; import { WaitpointSearchParamsSchema, WaitpointTokenFilters, } from "~/components/runs/v3/WaitpointTokenFilters"; import { V4Title } from "~/components/V4Badge"; import { useEnvironment } from "~/hooks/useEnvironment"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { findProjectBySlug } from "~/models/project.server"; import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server"; import { WaitpointListPresenter } from "~/presenters/v3/WaitpointListPresenter.server"; import { requireUserId } from "~/services/session.server"; import { runOpsNewReplicaClient, runOpsLegacyReplica, runOpsSplitReadEnabled, type PrismaClientOrTransaction, } from "~/db.server"; import { docsPath, EnvironmentParamSchema, v3WaitpointTokenPath } from "~/utils/pathBuilder"; export const meta: MetaFunction = () => { return [ { title: `Waitpoint tokens | Trigger.dev`, }, ]; }; export const loader = async ({ request, params }: LoaderFunctionArgs) => { const userId = await requireUserId(request); const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params); const url = new URL(request.url); const s = { id: url.searchParams.get("id") ?? undefined, statuses: url.searchParams.getAll("statuses"), idempotencyKey: url.searchParams.get("idempotencyKey") ?? undefined, tags: url.searchParams.getAll("tags"), period: url.searchParams.get("period") ?? undefined, from: url.searchParams.get("from") ?? undefined, to: url.searchParams.get("to") ?? undefined, cursor: url.searchParams.get("cursor") ?? undefined, direction: url.searchParams.get("direction") ?? undefined, }; const searchParams = WaitpointSearchParamsSchema.parse(s); const project = await findProjectBySlug(organizationSlug, projectParam, userId); if (!project) { throw new Response(undefined, { status: 404, statusText: "Project not found", }); } const environment = await findEnvironmentBySlug(project.id, envParam, userId); if (!environment) { throw new Response(undefined, { status: 404, statusText: "Environment not found", }); } try { const presenter = new WaitpointListPresenter(undefined, undefined, { runOpsNew: runOpsNewReplicaClient as unknown as PrismaClientOrTransaction, runOpsLegacyReplica: runOpsLegacyReplica as unknown as PrismaClientOrTransaction, splitEnabled: runOpsSplitReadEnabled, }); const result = await presenter.call({ environment, ...searchParams, }); return typedjson(result); } catch (error) { console.error(error); throw new Response(undefined, { status: 400, statusText: "Something went wrong, if this problem persists please contact support.", }); } }; export default function Page() { const { success: _success, tokens, pagination, hasFilters, hasAnyTokens, filters, } = useTypedLoaderData(); const organization = useOrganization(); const project = useProject(); const environment = useEnvironment(); const { waitpointParam } = useParams(); const isShowingWaitpoint = !!waitpointParam; return ( Waitpoint Tokens} /> Waitpoints docs {!hasAnyTokens ? ( ) : (
Created ID Callback URL Status Completed Idempotency Key Tags {tokens.length > 0 ? ( tokens.map((token) => { const ttlExpired = token.idempotencyKeyExpiresAt && token.idempotencyKeyExpiresAt < new Date(); const path = v3WaitpointTokenPath( organization, project, environment, token, filters ); const rowIsSelected = waitpointParam === token.id; return ( {token.completedAt ? : "–"} {token.idempotencyKey ? ( token.idempotencyKeyExpiresAt ? ( {ttlExpired ? ( (expired) ) : null} } buttonClassName={ttlExpired ? "opacity-50" : undefined} button={token.idempotencyKey} /> ) : ( token.idempotencyKey ) ) : ( "–" )}
{token.tags.map((tag) => ) || "–"}
); }) ) : (
No waitpoint tokens found
)}
{}} collapsedSize="0px" collapseAnimation={RESIZABLE_PANEL_ANIMATION} >
)}
); }