Skip to content

Commit 25448bd

Browse files
committed
Dashboard pages and panels
1 parent 27f2bde commit 25448bd

File tree

8 files changed

+476
-10
lines changed

8 files changed

+476
-10
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function HttpCallbackIcon({ className }: { className?: string }) {
2+
return (
3+
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
4+
<path
5+
fillRule="evenodd"
6+
clipRule="evenodd"
7+
d="M15.1715 2C15.702 1.99999 16.2107 2.21072 16.5858 2.5858L21.4142 7.41435C21.7893 7.78943 22 8.29813 22 8.82855V15.1717C22 15.7021 21.7893 16.2109 21.4142 16.5859L16.5858 21.4144C16.2107 21.7894 15.702 22.0001 15.1716 22.0001H8.82842C8.29798 22.0001 7.78927 21.7894 7.4142 21.4144L2.58578 16.5859C2.21071 16.2109 2 15.7022 2 15.1717V8.82856C2 8.29814 2.21071 7.78943 2.58578 7.41436L7.41422 2.58586C7.78929 2.21079 8.29799 2.00007 8.82842 2.00007L15.1715 2ZM8.49997 9.00007C8.49997 8.44779 8.94769 8.00007 9.49997 8.00007H9.99997C10.5523 8.00007 11 8.44779 11 9.00007V15.0001C11 15.5524 10.5523 16.0001 9.99997 16.0001H9.49997C8.94769 16.0001 8.49997 15.5524 8.49997 15.0001V9.00007ZM14 8.00007C13.4477 8.00007 13 8.44779 13 9.00007V15.0001C13 15.5524 13.4477 16.0001 14 16.0001H14.5C15.0523 16.0001 15.5 15.5524 15.5 15.0001V9.00007C15.5 8.44779 15.0523 8.00007 14.5 8.00007H14Z"
8+
fill="currentColor"
9+
/>
10+
</svg>
11+
);
12+
}

apps/webapp/app/components/navigation/SideMenu.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import {
5454
v3SchedulesPath,
5555
v3TestPath,
5656
v3UsagePath,
57+
v3WaitpointHttpCallbacksPath,
5758
v3WaitpointTokensPath,
5859
} from "~/utils/pathBuilder";
5960
import { useKapaWidget } from "../../hooks/useKapaWidget";
@@ -80,6 +81,7 @@ import { HelpAndFeedback } from "./HelpAndFeedbackPopover";
8081
import { SideMenuHeader } from "./SideMenuHeader";
8182
import { SideMenuItem } from "./SideMenuItem";
8283
import { SideMenuSection } from "./SideMenuSection";
84+
import { HttpCallbackIcon } from "~/assets/icons/HttpCallbackIcon";
8385

