From 5da843953c76b695298491def2da24e5921eca51 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Tue, 21 Jan 2025 13:44:25 +0000 Subject: [PATCH 01/23] Delete v2 Stripe routes --- .../route.tsx | 32 ----------------- .../route.tsx | 32 ----------------- .../route.tsx | 34 ------------------- 3 files changed, 98 deletions(-) delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationId.subscription.canceled/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationId.subscription.complete/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationId.subscription.failed/route.tsx diff --git a/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.canceled/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.canceled/route.tsx deleted file mode 100644 index a2f85995aa..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.canceled/route.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { LoaderFunctionArgs, redirect } from "@remix-run/server-runtime"; -import { z } from "zod"; -import { prisma } from "~/db.server"; -import { redirectWithErrorMessage } from "~/models/message.server"; -import { plansPath } from "~/utils/pathBuilder"; - -const ParamsSchema = z.object({ - organizationId: z.string(), -}); - -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const { organizationId } = ParamsSchema.parse(params); - - const org = await prisma.organization.findUnique({ - select: { - slug: true, - }, - where: { - id: organizationId, - }, - }); - - if (!org) { - throw new Response(null, { status: 404 }); - } - - return redirectWithErrorMessage( - `${plansPath({ slug: org.slug })}`, - request, - "You didn't complete your details on Stripe. Please try again." - ); -}; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.complete/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.complete/route.tsx deleted file mode 100644 index 0282294290..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.complete/route.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { z } from "zod"; -import { prisma } from "~/db.server"; -import { redirectWithSuccessMessage } from "~/models/message.server"; -import { subscribedPath } from "~/utils/pathBuilder"; - -const ParamsSchema = z.object({ - organizationId: z.string(), -}); - -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const { organizationId } = ParamsSchema.parse(params); - - const org = await prisma.organization.findUnique({ - select: { - slug: true, - }, - where: { - id: organizationId, - }, - }); - - if (!org) { - throw new Response(null, { status: 404 }); - } - - return redirectWithSuccessMessage( - `${subscribedPath({ slug: org.slug })}`, - request, - "You are now subscribed to Trigger.dev" - ); -}; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.failed/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.failed/route.tsx deleted file mode 100644 index efe9ddb578..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.failed/route.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { z } from "zod"; -import { prisma } from "~/db.server"; -import { redirectWithErrorMessage } from "~/models/message.server"; -import { plansPath } from "~/utils/pathBuilder"; - -const ParamsSchema = z.object({ - organizationId: z.string(), -}); - -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const { organizationId } = ParamsSchema.parse(params); - - const org = await prisma.organization.findUnique({ - select: { - slug: true, - }, - where: { - id: organizationId, - }, - }); - - if (!org) { - throw new Response(null, { status: 404 }); - } - - const url = new URL(request.url); - const searchParams = new URLSearchParams(url.search); - const reason = searchParams.get("reason"); - - let errorMessage = reason ? decodeURIComponent(reason) : "Subscribing failed to complete"; - - return redirectWithErrorMessage(`${plansPath({ slug: org.slug })}`, request, errorMessage); -}; From 24a667aef42db21e0f92858b01f345ca0892652c Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Tue, 21 Jan 2025 13:44:40 +0000 Subject: [PATCH 02/23] Delete v2 billing/usage pages --- .../route.tsx | 235 ------------------ .../route.tsx | 74 ------ .../route.tsx | 146 ----------- 3 files changed, 455 deletions(-) delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.billing._index/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing._index/route.tsx deleted file mode 100644 index 2bcc152517..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing._index/route.tsx +++ /dev/null @@ -1,235 +0,0 @@ -import { ArrowRightIcon } from "@heroicons/react/20/solid"; -import { ArrowUpCircleIcon } from "@heroicons/react/24/outline"; -import { Await, useLoaderData } from "@remix-run/react"; -import { DataFunctionArgs, defer } from "@remix-run/server-runtime"; -import { Suspense } from "react"; -import { Bar, BarChart, ResponsiveContainer, Tooltip, TooltipProps, XAxis, YAxis } from "recharts"; -import { ConcurrentRunsChart } from "~/components/billing/v2/ConcurrentRunsChart"; -import { UsageBar } from "~/components/billing/v2/UsageBar"; -import { LinkButton } from "~/components/primitives/Buttons"; -import { Callout } from "~/components/primitives/Callout"; -import { DailyRunsChart } from "~/components/billing/v2/DailyRunsChat"; -import { DateTime } from "~/components/primitives/DateTime"; -import { Header2, Header3 } from "~/components/primitives/Headers"; -import { Paragraph } from "~/components/primitives/Paragraph"; -import { Spinner } from "~/components/primitives/Spinner"; -import { useOrganization } from "~/hooks/useOrganizations"; -import { OrgUsagePresenter } from "~/presenters/OrgUsagePresenter.server"; -import { requireUserId } from "~/services/session.server"; -import { formatCurrency, formatNumberCompact } from "~/utils/numberFormatter"; -import { OrganizationParamsSchema, plansPath } from "~/utils/pathBuilder"; -import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; - -export async function loader({ request, params }: DataFunctionArgs) { - const userId = await requireUserId(request); - const { organizationSlug } = OrganizationParamsSchema.parse(params); - - const presenter = new OrgUsagePresenter(); - const usageData = presenter.call({ userId, slug: organizationSlug, request }); - return defer({ usageData }); -} - -const CustomTooltip = ({ active, payload, label }: TooltipProps) => { - if (active && payload) { - return ( -
-

{label}:

-

{payload[0].value}

-
- ); - } - - return null; -}; - -export default function Page() { - const organization = useOrganization(); - const { usageData } = useLoaderData(); - const currentPlan = useCurrentPlan(); - - const hitsRunLimit = currentPlan?.usage?.runCountCap - ? currentPlan.usage.currentRunCount > currentPlan.usage.runCountCap - : false; - - return ( -
- - - - - } - > - There was a problem loading your usage data.} - > - {(data) => { - const hitConcurrencyLimit = currentPlan?.subscription?.limits.concurrentRuns - ? data.concurrencyData.some( - (c) => - c.maxConcurrentRuns >= - (currentPlan.subscription?.limits.concurrentRuns ?? Infinity) - ) - : false; - - return ( - <> -
- Concurrent runs -
- {hitConcurrencyLimit && ( - - Increase concurrent runs - - } - > - {`Some of your runs are being queued because the number of concurrent runs is limited to - ${currentPlan?.subscription?.limits.concurrentRuns}.`} - - )} - -
-
- -
- Runs -
- {hitsRunLimit && ( - - Upgrade - - } - > - - You have exceeded the monthly{" "} - {formatNumberCompact(currentPlan?.subscription?.limits.runs ?? 0)} runs - limit. Upgrade to a paid plan before{" "} - - . - - - )} -
-
- {data.runCostEstimation !== undefined && - data.projectedRunCostEstimation !== undefined && ( -
-
- Month-to-date -

- {formatCurrency(data.runCostEstimation, false)} -

-
- -
- Projected -

- {formatCurrency(data.projectedRunCostEstimation, false)} -

-
-
- )} - -
-
- Monthly runs - {!data.hasMonthlyRunData && ( - - No runs to show - - )} - - - - `${value}`} - /> - } - /> - - - -
-
-
- Daily runs - -
-
-
- - ); - }} -
-
-
- ); -} - -function LoadingElement({ title }: { title: string }) { - return ( -
- {title} -
- -
-
- ); -} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx deleted file mode 100644 index 5406cb0c85..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { redirect, typedjson, useTypedLoaderData } from "remix-typedjson"; -import { PricingCalculator } from "~/components/billing/v2/PricingCalculator"; -import { PricingTiers } from "~/components/billing/v2/PricingTiers"; -import { RunsVolumeDiscountTable } from "~/components/billing/v2/RunsVolumeDiscountTable"; -import { Callout } from "~/components/primitives/Callout"; -import { Header2 } from "~/components/primitives/Headers"; -import { featuresForRequest } from "~/features.server"; -import { OrgBillingPlanPresenter } from "~/presenters/OrgBillingPlanPresenter"; -import { formatNumberCompact } from "~/utils/numberFormatter"; -import { OrganizationParamsSchema, organizationBillingPath } from "~/utils/pathBuilder"; -import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; - -export async function loader({ params, request }: LoaderFunctionArgs) { - const { organizationSlug } = OrganizationParamsSchema.parse(params); - - const { isManagedCloud } = featuresForRequest(request); - if (!isManagedCloud) { - return redirect(organizationBillingPath({ slug: organizationSlug })); - } - - const presenter = new OrgBillingPlanPresenter(); - const result = await presenter.call({ slug: organizationSlug, isManagedCloud }); - if (!result) { - throw new Response(null, { status: 404 }); - } - - return typedjson({ - plans: result.plans, - maxConcurrency: result.maxConcurrency, - organizationSlug, - }); -} - -export default function Page() { - const { plans, maxConcurrency, organizationSlug } = useTypedLoaderData(); - const currentPlan = useCurrentPlan(); - - const hitConcurrencyLimit = - currentPlan?.subscription?.limits.concurrentRuns && maxConcurrency - ? maxConcurrency >= currentPlan.subscription!.limits.concurrentRuns! - : false; - - const hitRunLimit = currentPlan?.usage?.runCountCap - ? currentPlan.usage.currentRunCount > currentPlan.usage.runCountCap - : false; - - return ( -
- {hitConcurrencyLimit && ( - - Some of your runs are being queued because your run concurrency is limited to{" "} - {currentPlan?.subscription?.limits.concurrentRuns}. - - )} - {hitRunLimit && ( - - {`You have exceeded the monthly - ${formatNumberCompact(currentPlan!.subscription!.limits.runs!)} runs limit. Upgrade so you - can continue to perform runs.`} - - )} - -
- Estimate your usage -
- -
- -
-
-
- ); -} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx deleted file mode 100644 index 0c91c510e2..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import { CalendarDaysIcon, ReceiptRefundIcon } from "@heroicons/react/20/solid"; -import { ArrowUpCircleIcon } from "@heroicons/react/24/outline"; -import { Outlet } from "@remix-run/react"; -import { ActiveSubscription } from "@trigger.dev/platform/v2"; -import { formatDurationInDays } from "@trigger.dev/core/v3"; -import { PageBody, PageContainer } from "~/components/layout/AppLayout"; -import { LinkButton } from "~/components/primitives/Buttons"; -import { DateTime } from "~/components/primitives/DateTime"; -import { - PageAccessories, - NavBar, - PageInfoGroup, - PageInfoProperty, - PageInfoRow, - PageTabs, - PageTitle, -} from "~/components/primitives/PageHeader"; -import { useFeatures } from "~/hooks/useFeatures"; -import { useOrganization } from "~/hooks/useOrganizations"; -import { plansPath, stripePortalPath, usagePath } from "~/utils/pathBuilder"; -import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; -import { Callout } from "~/components/primitives/Callout"; - -function planLabel(subscription: ActiveSubscription | undefined, periodEnd: Date) { - if (!subscription) { - return "You're currently on the Free plan"; - } - if (!subscription.isPaying) { - return `You're currently on the ${subscription.plan.title} plan`; - } - const costDescription = subscription.plan.concurrentRuns.pricing - ? `\$${subscription.plan.concurrentRuns.pricing.tierCost}/mo` - : ""; - if (subscription.canceledAt) { - return ( - <> - You're on the {costDescription} {subscription.plan.title} plan until{" "} - when you'll be on the Free plan - - ); - } - - return `You're currently on the ${costDescription} ${subscription.plan.title} plan`; -} - -export default function Page() { - const organization = useOrganization(); - const { isManagedCloud } = useFeatures(); - const currentPlan = useCurrentPlan(); - - const hasV3Project = organization.projects.some((p) => p.version === "V3"); - const hasV2Project = organization.projects.some((p) => p.version === "V2"); - const allV3Projects = organization.projects.every((p) => p.version === "V3"); - - return ( - - - - - {isManagedCloud && ( - <> - {currentPlan?.subscription?.isPaying && ( - <> - - Invoices - - - Manage card details - - - )} - {hasV2Project && ( - - Upgrade - - )} - - )} - - - -
-
- {hasV3Project ? ( - - This organization has a mix of v2 and v3 projects. They have separate subscriptions, - this is the usage and billing for v2. - - ) : null} - {hasV2Project && ( - - - {currentPlan?.subscription && currentPlan.usage && ( - } - value={planLabel(currentPlan.subscription, currentPlan.usage.periodEnd)} - /> - )} - {currentPlan?.subscription?.isPaying && currentPlan.usage && ( - } - label={"Billing period"} - value={ - <> - to{" "} - ( - {formatDurationInDays(currentPlan.usage.periodRemainingDuration)}{" "} - remaining) - - } - /> - )} - - - )} - {hasV2Project && isManagedCloud && ( - - )} -
- {hasV2Project && ( -
- -
- )} -
-
-
- ); -} From 6e7578120d1ae2b5fe2c3c7119120d368be010d2 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Tue, 21 Jan 2025 13:44:49 +0000 Subject: [PATCH 03/23] Delete v2 integration pages --- .../route.tsx | 470 ------------------ .../route.tsx | 88 ---- .../route.tsx | 88 ---- .../route.tsx | 35 -- .../route.tsx | 116 ----- 5 files changed, 797 deletions(-) delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam._index/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam.connections/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam.scopes/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam/route.tsx diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations/route.tsx deleted file mode 100644 index 6a790b7171..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations/route.tsx +++ /dev/null @@ -1,470 +0,0 @@ -import { ChevronRightIcon } from "@heroicons/react/24/solid"; -import type { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { useState } from "react"; -import { typedjson, useTypedLoaderData } from "remix-typedjson"; -import { IntegrationIcon } from "~/assets/icons/IntegrationIcon"; -import { Feedback } from "~/components/Feedback"; -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 { DetailCell } from "~/components/primitives/DetailCell"; -import { Header2 } from "~/components/primitives/Headers"; -import { Help, HelpContent, HelpTrigger } from "~/components/primitives/Help"; -import { Input } from "~/components/primitives/Input"; -import { NamedIcon } from "~/components/primitives/NamedIcon"; -import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader"; -import { Switch } from "~/components/primitives/Switch"; -import { - Table, - TableBlankRow, - TableBody, - TableCell, - TableCellChevron, - TableHeader, - TableHeaderCell, - TableRow, -} from "~/components/primitives/Table"; -import { SimpleTooltip } from "~/components/primitives/Tooltip"; -import { MatchedOrganization, useOrganization } from "~/hooks/useOrganizations"; -import { useTextFilter } from "~/hooks/useTextFilter"; -import { - Client, - IntegrationOrApi, - IntegrationsPresenter, -} from "~/presenters/IntegrationsPresenter.server"; -import { requireUserId } from "~/services/session.server"; -import { OrganizationParamsSchema, docsPath, integrationClientPath } from "~/utils/pathBuilder"; - -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const userId = await requireUserId(request); - const { organizationSlug } = OrganizationParamsSchema.parse(params); - - const presenter = new IntegrationsPresenter(); - const data = await presenter.call({ - userId, - organizationSlug, - }); - - return typedjson(data); -}; - -export default function Integrations() { - const { clients, clientMissingFields, options, callbackUrl } = - useTypedLoaderData(); - const organization = useOrganization(); - - return ( - - - - - - Integrations documentation - - - - - -
- -
- {clientMissingFields.length > 0 && ( - - )} - -
-
-
-
- ); -} - -function PossibleIntegrationsList({ - options, - organizationId, - callbackUrl, -}: { - options: IntegrationOrApi[]; - organizationId: string; - callbackUrl: string; -}) { - const [onlyShowIntegrations, setOnlyShowIntegrations] = useState(false); - const optionsToShow = onlyShowIntegrations - ? options.filter((o) => o.type === "integration") - : options; - const { filterText, setFilterText, filteredItems } = useTextFilter({ - items: optionsToShow, - filter: (integration, text) => integration.name.toLowerCase().includes(text.toLowerCase()), - }); - - return ( -
-
-
- Connect an API - - Trigger.dev Integrations - - } - /> -
- setFilterText(e.target.value)} - /> -
- {filteredItems.map((option) => { - switch (option.type) { - case "integration": - return ( - - } - /> - ); - case "api": - return ( - - } - /> - ); - } - })} -
- Missing an API? - - - - } - defaultValue="feature" - /> - - Create an Integration - - - -
-
- ); -} - -function ConnectedIntegrationsList({ - clients, - organization, -}: { - clients: Client[]; - organization: MatchedOrganization; -}) { - const { filterText, setFilterText, filteredItems } = useTextFilter({ - items: clients, - filter: (client, text) => { - if (client.title.toLowerCase().includes(text.toLowerCase())) { - return true; - } - - if ( - client.customClientId && - client.customClientId.toLowerCase().includes(text.toLowerCase()) - ) { - return true; - } - - if (client.integration.name.toLowerCase().includes(text.toLowerCase())) { - return true; - } - - if (client.authMethod.name.toLowerCase().includes(text.toLowerCase())) { - return true; - } - - return false; - }, - }); - - return ( - - - - -
- Your connected Integrations -
- {clients.length > 0 && ( -
-
- setFilterText(e.target.value)} - /> - -
- - - - Name - API - ID - Type - Jobs - Scopes - Client id - Connections - Added - Go to page - - - - {filteredItems.length === 0 ? ( - -
- - No connected Integrations match your filters. - -
-
- ) : ( - <> - {filteredItems.map((client) => { - const path = integrationClientPath(organization, client); - return ( - - {client.title} - - - - {client.integration.name} - - - {client.slug} - {client.authMethod.name} - {client.jobCount} - - {client.authSource === "LOCAL" ? "–" : client.scopesCount} - - - {client.authSource === "LOCAL" ? ( - "–" - ) : ( - Auto - ) - } - content={ - client.customClientId - ? client.customClientId - : "This uses the Trigger.dev OAuth client" - } - /> - )} - - - {client.authSource === "LOCAL" ? "–" : client.connectionsCount} - - - - - - - ); - })} - - )} -
-
-
- )} -
- ); -} - -function IntegrationsWithMissingFields({ - clients, - organizationId, - callbackUrl, - options, -}: { - clients: Client[]; - organizationId: string; - callbackUrl: string; - options: IntegrationOrApi[]; -}) { - const integrationsList = options.flatMap((o) => (o.type === "integration" ? [o] : [])); - - return ( -
- - - Integrations requiring configuration - - - - - - Name - API - Added - Go to page - - - - {clients.map((client) => { - const integration = integrationsList.find( - (i) => i.identifier === client.integrationIdentifier - ); - - if (!integration) { - return
Can't find matching integration
; - } - - return ( - - - - {client.title} - - } - callbackUrl={callbackUrl} - existingIntegration={client} - className="flex w-full cursor-pointer justify-start" - /> - - - - - {client.integration.name} - - } - callbackUrl={callbackUrl} - existingIntegration={client} - className="flex w-full cursor-pointer justify-start" - /> - - - } - callbackUrl={callbackUrl} - existingIntegration={client} - className="flex w-full cursor-pointer justify-start" - /> - - - - } - callbackUrl={callbackUrl} - existingIntegration={client} - className="flex w-full cursor-pointer justify-end" - /> - - - ); - })} -
-
-
- ); -} - -function AddIntegrationConnection({ - identifier, - name, - isIntegration, - icon, -}: { - identifier: string; - name: string; - isIntegration: boolean; - icon?: string; -}) { - return ( - - ); -} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam._index/route.tsx deleted file mode 100644 index ddd9b1dd46..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam._index/route.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { typedjson, useTypedLoaderData } from "remix-typedjson"; -import { HowToUseThisIntegration } from "~/components/helpContent/HelpContentText"; -import { JobsTable } from "~/components/jobs/JobsTable"; -import { Callout } from "~/components/primitives/Callout"; -import { Help, HelpContent, HelpTrigger } from "~/components/primitives/Help"; -import { Input } from "~/components/primitives/Input"; -import { Paragraph } from "~/components/primitives/Paragraph"; -import { useFilterJobs } from "~/hooks/useFilterJobs"; -import { useIntegrationClient } from "~/hooks/useIntegrationClient"; -import { JobListPresenter } from "~/presenters/JobListPresenter.server"; -import { requireUserId } from "~/services/session.server"; -import { cn } from "~/utils/cn"; -import { IntegrationClientParamSchema, docsIntegrationPath } from "~/utils/pathBuilder"; - -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const userId = await requireUserId(request); - const { organizationSlug, clientParam } = IntegrationClientParamSchema.parse(params); - - const jobsPresenter = new JobListPresenter(); - - const jobs = await jobsPresenter.call({ - userId, - organizationSlug, - integrationSlug: clientParam, - }); - - return typedjson({ jobs }); -}; - -export default function Page() { - const { jobs } = useTypedLoaderData(); - const client = useIntegrationClient(); - - const { filterText, setFilterText, filteredItems } = useFilterJobs(jobs); - - return ( - - {(open) => ( -
-
-
- {jobs.length !== 0 && ( - setFilterText(e.target.value)} - /> - )} - -
- {jobs.length === 0 ? ( -
- Jobs using this Integration will appear here. -
- ) : ( - - )} -
- - - - View the docs to learn more about using the {client.integration.name} Integration. - - -
- )} -
- ); -} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam.connections/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam.connections/route.tsx deleted file mode 100644 index 30a12e0223..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam.connections/route.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { typedjson, useTypedLoaderData } from "remix-typedjson"; -import { connectionType } from "~/components/integrations/connectionType"; -import { DateTime } from "~/components/primitives/DateTime"; -import { Paragraph } from "~/components/primitives/Paragraph"; -import { - Table, - TableBlankRow, - TableBody, - TableCell, - TableHeader, - TableHeaderCell, - TableRow, -} from "~/components/primitives/Table"; -import { IntegrationClientConnectionsPresenter } from "~/presenters/IntegrationClientConnectionsPresenter.server"; -import { requireUserId } from "~/services/session.server"; -import { IntegrationClientParamSchema } from "~/utils/pathBuilder"; - -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const userId = await requireUserId(request); - const { organizationSlug, clientParam } = IntegrationClientParamSchema.parse(params); - - const presenter = new IntegrationClientConnectionsPresenter(); - const { connections } = await presenter.call({ - userId: userId, - organizationSlug, - clientSlug: clientParam, - }); - - return typedjson({ connections }); -}; - -export default function Page() { - const { connections } = useTypedLoaderData(); - - return ( - - - - ID - Type - Run count - Account - Expires - Created - Updated - - - - {connections.length > 0 ? ( - connections.map((connection) => { - return ( - - {connection.id} - {connectionType(connection.type)} - {connection.runCount} - {connection.metadata?.account ?? "–"} - - - - {} - {} - - ); - }) - ) : ( - - - No connections - - - )} - -
- ); -} - -function ExpiresAt({ expiresAt }: { expiresAt: Date | null }) { - if (!expiresAt) return <>–; - - const inPast = expiresAt < new Date(); - - return ( - - - - ); -} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam.scopes/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam.scopes/route.tsx deleted file mode 100644 index 41b3b3249a..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam.scopes/route.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { typedjson, useTypedLoaderData } from "remix-typedjson"; -import { Paragraph } from "~/components/primitives/Paragraph"; -import { IntegrationClientScopesPresenter } from "~/presenters/IntegrationClientScopesPresenter.server"; -import { requireUserId } from "~/services/session.server"; -import { IntegrationClientParamSchema } from "~/utils/pathBuilder"; - -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const userId = await requireUserId(request); - const { organizationSlug, clientParam } = IntegrationClientParamSchema.parse(params); - - const presenter = new IntegrationClientScopesPresenter(); - const { scopes } = await presenter.call({ - userId: userId, - organizationSlug, - clientSlug: clientParam, - }); - - return typedjson({ scopes }); -}; - -export default function Page() { - const { scopes } = useTypedLoaderData(); - - return ( -
    - {scopes.map((scope) => ( -
  • - {scope.name} - {scope.description} -
  • - ))} -
- ); -} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam/route.tsx deleted file mode 100644 index 1ba642eb9c..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.integrations_.$clientParam/route.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { Outlet } from "@remix-run/react"; -import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { typedjson, useTypedLoaderData } from "remix-typedjson"; -import { connectionType } from "~/components/integrations/connectionType"; -import { PageBody, PageContainer } from "~/components/layout/AppLayout"; -import { ClipboardField } from "~/components/primitives/ClipboardField"; -import { DateTime } from "~/components/primitives/DateTime"; -import { - NavBar, - PageInfoGroup, - PageInfoProperty, - PageInfoRow, - PageTabs, - PageTitle, -} from "~/components/primitives/PageHeader"; -import { useOrganization } from "~/hooks/useOrganizations"; -import { IntegrationClientPresenter } from "~/presenters/IntegrationClientPresenter.server"; -import { requireUserId } from "~/services/session.server"; -import { - IntegrationClientParamSchema, - integrationClientConnectionsPath, - integrationClientPath, - integrationClientScopesPath, - organizationIntegrationsPath, -} from "~/utils/pathBuilder"; - -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const userId = await requireUserId(request); - const { organizationSlug, clientParam } = IntegrationClientParamSchema.parse(params); - - const presenter = new IntegrationClientPresenter(); - const client = await presenter.call({ - userId, - organizationSlug, - clientSlug: clientParam, - }); - - if (!client) { - throw new Response("Not found", { status: 404 }); - } - - return typedjson({ client }); -}; - -export default function Integrations() { - const { client } = useTypedLoaderData(); - const organization = useOrganization(); - - let tabs = [ - { - label: "Jobs", - to: integrationClientPath(organization, client), - }, - ]; - - if (client.authMethod.type !== "local") { - tabs.push({ - label: "Connections", - to: integrationClientConnectionsPath(organization, client), - }); - tabs.push({ - label: "Scopes", - to: integrationClientScopesPath(organization, client), - }); - } - - return ( - - - - - - -
-
- - - } - /> - - - - - - } - /> - - - -
- - -
-
-
- ); -} From 9fcae08c05afdcb34f5982d8d15cb630424d279e Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Tue, 21 Jan 2025 13:44:59 +0000 Subject: [PATCH 04/23] Delete v2 project pages --- .../route.tsx | 273 ------------- .../route.tsx | 16 - .../ConfigureEndpointSheet.tsx | 214 ---------- .../FirstEndpointSheet.tsx | 121 ------ .../route.tsx | 318 --------------- .../route.tsx | 111 ------ .../route.tsx | 87 ---- .../route.tsx | 5 - .../route.tsx | 234 ----------- .../route.tsx | 126 ------ .../route.tsx | 5 - .../route.tsx | 101 ----- .../route.tsx | 8 - .../route.tsx | 13 - .../route.tsx | 38 -- .../route.tsx | 32 -- .../route.tsx | 74 ---- .../route.tsx | 67 ---- .../route.tsx | 375 ------------------ .../route.tsx | 11 - .../route.tsx | 141 ------- .../route.tsx | 121 ------ .../route.tsx | 260 ------------ .../route.tsx | 9 - .../route.tsx | 92 ----- .../route.tsx | 106 ----- .../route.tsx | 14 - .../route.tsx | 212 ---------- .../route.tsx | 205 ---------- .../route.tsx | 14 - .../route.tsx | 14 - .../route.tsx | 92 ----- .../route.tsx | 103 ----- .../route.tsx | 53 --- .../route.tsx | 140 ------- .../route.tsx | 159 -------- .../route.tsx | 135 ------- .../route.tsx | 60 --- .../route.tsx | 221 ----------- .../route.tsx | 20 - .../route.tsx | 13 - .../route.tsx | 36 -- .../route.tsx | 34 -- .../route.tsx | 96 ----- .../route.tsx | 119 ------ .../route.tsx | 73 ---- .../route.tsx | 102 ----- .../route.tsx | 20 - .../route.tsx | 13 - .../route.tsx | 35 -- .../route.tsx | 34 -- .../route.tsx | 97 ----- .../route.tsx | 20 - .../route.tsx | 13 - .../route.tsx | 35 -- .../route.tsx | 34 -- .../route.tsx | 96 ----- .../route.tsx | 51 --- 58 files changed, 5321 deletions(-) delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam._index/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments.stream/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/ConfigureEndpointSheet.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/FirstEndpointSheet.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.events.$eventParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.events._index/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.events/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.http-endpoints.$httpEndpointParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.http-endpoints._index/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.http-endpoints/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam._index/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam.runs.$runParam.completed/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam.runs.$runParam.stream/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam.runs.$runParam.tasks.$taskParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam.runs.$runParam.trigger/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam.runs.$runParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam.settings/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam.test/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam.trigger/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.runs/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.settings/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup._index/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.astro/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.express/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.fastify/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nestjs/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nextjs/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nuxt/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.redwood/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.remix/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.sveltekit/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers._index/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers.scheduled/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers.webhooks/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.external.$triggerParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.external.$triggerParam_.runs.$runParam.completed/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.external.$triggerParam_.runs.$runParam.stream/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.external.$triggerParam_.runs.$runParam.tasks.$taskParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.external.$triggerParam_.runs.$runParam.trigger/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.external.$triggerParam_.runs.$runParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam._index/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam.delivery/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam_.runs.$runParam.completed/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam_.runs.$runParam.stream/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam_.runs.$runParam.tasks.$taskParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam_.runs.$runParam.trigger/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam_.runs.$runParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam_.runs.delivery.$runParam.completed/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam_.runs.delivery.$runParam.stream/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam_.runs.delivery.$runParam.tasks.$taskParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam_.runs.delivery.$runParam.trigger/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.triggers_.webhooks.$triggerParam_.runs.delivery.$runParam/route.tsx delete mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam/route.tsx diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam._index/route.tsx deleted file mode 100644 index fa47283bb3..0000000000 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam._index/route.tsx +++ /dev/null @@ -1,273 +0,0 @@ -import { ArrowUpIcon } from "@heroicons/react/24/solid"; -import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { typedjson, useTypedLoaderData } from "remix-typedjson"; -import { FrameworkSelector } from "~/components/frameworks/FrameworkSelector"; -import { JobsTable } from "~/components/jobs/JobsTable"; -import { PageBody, PageContainer } from "~/components/layout/AppLayout"; -import { Callout } from "~/components/primitives/Callout"; -import { Header2 } from "~/components/primitives/Headers"; -import { Help, HelpContent, HelpTrigger } from "~/components/primitives/Help"; -import { Input } from "~/components/primitives/Input"; -import { NamedIcon } from "~/components/primitives/NamedIcon"; -import { NavBar, PageTitle } from "~/components/primitives/PageHeader"; -import { Paragraph } from "~/components/primitives/Paragraph"; -import { Switch } from "~/components/primitives/Switch"; -import { TextLink } from "~/components/primitives/TextLink"; -import { useFilterJobs } from "~/hooks/useFilterJobs"; -import { useOrganization } from "~/hooks/useOrganizations"; -import { useProject } from "~/hooks/useProject"; -import { JobListPresenter } from "~/presenters/JobListPresenter.server"; -import { requireUserId } from "~/services/session.server"; -import { cn } from "~/utils/cn"; -import { ProjectParamSchema, organizationIntegrationsPath } from "~/utils/pathBuilder"; - -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const userId = await requireUserId(request); - const { organizationSlug, projectParam } = ProjectParamSchema.parse(params); - - try { - const presenter = new JobListPresenter(); - const jobs = await presenter.call({ userId, organizationSlug, projectSlug: projectParam }); - - return typedjson({ - jobs, - }); - } 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 organization = useOrganization(); - const project = useProject(); - const { jobs } = useTypedLoaderData(); - const { filterText, setFilterText, filteredItems, onlyActiveJobs, setOnlyActiveJobs } = - useFilterJobs(jobs); - const totalJobs = jobs.length; - const hasJobs = totalJobs > 0; - const activeJobCount = jobs.filter((j) => j.status === "ACTIVE").length; - - return ( - - - - - - - {(open) => ( -
-
- {hasJobs ? ( - <> - {jobs.some((j) => j.hasIntegrationsRequiringAction) && ( - - Some of your Jobs have Integrations that have not been configured. - - )} -
-
- setFilterText(e.target.value)} - autoFocus - /> - - -
-
- - {jobs.length === 1 && - jobs.every((r) => r.lastRun === undefined) && - jobs.every((i) => i.hasIntegrationsRequiringAction === false) && ( - - )} - - ) : ( - - )} -
- - - -
- )} -
-
-
- ); -} - -function RunYourJobPrompt() { - return ( -
- - - Your Job is ready to run! Click it to run it now. - -
- ); -} - -function ExampleJobs() { - return ( - <> - Video walk-through - - Watch Matt, CEO of Trigger.dev create a GitHub issue reminder in Slack using Trigger.dev. - (10 mins) - -