Early work on Trigger detail page

This commit is contained in:
Matt Aitken
2023-07-10 10:16:44 +01:00
parent 400354ee9e
commit 2aad2d29de
5 changed files with 243 additions and 36 deletions
@@ -38,6 +38,7 @@ export type Breadcrumb = {
| "integration-connections"
| "integration-scopes"
| "triggers"
| "trigger"
| "environments"
| "job"
| "test"
@@ -177,6 +178,14 @@ function BreadcrumbItem({
title={"Triggers"}
/>
);
case "trigger":
if (!organization || !project) return null;
return (
<BreadcrumbLink
to={projectTriggersPath(organization, project)}
title={"Triggers"}
/>
);
case "job":
if (!organization || !project || !job) return null;
return (
@@ -0,0 +1,58 @@
import { TriggerSource, User } from "@trigger.dev/database";
import { PrismaClient, prisma } from "~/db.server";
import { Organization } from "~/models/organization.server";
import { Project } from "~/models/project.server";
export class DynamicTriggerSourcePresenter {
#prismaClient: PrismaClient;
constructor(prismaClient: PrismaClient = prisma) {
this.#prismaClient = prismaClient;
}
public async call({
userId,
projectSlug,
organizationSlug,
triggerSourceId,
}: {
userId: User["id"];
projectSlug: Project["slug"];
organizationSlug: Organization["slug"];
triggerSourceId: TriggerSource["id"];
}) {
const trigger = await this.#prismaClient.triggerSource.findUnique({
select: {
id: true,
active: true,
dynamicTrigger: true,
integration: {
select: {
id: true,
title: true,
slug: true,
definitionId: true,
setupStatus: true,
},
},
environment: {
select: {
type: true,
},
},
createdAt: true,
updatedAt: true,
params: true,
registrations: true,
sourceRegistrationJob: true,
},
where: {
id: triggerSourceId,
},
});
return {
trigger,
};
}
}
@@ -0,0 +1,169 @@
import { Response } from "@remix-run/node";
import type { LoaderArgs } from "@remix-run/server-runtime";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
import {
PageHeader,
PageInfoGroup,
PageInfoProperty,
PageInfoRow,
PageTitle,
PageTitleRow,
} from "~/components/primitives/PageHeader";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import { DynamicTriggerSourcePresenter } from "~/presenters/DynamicTriggerSourcePresenter.server";
import { requireUser } from "~/services/session.server";
import { Handle } from "~/utils/handle";
import {
TriggerSourceParamSchema,
projectTriggersPath,
} from "~/utils/pathBuilder";
export const loader = async ({ request, params }: LoaderArgs) => {
const user = await requireUser(request);
const { organizationSlug, projectParam, triggerParam } =
TriggerSourceParamSchema.parse(params);
const presenter = new DynamicTriggerSourcePresenter();
const { trigger } = await presenter.call({
userId: user.id,
organizationSlug,
projectSlug: projectParam,
triggerSourceId: triggerParam,
});
if (!trigger) {
throw new Response("Trigger not found", {
status: 404,
statusText: "Not Found",
});
}
return typedjson({ trigger });
};
export const handle: Handle = {
breadcrumb: {
slug: "trigger",
},
};
//todo outlet somewhere, look at how I did this with the integration client page.
export default function Integrations() {
const { trigger } = useTypedLoaderData<typeof loader>();
const organization = useOrganization();
const project = useProject();
return (
<PageContainer>
<PageHeader>
<PageTitleRow>
<PageTitle
title="Trigger detail"
backButton={{
to: projectTriggersPath(organization, project),
text: "Triggers",
}}
/>
</PageTitleRow>
<PageInfoRow>
<PageInfoGroup>
<PageInfoProperty
icon={trigger.integration.definitionId}
label="Integration"
value={trigger.integration.title}
/>
</PageInfoGroup>
</PageInfoRow>
</PageHeader>
<PageBody scrollable={false}>
{/* <div className="h-full overflow-y-auto p-4 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700">
<div>
<Header2 spacing>External Triggers</Header2>
<Paragraph variant="small" spacing>
Webhooks are connected as external Triggers
</Paragraph>
<Table>
<TableHeader>
<TableRow>
<TableHeaderCell>Integration</TableHeaderCell>
<TableHeaderCell>Properties</TableHeaderCell>
<TableHeaderCell>Active</TableHeaderCell>
<TableHeaderCell>Environment</TableHeaderCell>
<TableHeaderCell hiddenLabel>Go to page</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{triggers.map((t) => {
const path = triggerSourcePath(organization, project, t);
return (
<TableRow key={t.id}>
<TableCell to={path}>
<span className="flex items-center gap-1">
<NamedIcon
name={t.integration.definitionId}
className="h-6 w-6"
/>
{t.integration.title}
</span>
</TableCell>
<TableCell to={path}>
{t.params && (
<SimpleTooltip
button={
<div className="flex max-w-[200px] items-start justify-start gap-5 truncate">
{Object.entries(t.params).map(
([label, value], index) => (
<LabelValueStack
key={index}
label={label}
value={value}
className="last:truncate"
/>
)
)}
</div>
}
content={
<div className="flex flex-col gap-2">
{Object.entries(t.params).map(
([label, value], index) => (
<LabelValueStack
key={index}
label={label}
value={value}
/>
)
)}
</div>
}
/>
)}
</TableCell>
<TableCell to={path}>
{t.active ? (
<CheckCircleIcon className="h-6 w-6 text-green-500" />
) : (
<XCircleIcon className="h-6 w-6 text-rose-500" />
)}
</TableCell>
<TableCell to={path}>
<span className="flex">
<EnvironmentLabel environment={t.environment} />
</span>
</TableCell>
<TableCellChevron to={path} />
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</div> */}
</PageBody>
</PageContainer>
);
}
@@ -1,38 +1,20 @@
import {
CheckCircleIcon,
ChevronRightIcon,
XCircleIcon,
} from "@heroicons/react/24/solid";
import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/solid";
import type { LoaderArgs } from "@remix-run/server-runtime";
import { useCallback, useState } from "react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { LogoIcon } from "~/components/LogoIcon";
import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel";
import { HowToConnectAnIntegration } from "~/components/helpContent/HelpContentText";
import { ConnectToIntegrationSheet } from "~/components/integrations/ConnectToIntegrationSheet";
import { IntegrationWithMissingFieldSheet } from "~/components/integrations/IntegrationWithMissingFieldSheet";
import { NoIntegrationSheet } from "~/components/integrations/NoIntegrationSheet";
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
import { LinkButton } from "~/components/primitives/Buttons";
import { Callout } from "~/components/primitives/Callout";
import { DateTime } from "~/components/primitives/DateTime";
import { Header2 } from "~/components/primitives/Headers";
import { Help, HelpContent, HelpTrigger } from "~/components/primitives/Help";
import { Input } from "~/components/primitives/Input";
import { LabelValueStack } from "~/components/primitives/LabelValueStack";
import { NamedIcon, NamedIconInBox } from "~/components/primitives/NamedIcon";
import { NamedIcon } from "~/components/primitives/NamedIcon";
import {
PageButtons,
PageDescription,
PageHeader,
PageTitle,
PageTitleRow,
} from "~/components/primitives/PageHeader";
import { Paragraph } from "~/components/primitives/Paragraph";
import { Switch } from "~/components/primitives/Switch";
import {
Table,
TableBlankRow,
TableBody,
TableCell,
TableCellChevron,
@@ -43,23 +25,10 @@ import {
import { SimpleTooltip } from "~/components/primitives/Tooltip";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import { useTextFilter } from "~/hooks/useTextFilter";
import { Organization } from "~/models/organization.server";
import { Project } from "~/models/project.server";
import {
Client,
IntegrationOrApi,
IntegrationsPresenter,
} from "~/presenters/IntegrationsPresenter.server";
import { TriggersPresenter } from "~/presenters/TriggersPresenter.server";
import { requireUser } from "~/services/session.server";
import { Handle } from "~/utils/handle";
import {
ProjectParamSchema,
docsCreateIntegration,
integrationClientPath,
triggerSourcePath,
} from "~/utils/pathBuilder";
import { ProjectParamSchema, triggerSourcePath } from "~/utils/pathBuilder";
export const loader = async ({ request, params }: LoaderArgs) => {
const user = await requireUser(request);
@@ -86,8 +55,6 @@ export default function Integrations() {
const organization = useOrganization();
const project = useProject();
console.log(triggers);
return (
<PageContainer>
<PageHeader>
+4
View File
@@ -35,6 +35,10 @@ export const IntegrationClientParamSchema = ProjectParamSchema.extend({
clientParam: z.string(),
});
export const TriggerSourceParamSchema = ProjectParamSchema.extend({
triggerParam: z.string(),
});
export function organizationsPath() {
return `/`;
}