8486
type SideMenuUser = Pick<User, "email" | "admin"> & { isImpersonating: boolean };
8587
export type SideMenuProject = Pick<
@@ -245,6 +247,12 @@ export function SideMenu({
245247
activeIconColor="text-sky-500"
246248
to={v3WaitpointTokensPath(organization, project, environment)}
247249
/>
250+
<SideMenuItem
251+
name="HTTP callbacks"
252+
icon={HttpCallbackIcon}
253+
activeIconColor="text-teal-500"
254+
to={v3WaitpointHttpCallbacksPath(organization, project, environment)}
255+
/>
248256
</SideMenuSection>
249257

250258
<SideMenuSection title="Manage">

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import { useOrganization } from "~/hooks/useOrganizations";
77
import { useProject } from "~/hooks/useProject";
88
import { type WaitpointDetail } from "~/presenters/v3/WaitpointPresenter.server";
99
import { ForceTimeout } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route";
10-
import { v3WaitpointTokenPath, v3WaitpointTokensPath } from "~/utils/pathBuilder";
10+
import {
11+
v3WaitpointHttpCallbackPath,
12+
v3WaitpointTokenPath,
13+
v3WaitpointTokensPath,
14+
} from "~/utils/pathBuilder";
1115
import { PacketDisplay } from "./PacketDisplay";
1216
import { WaitpointStatusCombo } from "./WaitpointStatus";
1317
import { RunTag } from "./RunTag";
@@ -41,9 +45,15 @@ export function WaitpointDetailTable({
4145
<Property.Value className="whitespace-pre-wrap">
4246
{linkToList ? (
4347
<TextLink
44-
to={v3WaitpointTokenPath(organization, project, environment, waitpoint, {
45-
id: waitpoint.id,
46-
})}
48+
to={
49+
waitpoint.resolver === "TOKEN"
50+
? v3WaitpointTokenPath(organization, project, environment, waitpoint, {
51+
id: waitpoint.id,
52+
})
53+
: v3WaitpointHttpCallbackPath(organization, project, environment, waitpoint, {
54+
id: waitpoint.id,
55+
})
56+
}
4757
>
4858
{waitpoint.id}
4959
</TextLink>

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { BasePresenter } from "./basePresenter.server";
1212
import { type WaitpointSearchParams } from "~/components/runs/v3/WaitpointTokenFilters";
1313
import { determineEngineVersion } from "~/v3/engineVersion.server";
1414
import { type WaitpointTokenStatus, type WaitpointTokenItem } from "@trigger.dev/core/v3";
15+
import { generateWaitpointCallbackUrl } from "./WaitpointPresenter.server";
1516

1617
const DEFAULT_PAGE_SIZE = 25;
1718

@@ -42,7 +43,7 @@ export type WaitpointListOptions = {
4243
type Result =
4344
| {
4445
success: true;
45-
tokens: WaitpointTokenItem[];
46+
tokens: (WaitpointTokenItem & { callbackUrl: string })[];
4647
pagination: {
4748
next: string | undefined;
4849
previous: string | undefined;
@@ -266,6 +267,7 @@ export class WaitpointListPresenter extends BasePresenter {
266267
success: true,
267268
tokens: tokensToReturn.map((token) => ({
268269
id: token.friendlyId,
270+
callbackUrl: generateWaitpointCallbackUrl(token.id),
269271
status: waitpointStatusToApiStatus(token.status, token.outputIsError),
270272
completedAt: token.completedAt ?? undefined,
271273
timeoutAt: token.completedAfter ?? undefined,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { useLocation } from "@remix-run/react";
2+
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
3+
import { typedjson, useTypedLoaderData } from "remix-typedjson";
4+
import { z } from "zod";
5+
import { ExitIcon } from "~/assets/icons/ExitIcon";
6+
import { LinkButton } from "~/components/primitives/Buttons";
7+
import { Header2, Header3 } from "~/components/primitives/Headers";
8+
import { useEnvironment } from "~/hooks/useEnvironment";
9+
import { useOrganization } from "~/hooks/useOrganizations";
10+
import { useProject } from "~/hooks/useProject";
11+
import { findProjectBySlug } from "~/models/project.server";
12+
import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
13+
import { WaitpointPresenter } from "~/presenters/v3/WaitpointPresenter.server";
14+
import { requireUserId } from "~/services/session.server";
15+
import { cn } from "~/utils/cn";
16+
import {
17+
EnvironmentParamSchema,
18+
v3WaitpointHttpCallbacksPath,
19+
v3WaitpointTokensPath,
20+
} from "~/utils/pathBuilder";
21+
import { CompleteWaitpointForm } from "../resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route";
22+
import { WaitpointDetailTable } from "~/components/runs/v3/WaitpointDetails";
23+
import { TaskRunsTable } from "~/components/runs/v3/TaskRunsTable";
24+
import { InfoIconTooltip } from "~/components/primitives/Tooltip";
25+
import { logger } from "~/services/logger.server";
26+
27+
const Params = EnvironmentParamSchema.extend({
28+
waitpointParam: z.string(),
29+
});
30+
31+
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
32+
const userId = await requireUserId(request);
33+
const { organizationSlug, projectParam, envParam, waitpointParam } = Params.parse(params);
34+
35+
const project = await findProjectBySlug(organizationSlug, projectParam, userId);
36+
if (!project) {
37+
throw new Response(undefined, {
38+
status: 404,
39+
statusText: "Project not found",
40+
});
41+
}
42+
43+
const environment = await findEnvironmentBySlug(project.id, envParam, userId);
44+
if (!environment) {
45+
throw new Response(undefined, {
46+
status: 404,
47+
statusText: "Environment not found",
48+
});
49+
}
50+
51+
try {
52+
const presenter = new WaitpointPresenter();
53+
const result = await presenter.call({
54+
friendlyId: waitpointParam,
55+
environmentId: environment.id,
56+
projectId: project.id,
57+
});
58+
59+
if (!result) {
60+
throw new Response(undefined, {
61+
status: 404,
62+
statusText: "Waitpoint not found",
63+
});
64+
}
65+
66+
return typedjson({ waitpoint: result });
67+
} catch (error) {
68+
logger.error("Error loading waitpoint for inspector", {
69+
error,
70+
organizationSlug,
71+
projectParam,
72+
envParam,
73+
waitpointParam,
74+
});
75+
throw new Response(undefined, {
76+
status: 400,
77+
statusText: "Something went wrong, if this problem persists please contact support.",
78+
});
79+
}
80+
};
81+
82+
export default function Page() {
83+
const { waitpoint } = useTypedLoaderData<typeof loader>();
84+
85+
const location = useLocation();
86+
87+
const organization = useOrganization();
88+
const project = useProject();
89+
const environment = useEnvironment();
90+
91+
return (
92+
<div
93+
className={cn(
94+
cn(
95+
"grid h-full max-h-full grid-rows-[2.5rem_1fr] overflow-hidden bg-background-bright",
96+
waitpoint.status === "WAITING" && "grid-rows-[2.5rem_1fr_auto]"
97+
)
98+
)}
99+
>
100+
<div className="mx-3 flex items-center justify-between gap-2 border-b border-grid-dimmed">
101+
<Header2 className={cn("whitespace-nowrap")}>{waitpoint.id}</Header2>
102+
<LinkButton
103+
to={`${v3WaitpointHttpCallbacksPath(organization, project, environment)}${
104+
location.search
105+
}`}
106+
variant="minimal/small"
107+
TrailingIcon={ExitIcon}
108+
shortcut={{ key: "esc" }}
109+
shortcutPosition="before-trailing-icon"
110+
className="pl-1"
111+
/>
112+
</div>
113+
<div className="overflow-y-auto pt-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
114+
<div className="px-3">
115+
<WaitpointDetailTable waitpoint={waitpoint} />
116+
</div>
117+
<div className="flex flex-col gap-1 pt-6">
118+
<div className="mb-1 flex items-center gap-1 pl-3">
119+
<Header3>Related runs</Header3>
120+
<InfoIconTooltip content="These runs have been blocked by this waitpoint." />
121+
</div>
122+
<TaskRunsTable
123+
total={waitpoint.connectedRuns.length}
124+
hasFilters={false}
125+
filters={{
126+
tasks: [],
127+
versions: [],
128+
statuses: [],
129+
environments: [],
130+
from: undefined,
131+
to: undefined,
132+
}}
133+
runs={waitpoint.connectedRuns}
134+
isLoading={false}
135+
variant="bright"
136+
/>
137+
</div>
138+
</div>
139+
{waitpoint.status === "WAITING" && (
140+
<div>
141+
<CompleteWaitpointForm waitpoint={waitpoint} />
142+
</div>
143+
)}
144+
</div>
145+
);
146+
}

0 commit comments

Comments
 (0)