From 2aad2d29de135c03250f8035adfc4e3fdee21d37 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Mon, 10 Jul 2023 10:16:44 +0100 Subject: [PATCH] Early work on Trigger detail page --- .../app/components/navigation/Breadcrumb.tsx | 9 + .../DynamicTriggerSourcePresenter.server.ts | 58 ++++++ .../route.tsx | 169 ++++++++++++++++++ .../route.tsx | 39 +--- apps/webapp/app/utils/pathBuilder.ts | 4 + 5 files changed, 243 insertions(+), 36 deletions(-) create mode 100644 apps/webapp/app/presenters/DynamicTriggerSourcePresenter.server.ts create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers.triggersources.$triggerParam/route.tsx diff --git a/apps/webapp/app/components/navigation/Breadcrumb.tsx b/apps/webapp/app/components/navigation/Breadcrumb.tsx index 3996cb02e..11062011c 100644 --- a/apps/webapp/app/components/navigation/Breadcrumb.tsx +++ b/apps/webapp/app/components/navigation/Breadcrumb.tsx @@ -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 ( + + ); case "job": if (!organization || !project || !job) return null; return ( diff --git a/apps/webapp/app/presenters/DynamicTriggerSourcePresenter.server.ts b/apps/webapp/app/presenters/DynamicTriggerSourcePresenter.server.ts new file mode 100644 index 000000000..04daf71fb --- /dev/null +++ b/apps/webapp/app/presenters/DynamicTriggerSourcePresenter.server.ts @@ -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, + }; + } +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers.triggersources.$triggerParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers.triggersources.$triggerParam/route.tsx new file mode 100644 index 000000000..bc530b913 --- /dev/null +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers.triggersources.$triggerParam/route.tsx @@ -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(); + const organization = useOrganization(); + const project = useProject(); + + return ( + + + + + + + + + + + + + + {/*
+
+ External Triggers + + Webhooks are connected as external Triggers + + + + + Integration + Properties + Active + Environment + Go to page + + + + {triggers.map((t) => { + const path = triggerSourcePath(organization, project, t); + return ( + + + + + {t.integration.title} + + + + {t.params && ( + + {Object.entries(t.params).map( + ([label, value], index) => ( + + ) + )} + + } + content={ +
+ {Object.entries(t.params).map( + ([label, value], index) => ( + + ) + )} +
+ } + /> + )} +
+ + {t.active ? ( + + ) : ( + + )} + + + + + + + +
+ ); + })} +
+
+
+
*/} +
+
+ ); +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers/route.tsx index 653c712ae..416c08ae8 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers/route.tsx @@ -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 ( diff --git a/apps/webapp/app/utils/pathBuilder.ts b/apps/webapp/app/utils/pathBuilder.ts index 482788de6..582036b8f 100644 --- a/apps/webapp/app/utils/pathBuilder.ts +++ b/apps/webapp/app/utils/pathBuilder.ts @@ -35,6 +35,10 @@ export const IntegrationClientParamSchema = ProjectParamSchema.extend({ clientParam: z.string(), }); +export const TriggerSourceParamSchema = ProjectParamSchema.extend({ + triggerParam: z.string(), +}); + export function organizationsPath() { return `/`; }