From 4bd1786ae5612277c017cc2bb8083965dcb63fb9 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Wed, 13 Dec 2023 12:48:26 +0000 Subject: [PATCH] Billing (#789) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial commit - started work on new billing components * Added a slider component * Added features to the pricing tiers * Small pricing tier margin tweaks * WIP on a concurrecy chart * Reworked the pricing tiers to include a segmented controller and tooltips * Renamed the charts storybook page * Made the way data is added more flexible and added some definition tool tips * Term definitions are used properly in the tiers * Callouts can now have an optional CTA on the right hand side * Alignment fix for the callouts * organize imports * Definition tooltip now its own component * renamed the storybook story * WIP new volume discount table and usage sliders * Added pricing calculator sliders * Fixed alignment of the legend * Breadcrumb now has an upgrade prompt and button * New Join our Slack button in the side menu * New progress meter in the side menu * Use the highest of 2 values to show progress * An attempt to fix the step count in the calculator slider * WIP usage progress bar * Added the 4 progress bars * More examples of the usage bar * Better way to include the percentage in the free plan progress meter * The usage bar now works with the extra runs over the free limit * Pricing calculator has better slider logic * Moved the free plan usage bar into it’s own component and added it to storybook * Usage bar chart now supports a paying customer option and optional billing limit. Also added more usage examples to storybook * Added more examples of usage to storybook * tooltip takes classname * Format numbers nicely * Added a tooltip to show the precise numbers in the chart * small improvements to the billing calculator * New onboarding choose plan page * pricing tiers better fill the size of their container * Removed unused code * Usage bars animate * Wording tweak * Callouts fit the button size better * Added new routes for the 2 new billing pages * import cleanup * Added meta info in the header for bill price, plan type and billing period * made free a noun * Added pricing calculator to the plans page * Fixed some illegal markup when using tooltips * Added container query support * Added new concurrency chart to the usage page * billing now has a green theme * Simplified the plan summary info int the header * Fixed padding alignment * Use a custom lable for the concurrent runs chart * Removed the Job runs table * Latest lockfile * Show a message if you haven’t done runs yet * Added a layoutId to the pageTabs * Show a message callout if you’ve exceeeded 10k runs on the free plan * Added a callout on the plans page if you’re over the runs limit * Fixed button inside button bug * Removed the background gradients from the app * The billing package is importing properly * Getting the curent plan for an org * Reading the current plan and usage * Hooked up the free plan bar * Render basic billing details * vol discount table has optional values * Added a new page to show new subscribers * Created a new hook for confetti on the subscribed page * Toast styling updated * The Invoice and Manage card details links are working * Meta appEnv data optional * Data for the plans page * Format the billing period duration in days * Switch to 20 icons * URL for the subscribed page now includes the org path * New pathBuilder path for the subscribed page * Plans are upgraded/downgraded successfully * Fixed badly named paths * Improved some of the display * Deal with when the user has canceled so they can re-upgrade * Subscribing from Stripe is working, and canceling * Tidied up some bits, latest billing package * The tiers are now rendering using the real data * The onboarding screen is hooked up, but not linked to yet * Onboarding price selection working * Pricing slider working * Price estimation working * Pricing calculator working on the select a plan page * Button copy change * Improved the layout of the run calculator marker * Fix for the period end when you’ve canceled * Improved the formatting in the calculator * Pricing table tooltip uses the vol discount pricing table * Remove the vertical lines from the calculator * Contact us button in Enterprise tier opens the contact us form * Added composite index to triggerdotdev_events.run_executions for event_time and organization_id * Concurrent run chart data * Improves styling, fix for React error with Enterprise contact button * Show warning box when you’ve hit the concurrency in the past 30 days * Lots of work o the usage page * Improvements to the usage page * Show 31 days of data, fix for not showing the current date… * Stripe portal links are generated when the user clicks through * Better alignment of the reference line and x-axis label * Readme update * Fixed pricing button loading buttons * Show warnings about concurrency and runs on the plans page * Removed some storybook stories * Join Slack channel shows if you’re subscribed with instructions * Added a gap between the runs charts * Improved the definitions * Added some margin to the page loading spinner * A wider, cleaner feedback panel * Modal backgrounds match the Sheet style * Fixed menu item text being clipped * Improved icons for execution time and exclusion count * Concurrency chart now renders the dates nicely * Fix for plans data on the plans page * The healthcheck doesn’t need to do a HEAD request to / * Better disabled states and fixed the disabled hover state issue * Improved the segmented controller style * loading spinner now centered inside the button * Redirect to the project page when selecting the free plan * Fix for “Runs” and added real date to upgrade warning * DeCAPITALIZED some things --------- Co-authored-by: James Ritchie --- .../app/assets/images/gradient-background.png | Bin 154551 -> 0 bytes .../app/components/DefinitionTooltip.tsx | 33 ++ apps/webapp/app/components/Feedback.tsx | 47 +- apps/webapp/app/components/PageGradient.tsx | 12 - .../billing/ConcurrentRunsChart.tsx | 114 ++++ .../app/components/billing/FreePlanUsage.tsx | 40 ++ .../components/billing/PricingCalculator.tsx | 259 +++++++++ .../app/components/billing/PricingTiers.tsx | 519 ++++++++++++++++++ .../billing/RunsVolumeDiscountTable.tsx | 64 +++ .../app/components/billing/UpgradePrompt.tsx | 36 ++ .../app/components/billing/UsageBar.tsx | 167 ++++++ .../app/components/layout/AppLayout.tsx | 38 +- .../navigation/PageNavigationIndicator.tsx | 5 +- .../app/components/navigation/SideMenu.tsx | 93 +++- .../app/components/primitives/Buttons.tsx | 6 +- .../app/components/primitives/Callout.tsx | 35 +- .../app/components/primitives/DateTime.tsx | 29 +- .../app/components/primitives/Dialog.tsx | 4 +- .../primitives/FormSegmentedControl.tsx | 72 --- .../primitives/SegmentedControl.tsx | 79 +++ .../app/components/primitives/Toast.tsx | 24 +- .../app/components/primitives/Tooltip.tsx | 34 +- .../webapp/app/components/run/RunOverview.tsx | 7 +- apps/webapp/app/components/runs/RunsTable.tsx | 4 +- .../runs/WebhookDeliveryRunsTable.tsx | 4 +- .../stories/FreePlanUsage.stories.tsx | 46 ++ .../stories/PricingCallout.stories.tsx | 39 ++ ...ories.tsx => SegmentedControl.stories.tsx} | 15 +- .../components/stories/ToastUI.stories.tsx | 6 + .../components/stories/UsageBar.stories.tsx | 76 +++ apps/webapp/app/features.server.ts | 1 + .../app/hooks/useNewCustomerSubscribed.ts | 55 ++ apps/webapp/app/hooks/useOrganizations.ts | 5 +- .../app/presenters/OrgBillingPlanPresenter.ts | 60 ++ .../presenters/OrgUsagePresenter.server.ts | 235 ++++---- apps/webapp/app/root.tsx | 4 +- .../route.tsx | 32 ++ .../route.tsx | 32 ++ .../route.tsx | 34 ++ .../route.tsx | 193 +++++++ .../route.tsx | 85 +++ .../route.tsx | 274 ++++----- .../route.tsx | 4 +- .../_app.orgs.$organizationSlug/route.tsx | 28 +- .../route.tsx | 89 +++ .../route.tsx | 96 ++++ .../webapp/app/routes/_app.orgs.new/route.tsx | 19 +- apps/webapp/app/routes/_app/route.tsx | 8 +- apps/webapp/app/routes/account/route.tsx | 2 +- .../app/routes/confirm-basic-details.tsx | 2 +- apps/webapp/app/routes/healthcheck.tsx | 17 +- apps/webapp/app/routes/invites.tsx | 2 +- .../resources.$organizationSlug.subscribe.ts | 79 +++ ...s.$organizationSlug.subscription.portal.ts | 47 ++ apps/webapp/app/routes/resources.feedback.ts | 1 + apps/webapp/app/services/billing.server.ts | 111 ++++ apps/webapp/app/utils.ts | 12 +- apps/webapp/app/utils/numberFormatter.ts | 21 + apps/webapp/app/utils/pathBuilder.ts | 20 + apps/webapp/package.json | 3 + apps/webapp/remix.config.js | 1 + apps/webapp/tailwind.config.js | 5 +- .../migration.sql | 1 + perf/README.md | 4 + pnpm-lock.yaml | 57 ++ 65 files changed, 3020 insertions(+), 526 deletions(-) delete mode 100644 apps/webapp/app/assets/images/gradient-background.png create mode 100644 apps/webapp/app/components/DefinitionTooltip.tsx delete mode 100644 apps/webapp/app/components/PageGradient.tsx create mode 100644 apps/webapp/app/components/billing/ConcurrentRunsChart.tsx create mode 100644 apps/webapp/app/components/billing/FreePlanUsage.tsx create mode 100644 apps/webapp/app/components/billing/PricingCalculator.tsx create mode 100644 apps/webapp/app/components/billing/PricingTiers.tsx create mode 100644 apps/webapp/app/components/billing/RunsVolumeDiscountTable.tsx create mode 100644 apps/webapp/app/components/billing/UpgradePrompt.tsx create mode 100644 apps/webapp/app/components/billing/UsageBar.tsx delete mode 100644 apps/webapp/app/components/primitives/FormSegmentedControl.tsx create mode 100644 apps/webapp/app/components/primitives/SegmentedControl.tsx create mode 100644 apps/webapp/app/components/stories/FreePlanUsage.stories.tsx create mode 100644 apps/webapp/app/components/stories/PricingCallout.stories.tsx rename apps/webapp/app/components/stories/{FormSegmentedControl.stories.tsx => SegmentedControl.stories.tsx} (62%) create mode 100644 apps/webapp/app/components/stories/UsageBar.stories.tsx create mode 100644 apps/webapp/app/hooks/useNewCustomerSubscribed.ts create mode 100644 apps/webapp/app/presenters/OrgBillingPlanPresenter.ts create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationId.subscription.canceled/route.tsx create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationId.subscription.complete/route.tsx create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationId.subscription.failed/route.tsx create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.billing._index/route.tsx create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug_.select-plan/route.tsx create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug_.subscribed/route.tsx create mode 100644 apps/webapp/app/routes/resources.$organizationSlug.subscribe.ts create mode 100644 apps/webapp/app/routes/resources.$organizationSlug.subscription.portal.ts create mode 100644 apps/webapp/app/services/billing.server.ts create mode 100644 apps/webapp/app/utils/numberFormatter.ts create mode 100644 packages/database/prisma/migrations/20231204163703_add_composite_index_to_triggerdotdev_events_to_speed_up_organization_queries/migration.sql diff --git a/apps/webapp/app/assets/images/gradient-background.png b/apps/webapp/app/assets/images/gradient-background.png deleted file mode 100644 index 9f40d248366b8ac180238addab974874906f47aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154551 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 z 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 diff --git a/apps/webapp/app/components/DefinitionTooltip.tsx b/apps/webapp/app/components/DefinitionTooltip.tsx new file mode 100644 index 0000000000..ad02663f95 --- /dev/null +++ b/apps/webapp/app/components/DefinitionTooltip.tsx @@ -0,0 +1,33 @@ +import { Header3 } from "./primitives/Headers"; +import { Paragraph } from "./primitives/Paragraph"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./primitives/Tooltip"; + +export function DefinitionTip({ + content, + children, + title, +}: { + content: React.ReactNode; + children: React.ReactNode; + title: React.ReactNode; +}) { + return ( + + + + + {children} + + + + {title} + {typeof content === "string" ? ( + {content} + ) : ( +
{content}
+ )} +
+
+
+ ); +} diff --git a/apps/webapp/app/components/Feedback.tsx b/apps/webapp/app/components/Feedback.tsx index 8ebf2af9d7..ea67f783a5 100644 --- a/apps/webapp/app/components/Feedback.tsx +++ b/apps/webapp/app/components/Feedback.tsx @@ -1,12 +1,15 @@ import { conform, useForm } from "@conform-to/react"; import { parse } from "@conform-to/zod"; +import { ChevronRightIcon } from "@heroicons/react/24/solid"; import { Form, useActionData, useLocation, useNavigation } from "@remix-run/react"; +import { DiscordIcon } from "@trigger.dev/companyicons"; import { ReactNode, useState } from "react"; import { FeedbackType, feedbackTypeLabel, schema } from "~/routes/resources.feedback"; import { Button } from "./primitives/Buttons"; import { Fieldset } from "./primitives/Fieldset"; import { FormButtons } from "./primitives/FormButtons"; import { FormError } from "./primitives/FormError"; +import { Header2 } from "./primitives/Headers"; import { InputGroup } from "./primitives/InputGroup"; import { Label } from "./primitives/Label"; import { Paragraph } from "./primitives/Paragraph"; @@ -20,8 +23,6 @@ import { } from "./primitives/Select"; import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "./primitives/Sheet"; import { TextArea } from "./primitives/TextArea"; -import { DiscordIcon } from "@trigger.dev/companyicons"; -import { ChevronRightIcon } from "@heroicons/react/24/solid"; type FeedbackProps = { button: ReactNode; @@ -55,19 +56,16 @@ export function Feedback({ button, defaultValue = "bug" }: FeedbackProps) { return ( {button} - - Help & feedback - + + How can we help? + - - Or use this form to ask for help or give us feedback. We read every message and will get - back to you as soon as we can. - +
+ Send us a message
- +
+ {buttonPath ? ( + + {actionText} + + ) : ( + + )} +
+
    + + Up to {plan.runs?.freeAllowance ? formatNumberCompact(plan.runs.freeAllowance) : ""}{" "} + + {pricingDefinitions.jobRuns.title} + + + + Unlimited{" "} + + jobs + + + + Unlimited{" "} + + tasks + + + + Unlimited{" "} + + events + + + Unlimited team members + 24 hour log retention + Community support + Custom integrations + Role-based access control + SSO + On-prem option +
+ + + ); +} + +export function TierPro({ + plan, + organizationSlug, + showActionText, + currentSubscription, +}: { + plan: Plan; + organizationSlug: string; + showActionText: boolean; + currentSubscription?: ActiveSubscription; +}) { + const lastSubmission = useActionData(); + const [form] = useForm({ + id: "subscribe", + // TODO: type this + lastSubmission: lastSubmission as any, + onValidate({ formData }) { + return parse(formData, { schema: SetPlanBodySchema }); + }, + }); + + const navigation = useNavigation(); + const isLoading = + (navigation.state === "submitting" || navigation.state === "loading") && + navigation.formData?.get("planCode") === plan.code; + + const currentConcurrencyTier = currentSubscription?.plan.concurrentRuns.pricing?.code; + const [concurrentBracketCode, setConcurrentBracketCode] = useState( + currentConcurrencyTier ?? plan.concurrentRuns?.pricing?.tiers[0].code + ); + + const concurrencyTiers = plan.concurrentRuns?.pricing?.tiers ?? []; + const selectedTier = concurrencyTiers.find((c) => c.code === concurrentBracketCode); + + const freeRunCount = plan.runs?.pricing?.brackets[0].upto ?? 0; + const mostExpensiveRunCost = plan.runs?.pricing?.brackets[1]?.unitCost ?? 0; + + const isCurrentPlan = currentConcurrencyTier === concurrentBracketCode; + + let actionText = "Select plan"; + + if (showActionText) { + if (isCurrentPlan) { + actionText = "Current Plan"; + } else { + const currentTierIndex = concurrencyTiers.findIndex((c) => c.code === currentConcurrencyTier); + const selectedTierIndex = concurrencyTiers.findIndex((c) => c.code === concurrentBracketCode); + actionText = currentTierIndex < selectedTierIndex ? "Upgrade" : "Downgrade"; + } + } + + return ( + +
+
+ +
+ + {pricingDefinitions.concurrentRuns.title} + +
+ + + ({ label: `Up to ${c.upto}`, value: c.code }))} + fullWidth + value={concurrentBracketCode} + onChange={(v) => setConcurrentBracketCode(v)} + /> +
+ +
+
    + + Includes {freeRunCount ? formatNumberCompact(freeRunCount) : ""}{" "} + + {pricingDefinitions.jobRuns.title} + + , then{" "} + + } + > + {"<"} ${(mostExpensiveRunCost * 1000).toFixed(2)}/1K runs + + + + Unlimited{" "} + + jobs + + + + Unlimited{" "} + + tasks + + + + Unlimited{" "} + + events + + + Unlimited team members + 7 day log retention + Dedicated Slack support + Custom integrations + Role-based access control + SSO + On-prem option +
+ + + ); +} + +export function TierEnterprise() { + return ( + +
+ + Flexible{" "} + + {pricingDefinitions.concurrentRuns.title} + + +
+ + Contact us + + } + defaultValue="enterprise" + /> +
+
    + + Flexible{" "} + + {pricingDefinitions.jobRuns.title} + + + + Unlimited{" "} + + jobs + + + + Unlimited{" "} + + tasks + + + + Unlimited{" "} + + events + + + Unlimited team members + 30 day log retention + Priority support + + Custom{" "} + + {pricingDefinitions.integrations.title} + + + Role-based access control + SSO + On-prem option +
+ + ); +} + +function TierContainer({ + children, + isHighlighted, +}: { + children: React.ReactNode; + isHighlighted?: boolean; +}) { + return ( +
+ {children} +
+ ); +} + +function Header({ + title, + cost: flatCost, + isHighlighted, +}: { + title: string; + cost?: number; + isHighlighted?: boolean; +}) { + return ( +
+

+ {title} +

+ {flatCost === 0 || flatCost ? ( +

+ ${flatCost} + /month +

+ ) : ( +

Custom

+ )} +
+ ); +} + +function TierLimit({ children }: { children: React.ReactNode }) { + return ( +
+
+
{children}
+
+ ); +} + +function FeatureItem({ checked, children }: { checked?: boolean; children: React.ReactNode }) { + return ( +
  • + {checked ? ( + + ) : ( + + )} +
    + {children} +
    +
  • + ); +} diff --git a/apps/webapp/app/components/billing/RunsVolumeDiscountTable.tsx b/apps/webapp/app/components/billing/RunsVolumeDiscountTable.tsx new file mode 100644 index 0000000000..11d719d58c --- /dev/null +++ b/apps/webapp/app/components/billing/RunsVolumeDiscountTable.tsx @@ -0,0 +1,64 @@ +import { RunPriceBracket } from "@trigger.dev/billing"; +import { Header2 } from "../primitives/Headers"; +import { Paragraph } from "../primitives/Paragraph"; +import { formatNumberCompact } from "~/utils/numberFormatter"; + +export function RunsVolumeDiscountTable({ + className, + hideHeader = false, + brackets, +}: { + className?: string; + hideHeader?: boolean; + brackets: RunPriceBracket[]; +}) { + const runsVolumeDiscountRow = + "flex justify-between whitespace-nowrap border-b gap-16 border-border last:pb-0 last:border-none py-2"; + + const bracketData = bracketInfo(brackets); + + return ( +
    + {hideHeader ? null : Runs volume discount} +
      + {bracketData.map((bracket, index) => ( +
    • + {bracket.range} + {bracket.costLabel} +
    • + ))} +
    +
    + ); +} + +function bracketInfo(brackets: RunPriceBracket[]) { + return brackets.map((bracket, index) => { + const { upto, unitCost } = bracket; + + if (index === 0) { + return { + range: `First ${formatNumberCompact(upto!)}/mo`, + costLabel: "Free", + }; + } + + const from = brackets[index - 1].upto; + const fromFormatted = formatNumberCompact(from!); + const toFormatted = upto ? formatNumberCompact(upto) : undefined; + + const costLabel = `$${(unitCost * 1000).toFixed(2)}/1,000`; + + if (!upto) { + return { + range: `${fromFormatted} +`, + costLabel, + }; + } + + return { + range: `${fromFormatted}–${toFormatted}`, + costLabel, + }; + }); +} diff --git a/apps/webapp/app/components/billing/UpgradePrompt.tsx b/apps/webapp/app/components/billing/UpgradePrompt.tsx new file mode 100644 index 0000000000..e2ab76a87b --- /dev/null +++ b/apps/webapp/app/components/billing/UpgradePrompt.tsx @@ -0,0 +1,36 @@ +import { ArrowUpCircleIcon } from "@heroicons/react/24/outline"; +import { MatchedOrganization } from "~/hooks/useOrganizations"; +import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route"; +import { formatNumberCompact } from "~/utils/numberFormatter"; +import { plansPath } from "~/utils/pathBuilder"; +import { LinkButton } from "../primitives/Buttons"; +import { Paragraph } from "../primitives/Paragraph"; + +type UpgradePromptProps = { + organization: MatchedOrganization; +}; + +export function UpgradePrompt({ organization }: UpgradePromptProps) { + const currentPlan = useCurrentPlan(); + + if (!currentPlan || !currentPlan.usage.exceededRunCount || !currentPlan.usage.runCountCap) { + return null; + } + + return ( +
    + + You have exceeded the monthly {formatNumberCompact(currentPlan.usage.runCountCap)} runs + limit + + + Upgrade + +
    + ); +} diff --git a/apps/webapp/app/components/billing/UsageBar.tsx b/apps/webapp/app/components/billing/UsageBar.tsx new file mode 100644 index 0000000000..0dfaeb4e9d --- /dev/null +++ b/apps/webapp/app/components/billing/UsageBar.tsx @@ -0,0 +1,167 @@ +import { cn } from "~/utils/cn"; +import { formatNumberCompact } from "~/utils/numberFormatter"; +import { Paragraph } from "../primitives/Paragraph"; +import { SimpleTooltip } from "../primitives/Tooltip"; +import { motion } from "framer-motion"; + +type UsageBarProps = { + numberOfCurrentRuns: number; + billingLimit?: number; + tierRunLimit?: number; + projectedRuns: number; + subscribedToPaidTier?: boolean; +}; + +export function UsageBar({ + numberOfCurrentRuns, + billingLimit, + tierRunLimit, + projectedRuns, + subscribedToPaidTier = false, +}: UsageBarProps) { + const getLargestNumber = Math.max( + numberOfCurrentRuns, + tierRunLimit ?? -Infinity, + projectedRuns, + billingLimit ?? -Infinity + ); + //creates a maximum range for the progress bar, add 10% to the largest number so the bar doesn't reach the end + const maxRange = Math.round(getLargestNumber * 1.1); + const tierRunLimitPercentage = tierRunLimit ? Math.round((tierRunLimit / maxRange) * 100) : 0; + const projectedRunsPercentage = Math.round((projectedRuns / maxRange) * 100); + const billingLimitPercentage = + billingLimit !== undefined ? Math.round((billingLimit / maxRange) * 100) : 0; + const usagePercentage = Math.round((numberOfCurrentRuns / maxRange) * 100); + + //cap the usagePercentage to the freeRunLimitPercentage + const usageCappedToLimitPercentage = Math.min(usagePercentage, tierRunLimitPercentage); + + return ( +
    +
    + {billingLimit && ( + + + + )} + {tierRunLimit && ( + + + + )} + {projectedRuns !== 0 && ( + + + + )} + + + + +
    +
    + ); +} + +const positions = { + topRow1: "bottom-0 h-9", + topRow2: "bottom-0 h-14", + bottomRow1: "top-0 h-9 items-end", + bottomRow2: "top-0 h-14 items-end", +}; + +type LegendProps = { + text: string; + value: number | string; + percentage: number; + position: keyof typeof positions; + tooltipContent: string; +}; + +function Legend({ text, value, position, percentage, tooltipContent }: LegendProps) { + const flipLegendPositionValue = 80; + const flipLegendPosition = percentage > flipLegendPositionValue ? true : false; + return ( +
    + + {text} + {value} + + } + variant="dark" + side="top" + content={tooltipContent} + className="z-50 h-fit" + /> +
    + ); +} diff --git a/apps/webapp/app/components/layout/AppLayout.tsx b/apps/webapp/app/components/layout/AppLayout.tsx index cf33364bfe..b9e72d1aaa 100644 --- a/apps/webapp/app/components/layout/AppLayout.tsx +++ b/apps/webapp/app/components/layout/AppLayout.tsx @@ -1,42 +1,8 @@ import { cn } from "~/utils/cn"; -import gradientPath from "./app-container-gradient.svg"; /** This container is used to surround the entire app, it correctly places the nav bar */ -export function AppContainer({ - children, - showBackgroundGradient, -}: { - children: React.ReactNode; - showBackgroundGradient?: boolean; -}) { - return ( - -
    {children}
    -
    - ); -} - -export function BackgroundGradient({ - children, - showBackgroundGradient, -}: { - children: React.ReactNode; - showBackgroundGradient?: boolean; -}) { - return ( -
    - {children} -
    - ); +export function AppContainer({ children }: { children: React.ReactNode }) { + return
    {children}
    ; } /** This container should be placed around the content on a page */ diff --git a/apps/webapp/app/components/navigation/PageNavigationIndicator.tsx b/apps/webapp/app/components/navigation/PageNavigationIndicator.tsx index 3fb255ac57..e067a5742a 100644 --- a/apps/webapp/app/components/navigation/PageNavigationIndicator.tsx +++ b/apps/webapp/app/components/navigation/PageNavigationIndicator.tsx @@ -1,9 +1,10 @@ import { useNavigation } from "@remix-run/react"; import { Spinner } from "../primitives/Spinner"; +import { cn } from "~/utils/cn"; -export function PageNavigationIndicator() { +export function PageNavigationIndicator({ className }: { className?: string }) { const navigation = useNavigation(); if (navigation.state === "loading") { - return ; + return ; } } diff --git a/apps/webapp/app/components/navigation/SideMenu.tsx b/apps/webapp/app/components/navigation/SideMenu.tsx index 3591f4c347..49c44c5961 100644 --- a/apps/webapp/app/components/navigation/SideMenu.tsx +++ b/apps/webapp/app/components/navigation/SideMenu.tsx @@ -1,5 +1,6 @@ import { AcademicCapIcon, + ArrowRightIcon, ArrowRightOnRectangleIcon, ChartBarIcon, EllipsisHorizontalIcon, @@ -7,11 +8,14 @@ import { import { UserGroupIcon, UserPlusIcon } from "@heroicons/react/24/solid"; import { useNavigation } from "@remix-run/react"; import { IconExclamationCircle } from "@tabler/icons-react"; +import { DiscordIcon, SlackIcon } from "@trigger.dev/companyicons"; import { AnchorHTMLAttributes, Fragment, useEffect, useRef, useState } from "react"; +import { useFeatures } from "~/hooks/useFeatures"; import { MatchedOrganization } from "~/hooks/useOrganizations"; import { usePathName } from "~/hooks/usePathName"; import { MatchedProject } from "~/hooks/useProject"; import { User } from "~/models/user.server"; +import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route"; import { cn } from "~/utils/cn"; import { accountPath, @@ -33,8 +37,12 @@ import { import { Feedback } from "../Feedback"; import { ImpersonationBanner } from "../ImpersonationBanner"; import { LogoIcon } from "../LogoIcon"; -import { UserAvatar, UserProfilePhoto } from "../UserProfilePhoto"; +import { StepContentContainer } from "../StepContentContainer"; +import { UserProfilePhoto } from "../UserProfilePhoto"; +import { FreePlanUsage } from "../billing/FreePlanUsage"; import { Button, LinkButton } from "../primitives/Buttons"; +import { ClipboardField } from "../primitives/ClipboardField"; +import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "../primitives/Dialog"; import { Icon } from "../primitives/Icon"; import { type IconNames } from "../primitives/NamedIcon"; import { Paragraph } from "../primitives/Paragraph"; @@ -46,8 +54,8 @@ import { PopoverMenuItem, PopoverSectionHeader, } from "../primitives/Popover"; +import { StepNumber } from "../primitives/StepNumber"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip"; -import { useFeatures } from "~/hooks/useFeatures"; type SideMenuUser = Pick & { isImpersonating: boolean }; type SideMenuProject = Pick< @@ -66,6 +74,7 @@ export function SideMenu({ user, project, organization, organizations }: SideMen const borderRef = useRef(null); const [showHeaderDivider, setShowHeaderDivider] = useState(false); const { isManagedCloud } = useFeatures(); + const currentPlan = useCurrentPlan(); useEffect(() => { const handleScroll = () => { @@ -138,7 +147,7 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
    + {currentPlan?.subscription?.isPaying === true ? ( + + + + + + Join our Slack +
    +
    + + + As a subscriber, you have access to a dedicated Slack channel for 1-to-1 + support with the Trigger.dev team. + +
    +
    +
    + + + + In your Slack app, create a new channel from the main menu by going to File{" "} + New Channel + + + + + + Name your channel, set its visibility and click 'Create'. + + + + + + Invite this email address to your channel:{" "} + + + + As soon as we can, we'll accept your invitation and say hello! + + +
    +
    +
    +
    + ) : ( + + )} + } /> + {currentPlan && !currentPlan.subscription?.isPaying && currentPlan.usage.runCountCap && ( + + )}
    @@ -409,8 +490,8 @@ function SideMenuItem({ isActive ? "bg-slate-850 text-bright" : "group-hover:text-bright" )} > -
    - {name} +
    + {name}
    {count !== undefined && count > 0 && } {typeof hasWarning === "string" ? ( diff --git a/apps/webapp/app/components/primitives/Buttons.tsx b/apps/webapp/app/components/primitives/Buttons.tsx index e447370148..ffc8c0dc5d 100644 --- a/apps/webapp/app/components/primitives/Buttons.tsx +++ b/apps/webapp/app/components/primitives/Buttons.tsx @@ -82,16 +82,16 @@ const variant = { "primary/large": { textColor: "text-bright group-hover:text-white transition group-disabled:text-dimmed/80 px-1", button: - "h-10 px-2 text-sm font-medium bg-indigo-600 group-hover:bg-indigo-500/90 group-disabled:opacity-50", + "h-10 px-2 text-sm font-medium bg-indigo-600 group-hover:bg-indigo-500/90 group-disabled:opacity-50 group-disabled:group-hover:bg-indigo-600", icon: "h-5", iconSpacing: "gap-x-0.5", shortcutVariant: undefined, shortcut: undefined, }, "secondary/large": { - textColor: "text-dimmed px-1", + textColor: "text-bright group-disabled:text-dimmed px-1", button: - "h-10 px-2 text-sm text-dimmed group-hover:text-bright transition font-medium bg-slate-800 group-hover:bg-slate-700/70 disabled:opacity-50", + "h-10 px-2 text-sm transition font-medium bg-slate-800 group-hover:bg-slate-700/70 group-disabled:opacity-40 group-disabled:group-hover:bg-slate-800", icon: "h-5", iconSpacing: "gap-x-0.5", shortcutVariant: undefined, diff --git a/apps/webapp/app/components/primitives/Callout.tsx b/apps/webapp/app/components/primitives/Callout.tsx index a4bd20be67..6c15367b03 100644 --- a/apps/webapp/app/components/primitives/Callout.tsx +++ b/apps/webapp/app/components/primitives/Callout.tsx @@ -7,6 +7,7 @@ import { import { ArrowTopRightOnSquareIcon, BookOpenIcon, + ChartBarIcon, CheckCircleIcon, ChevronRightIcon, } from "@heroicons/react/24/solid"; @@ -58,6 +59,12 @@ export const variantClasses = { textColor: "text-blue-300", linkClassName: "transition hover:bg-blue-400/40", }, + pricing: { + className: "border-indigo-400/20 bg-indigo-800/30", + icon: , + textColor: "text-indigo-200", + linkClassName: "transition hover:bg-indigo-400/40", + }, } as const; export type CalloutVariant = keyof typeof variantClasses; @@ -66,12 +73,14 @@ export function Callout({ children, className, icon, + cta, variant, to, }: { children?: React.ReactNode; className?: string; icon?: React.ReactNode; + cta?: React.ReactNode; variant: CalloutVariant; to?: string; }) { @@ -84,13 +93,13 @@ export function Callout({ href={to} target="_blank" className={cn( - `flex w-full items-start justify-between gap-2.5 rounded-md border py-2 pl-2 pr-3 shadow-md backdrop-blur-sm`, + `flex w-full items-center justify-between gap-2.5 rounded-md border py-2 pl-2 pr-3 shadow-md backdrop-blur-sm`, variantDefinition.className, variantDefinition.linkClassName, className )} > -
    +
    {icon ? icon : variantDefinition.icon} {typeof children === "string" ? ( @@ -135,20 +144,24 @@ export function Callout({ return (
    - {icon ? icon : variantDefinition.icon} +
    + {icon ? icon : variantDefinition.icon} - {typeof children === "string" ? ( - - {children} - - ) : ( - children - )} + {typeof children === "string" ? ( + + {children} + + ) : ( + children + )} +
    + {cta && cta}
    ); } diff --git a/apps/webapp/app/components/primitives/DateTime.tsx b/apps/webapp/app/components/primitives/DateTime.tsx index a9d618b862..766dd83cf6 100644 --- a/apps/webapp/app/components/primitives/DateTime.tsx +++ b/apps/webapp/app/components/primitives/DateTime.tsx @@ -5,14 +5,26 @@ type DateTimeProps = { date: Date | string; timeZone?: string; includeSeconds?: boolean; + includeTime?: boolean; }; -export const DateTime = ({ date, timeZone = "UTC", includeSeconds = true }: DateTimeProps) => { +export const DateTime = ({ + date, + timeZone = "UTC", + includeSeconds = true, + includeTime = true, +}: DateTimeProps) => { const locales = useLocales(); const realDate = typeof date === "string" ? new Date(date) : date; - const initialFormattedDateTime = formatDateTime(realDate, timeZone, locales, includeSeconds); + const initialFormattedDateTime = formatDateTime( + realDate, + timeZone, + locales, + includeSeconds, + includeTime + ); const [formattedDateTime, setFormattedDateTime] = useState(initialFormattedDateTime); @@ -20,26 +32,27 @@ export const DateTime = ({ date, timeZone = "UTC", includeSeconds = true }: Date const resolvedOptions = Intl.DateTimeFormat().resolvedOptions(); setFormattedDateTime( - formatDateTime(realDate, resolvedOptions.timeZone, locales, includeSeconds) + formatDateTime(realDate, resolvedOptions.timeZone, locales, includeSeconds, includeTime) ); }, [locales, includeSeconds, realDate]); return {formattedDateTime.replace(/\s/g, String.fromCharCode(32))}; }; -function formatDateTime( +export function formatDateTime( date: Date, timeZone: string, locales: string[], - includeSeconds: boolean + includeSeconds: boolean, + includeTime: boolean ): string { return new Intl.DateTimeFormat(locales, { year: "numeric", month: "short", day: "numeric", - hour: "numeric", - minute: "numeric", - second: includeSeconds ? "numeric" : undefined, + hour: includeTime ? "numeric" : undefined, + minute: includeTime ? "numeric" : undefined, + second: includeTime && includeSeconds ? "numeric" : undefined, timeZone, }).format(date); } diff --git a/apps/webapp/app/components/primitives/Dialog.tsx b/apps/webapp/app/components/primitives/Dialog.tsx index e84c648634..3a382112f7 100644 --- a/apps/webapp/app/components/primitives/Dialog.tsx +++ b/apps/webapp/app/components/primitives/Dialog.tsx @@ -26,7 +26,7 @@ const DialogOverlay = React.forwardRef< void; -}; - -export default function FormSegmentedControl({ - name, - defaultValue, - options, - onChange, -}: FormSegmentedControlProps) { - return ( -
    - { - if (onChange) { - onChange(c); - } - }} - > -
    - {options.map((option) => ( - - cn( - "relative flex cursor-pointer rounded-[2px] px-3 py-[0.13rem] shadow-md focus:outline-none", - active - ? "focus-visible:ring focus-visible:ring-indigo-500 focus-visible:ring-opacity-60" - : "", - checked - ? "bg-slate-700 text-bright" - : "bg-transparent transition hover:bg-slate-750" - ) - } - > - {({ checked }) => ( - <> -
    -
    -
    - - {option.label} - -
    -
    -
    - - )} -
    - ))} -
    -
    -
    - ); -} diff --git a/apps/webapp/app/components/primitives/SegmentedControl.tsx b/apps/webapp/app/components/primitives/SegmentedControl.tsx new file mode 100644 index 0000000000..b01d9eb9f7 --- /dev/null +++ b/apps/webapp/app/components/primitives/SegmentedControl.tsx @@ -0,0 +1,79 @@ +import { RadioGroup } from "@headlessui/react"; +import { motion } from "framer-motion"; +import { cn } from "~/utils/cn"; + +type Options = { + label: string; + value: string; +}; + +type SegmentedControlProps = { + name: string; + value?: string; + defaultValue?: string; + options: Options[]; + fullWidth?: boolean; + onChange?: (value: string) => void; +}; + +export default function SegmentedControl({ + name, + value, + defaultValue, + options, + fullWidth, + onChange, +}: SegmentedControlProps) { + return ( +
    + { + if (onChange) { + onChange(c); + } + }} + className="w-full" + > +
    + {options.map((option) => ( + + cn( + "relative flex h-full grow cursor-pointer text-center font-normal focus:outline-none", + active + ? "ring-offset-2 focus-visible:ring focus-visible:ring-indigo-500 focus-visible:ring-opacity-60" + : "", + checked + ? "text-bright" + : "rounded-[2px] text-dimmed transition hover:bg-slate-750/50 hover:text-bright" + ) + } + > + {({ checked }) => ( + <> +
    +
    + {option.label} +
    + {checked && ( + + )} +
    + + )} +
    + ))} +
    +
    +
    + ); +} diff --git a/apps/webapp/app/components/primitives/Toast.tsx b/apps/webapp/app/components/primitives/Toast.tsx index 8a16cc9847..8bd6954a12 100644 --- a/apps/webapp/app/components/primitives/Toast.tsx +++ b/apps/webapp/app/components/primitives/Toast.tsx @@ -5,6 +5,7 @@ import { Toaster, toast } from "sonner"; import { useTypedLoaderData } from "remix-typedjson"; import { loader } from "~/root"; import { useEffect } from "react"; +import { Paragraph } from "./Paragraph"; const defaultToastDuration = 5000; const permanentToastDuration = 60 * 60 * 24 * 1000; @@ -38,26 +39,23 @@ export function ToastUI({ }) { return (
    -
    +
    {variant === "success" ? ( - + ) : ( - + )} - {message} -
    diff --git a/apps/webapp/app/components/primitives/Tooltip.tsx b/apps/webapp/app/components/primitives/Tooltip.tsx index ca99884d52..f855a0fbba 100644 --- a/apps/webapp/app/components/primitives/Tooltip.tsx +++ b/apps/webapp/app/components/primitives/Tooltip.tsx @@ -1,9 +1,15 @@ -"use client"; - import * as React from "react"; import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { cn } from "~/utils/cn"; +const variantClasses = { + basic: + "bg-popover border border-slate-800 rounded-md px-3 py-1.5 text-sm text-bright shadow-md fade-in-50", + dark: "bg-background border border-border rounded px-3 py-2 text-sm text-bright shadow-md fade-in-50", +}; + +type Variant = keyof typeof variantClasses; + const TooltipProvider = TooltipPrimitive.Provider; const TooltipArrow = React.forwardRef< @@ -22,15 +28,20 @@ Tooltip.displayName = TooltipPrimitive.Root.displayName; const TooltipTrigger = TooltipPrimitive.Trigger; +type TooltipContentProps = { + variant?: Variant; +} & React.ComponentPropsWithoutRef; + const TooltipContent = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, sideOffset = 4, ...props }, ref) => ( + TooltipContentProps +>(({ className, sideOffset = 4, variant = "basic", ...props }, ref) => ( ["side"]; hidden?: boolean; + variant?: Variant; + className?: string; }) { return ( - {button} - diff --git a/apps/webapp/app/components/run/RunOverview.tsx b/apps/webapp/app/components/run/RunOverview.tsx index 385219ed4a..cdebcb5833 100644 --- a/apps/webapp/app/components/run/RunOverview.tsx +++ b/apps/webapp/app/components/run/RunOverview.tsx @@ -27,6 +27,7 @@ import { Button } from "../primitives/Buttons"; import { Callout } from "../primitives/Callout"; import { DateTime } from "../primitives/DateTime"; import { Header2 } from "../primitives/Headers"; +import { Icon } from "../primitives/Icon"; import { NamedIcon } from "../primitives/NamedIcon"; import { PageButtons, @@ -143,18 +144,18 @@ export function RunOverview({ run, trigger, showRerun, paths }: RunOverviewProps value={formatDuration(run.startedAt, run.completedAt, { style: "short" })} /> } label={"Execution Time"} value={formatDurationMilliseconds(run.executionDuration, { style: "short" })} /> } label={"Execution Count"} value={<>{run.executionCount}} /> - + RUN ID: {run.id} diff --git a/apps/webapp/app/components/runs/RunsTable.tsx b/apps/webapp/app/components/runs/RunsTable.tsx index 0cb184c89d..fa0f1e2053 100644 --- a/apps/webapp/app/components/runs/RunsTable.tsx +++ b/apps/webapp/app/components/runs/RunsTable.tsx @@ -73,11 +73,11 @@ export function RunsTable({ {total === 0 && !hasFilters ? ( - + ) : runs.length === 0 ? ( - + ) : ( runs.map((run) => { diff --git a/apps/webapp/app/components/runs/WebhookDeliveryRunsTable.tsx b/apps/webapp/app/components/runs/WebhookDeliveryRunsTable.tsx index 46160052c3..de72c81a0c 100644 --- a/apps/webapp/app/components/runs/WebhookDeliveryRunsTable.tsx +++ b/apps/webapp/app/components/runs/WebhookDeliveryRunsTable.tsx @@ -61,11 +61,11 @@ export function WebhookDeliveryRunsTable({ {total === 0 && !hasFilters ? ( - + ) : runs.length === 0 ? ( - + ) : ( runs.map((run) => { diff --git a/apps/webapp/app/components/stories/FreePlanUsage.stories.tsx b/apps/webapp/app/components/stories/FreePlanUsage.stories.tsx new file mode 100644 index 0000000000..e62477ee36 --- /dev/null +++ b/apps/webapp/app/components/stories/FreePlanUsage.stories.tsx @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { FreePlanUsage } from "../billing/FreePlanUsage"; +import { organizationBillingPath } from "~/utils/pathBuilder"; +import { MatchedOrganization } from "~/hooks/useOrganizations"; + +const meta: Meta = { + title: "Billing/FreePlanUsage", + component: FreePlanUsageBar, +}; + +export default meta; + +type Story = StoryObj; + +const mockOrganization: MatchedOrganization = { + id: "mockID", + title: "mockTitle", + slug: "mockSlug", + projects: [ + { id: "mockId1", slug: "mockSlug1", name: "mockName1", jobCount: 1 }, + { id: "mockId2", slug: "mockSlug2", name: "mockName2", jobCount: 2 }, + ], + hasUnconfiguredIntegrations: false, + memberCount: 1, +}; + +export const ProgressBar: Story = { + args: { + organization: mockOrganization, + }, + render: (args) => , +}; + +type FreePlanUsageBarProps = { + organization: MatchedOrganization; +}; + +function FreePlanUsageBar({ organization }: FreePlanUsageBarProps) { + return ( +
    +
    + +
    +
    + ); +} diff --git a/apps/webapp/app/components/stories/PricingCallout.stories.tsx b/apps/webapp/app/components/stories/PricingCallout.stories.tsx new file mode 100644 index 0000000000..ddcc038f3b --- /dev/null +++ b/apps/webapp/app/components/stories/PricingCallout.stories.tsx @@ -0,0 +1,39 @@ +import { ArrowUpCircleIcon } from "@heroicons/react/24/outline"; +import type { Meta, StoryObj } from "@storybook/react"; +import { LinkButton } from "../primitives/Buttons"; +import { Callout } from "../primitives/Callout"; + +const meta: Meta = { + title: "Billing/PricingCallouts", + component: PricingCallouts, +}; + +export default meta; + +type Story = StoryObj; + +export const Callouts: Story = { + render: (args) => , +}; + +function PricingCallouts() { + return ( +
    + + Upgrade + + } + > + Some of your runs are being queued because your run concurrency is limited to 50. + +
    + ); +} diff --git a/apps/webapp/app/components/stories/FormSegmentedControl.stories.tsx b/apps/webapp/app/components/stories/SegmentedControl.stories.tsx similarity index 62% rename from apps/webapp/app/components/stories/FormSegmentedControl.stories.tsx rename to apps/webapp/app/components/stories/SegmentedControl.stories.tsx index 1357a73522..e4acc2577e 100644 --- a/apps/webapp/app/components/stories/FormSegmentedControl.stories.tsx +++ b/apps/webapp/app/components/stories/SegmentedControl.stories.tsx @@ -1,19 +1,20 @@ import type { Meta, StoryObj } from "@storybook/react"; import { withDesign } from "storybook-addon-designs"; -import FormSegmentedControl from "../primitives/FormSegmentedControl"; import { MainCenteredContainer } from "../layout/AppLayout"; +import SegmentedControl from "../primitives/SegmentedControl"; -const meta: Meta = { - title: "Primitives/FormSegmentedControl", +const meta: Meta = { + title: "Primitives/SegmentedControl", decorators: [withDesign], + component: StyledSegmentedControl, }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Basic: Story = { - render: () => , + render: () => , }; Basic.parameters = { @@ -28,10 +29,10 @@ const options = [ { label: "Label 2", value: "Users" }, ]; -function SegmentedControl() { +function StyledSegmentedControl() { return ( - + ); } diff --git a/apps/webapp/app/components/stories/ToastUI.stories.tsx b/apps/webapp/app/components/stories/ToastUI.stories.tsx index c4a7164d80..7dc5cba246 100644 --- a/apps/webapp/app/components/stories/ToastUI.stories.tsx +++ b/apps/webapp/app/components/stories/ToastUI.stories.tsx @@ -20,6 +20,11 @@ function Collection() {
    +
    +
    ); diff --git a/apps/webapp/app/components/stories/UsageBar.stories.tsx b/apps/webapp/app/components/stories/UsageBar.stories.tsx new file mode 100644 index 0000000000..c8818ed3d7 --- /dev/null +++ b/apps/webapp/app/components/stories/UsageBar.stories.tsx @@ -0,0 +1,76 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { UsageBar } from "../billing/UsageBar"; +import { Paragraph } from "../primitives/Paragraph"; + +const meta: Meta = { + title: "Billing/UsageBar", + component: UsageProgressBar, +}; + +export default meta; + +type Story = StoryObj; + +export const JobsUsageBar: Story = { + render: () => , +}; + +function UsageProgressBar() { + return ( +
    + + + + + + + + + + + + + + + + + + + + + +
    + ); +} + +function UsageBarWrapper({ title, children }: { title: string; children: React.ReactNode }) { + return ( +
    + {title} + {children} +
    + ); +} diff --git a/apps/webapp/app/features.server.ts b/apps/webapp/app/features.server.ts index f027510954..52b683e399 100644 --- a/apps/webapp/app/features.server.ts +++ b/apps/webapp/app/features.server.ts @@ -12,6 +12,7 @@ export function featuresForRequest(request: Request): TriggerFeatures { const isManagedCloud = url.host === "cloud.trigger.dev" || url.host === "test-cloud.trigger.dev" || + url.host === "internal.trigger.dev" || process.env.CLOUD_ENV === "development"; return { diff --git a/apps/webapp/app/hooks/useNewCustomerSubscribed.ts b/apps/webapp/app/hooks/useNewCustomerSubscribed.ts new file mode 100644 index 0000000000..2c81d18bf1 --- /dev/null +++ b/apps/webapp/app/hooks/useNewCustomerSubscribed.ts @@ -0,0 +1,55 @@ +import { useEffect } from "react"; + +export function useNewCustomerSubscribed() { + useEffect(() => { + if ("confetti" in window && typeof window.confetti !== "undefined") { + const duration = 3.5 * 1000; + const animationEnd = Date.now() + duration; + const defaults = { + startVelocity: 30, + spread: 360, + ticks: 60, + zIndex: 0, + colors: [ + "#E7FF52", + "#41FF54", + "rgb(245 158 11)", + "rgb(22 163 74)", + "rgb(37 99 235)", + "rgb(67 56 202)", + "rgb(219 39 119)", + "rgb(225 29 72)", + "rgb(217 70 239)", + ], + }; + function randomInRange(min: number, max: number): number { + return Math.random() * (max - min) + min; + } + // @ts-ignore + const interval = setInterval(function () { + const timeLeft = animationEnd - Date.now(); + + if (timeLeft <= 0) { + return clearInterval(interval); + } + + const particleCount = 60 * (timeLeft / duration); + // since particles fall down, start a bit higher than random + // @ts-ignore + window.confetti( + Object.assign({}, defaults, { + particleCount, + origin: { x: randomInRange(0.1, 0.4), y: Math.random() - 0.2 }, + }) + ); + // @ts-ignore + window.confetti( + Object.assign({}, defaults, { + particleCount, + origin: { x: randomInRange(0.6, 0.9), y: Math.random() - 0.2 }, + }) + ); + }, 250); + } + }, []); +} diff --git a/apps/webapp/app/hooks/useOrganizations.ts b/apps/webapp/app/hooks/useOrganizations.ts index ace3e5da59..4560efc0d7 100644 --- a/apps/webapp/app/hooks/useOrganizations.ts +++ b/apps/webapp/app/hooks/useOrganizations.ts @@ -1,9 +1,8 @@ -import { UseDataFunctionReturn, useTypedRouteLoaderData } from "remix-typedjson"; +import { UIMatch } from "@remix-run/react"; +import { UseDataFunctionReturn } from "remix-typedjson"; import invariant from "tiny-invariant"; import type { loader as orgLoader } from "~/routes/_app.orgs.$organizationSlug/route"; -import { hydrateObject, useMatchesData } from "~/utils"; import { useChanged } from "./useChanged"; -import { UIMatch } from "@remix-run/react"; import { useTypedMatchesData } from "./useTypedMatchData"; export type MatchedOrganization = UseDataFunctionReturn["organizations"][number]; diff --git a/apps/webapp/app/presenters/OrgBillingPlanPresenter.ts b/apps/webapp/app/presenters/OrgBillingPlanPresenter.ts new file mode 100644 index 0000000000..018b9aa922 --- /dev/null +++ b/apps/webapp/app/presenters/OrgBillingPlanPresenter.ts @@ -0,0 +1,60 @@ +import { PrismaClient, prisma } from "~/db.server"; +import { logger } from "~/services/logger.server"; +import { BillingService } from "../services/billing.server"; + +export class OrgBillingPlanPresenter { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call({ slug, isManagedCloud }: { slug: string; isManagedCloud: boolean }) { + const billingPresenter = new BillingService(isManagedCloud); + const plans = await billingPresenter.getPlans(); + + if (plans === undefined) { + return; + } + + const organization = await this.#prismaClient.organization.findFirst({ + where: { + slug, + }, + }); + + if (!organization) { + return; + } + + const maxConcurrency = await this.#prismaClient.$queryRaw< + { organization_id: string; max_concurrent_runs: BigInt }[] + >`WITH events AS ( + SELECT + re.event_time, + re.organization_id, + re.event_type, + SUM(re.event_type) OVER (PARTITION BY re.organization_id ORDER BY re.event_time) AS running_total + FROM + triggerdotdev_events.run_executions re + WHERE + re.organization_id = ${organization.id} + AND re.event_time >= DATE_TRUNC('month', + CURRENT_DATE) + ) + SELECT + organization_id, MAX(running_total) AS max_concurrent_runs + FROM + events + GROUP BY + organization_id;`; + + return { + plans, + maxConcurrency: + maxConcurrency.at(0) !== undefined + ? Number(maxConcurrency[0].max_concurrent_runs) + : undefined, + }; + } +} diff --git a/apps/webapp/app/presenters/OrgUsagePresenter.server.ts b/apps/webapp/app/presenters/OrgUsagePresenter.server.ts index c43b7fcf80..6384cd8717 100644 --- a/apps/webapp/app/presenters/OrgUsagePresenter.server.ts +++ b/apps/webapp/app/presenters/OrgUsagePresenter.server.ts @@ -1,4 +1,8 @@ +import { estimate } from "@trigger.dev/billing"; +import { formatDateTime } from "~/components/primitives/DateTime"; import { PrismaClient, prisma } from "~/db.server"; +import { featuresForRequest } from "~/features.server"; +import { BillingService } from "~/services/billing.server"; import { logger } from "~/services/logger.server"; export class OrgUsagePresenter { @@ -8,7 +12,7 @@ export class OrgUsagePresenter { this.#prismaClient = prismaClient; } - public async call({ userId, slug }: { userId: string; slug: string }) { + public async call({ userId, slug, request }: { userId: string; slug: string; request: Request }) { const organization = await this.#prismaClient.organization.findFirst({ where: { slug, @@ -24,9 +28,6 @@ export class OrgUsagePresenter { return; } - const startOfMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1); - const startOfLastMonth = new Date(new Date().getFullYear(), new Date().getMonth() - 1, 1); // this works for January as well - // Get count of runs since the start of the current month const runsCount = await this.#prismaClient.jobRun.count({ where: { @@ -38,18 +39,6 @@ export class OrgUsagePresenter { }, }); - // Get the count of runs for last month - const runsCountLastMonth = await this.#prismaClient.jobRun.count({ - where: { - organizationId: organization.id, - createdAt: { - gte: startOfLastMonth, - lt: startOfMonth, - }, - internal: false, - }, - }); - // Get the count of the runs for the last 6 months, by month. So for example we want the data shape to be: // [ // { month: "2021-01", count: 10 }, @@ -61,100 +50,121 @@ export class OrgUsagePresenter { // ] // This will be used to generate the chart on the usage page // Use prisma queryRaw for this since prisma doesn't support grouping by month - const chartDataRaw = await this.#prismaClient.$queryRaw< + const monthlyRunsDataRaw = await this.#prismaClient.$queryRaw< { month: string; count: number; }[] >`SELECT TO_CHAR("createdAt", 'YYYY-MM') as month, COUNT(*) as count FROM "JobRun" WHERE "organizationId" = ${organization.id} AND "createdAt" >= NOW() - INTERVAL '6 months' AND "internal" = FALSE GROUP BY month ORDER BY month ASC`; - const chartData = chartDataRaw.map((obj) => ({ + const hasMonthlyRunData = monthlyRunsDataRaw.length > 0; + const monthlyRunsData = monthlyRunsDataRaw.map((obj) => ({ name: obj.month, total: Number(obj.count), // Convert BigInt to Number })); - const totalJobs = await this.#prismaClient.job.count({ - where: { - organizationId: organization.id, - internal: false, - }, - }); - - const totalJobsLastMonth = await this.#prismaClient.job.count({ - where: { - organizationId: organization.id, - createdAt: { - lt: startOfMonth, - }, - deletedAt: null, - internal: false, - }, - }); - - const totalIntegrations = await this.#prismaClient.integration.count({ - where: { - organizationId: organization.id, - }, - }); - - const totalIntegrationsLastMonth = await this.#prismaClient.integration.count({ - where: { - organizationId: organization.id, - createdAt: { - lt: startOfMonth, - }, - }, - }); - - const totalMembers = await this.#prismaClient.orgMember.count({ - where: { - organizationId: organization.id, - }, - }); + const monthlyRunsDataDisplay = fillInMissingRunMonthlyData(monthlyRunsData, 6); + + // Max concurrency each day over past 30 days + const concurrencyChartRawData = await this.#prismaClient.$queryRaw< + { day: Date; max_concurrent_runs: BigInt }[] + >` + WITH time_boundaries AS ( + SELECT generate_series( + NOW() - interval '30 days', + NOW(), + interval '1 day' + ) AS day_start + ), + events AS ( + SELECT + day_start, + event_time, + event_type, + SUM(event_type) OVER (ORDER BY event_time) AS running_total + FROM + time_boundaries + JOIN + triggerdotdev_events.run_executions + ON + event_time >= day_start AND event_time < day_start + interval '1 day' + WHERE triggerdotdev_events.run_executions.organization_id = ${organization.id} + ), + max_concurrent_per_day AS ( + SELECT + date_trunc('day', event_time) AS day, + MAX(running_total) AS max_concurrent_runs + FROM + events + GROUP BY day + ) + SELECT + day, + max_concurrent_runs + FROM + max_concurrent_per_day + ORDER BY + day;`; + + const ThirtyDaysAgo = new Date(); + ThirtyDaysAgo.setDate(ThirtyDaysAgo.getDate() - 30); + ThirtyDaysAgo.setHours(0, 0, 0, 0); + + const hasConcurrencyData = concurrencyChartRawData.length > 0; + const concurrencyChartRawDataFilledIn = fillInMissingConcurrencyDays( + ThirtyDaysAgo, + 31, + concurrencyChartRawData + ); + + const endOfMonth = new Date(new Date().getFullYear(), new Date().getMonth() + 1, 1); + endOfMonth.setDate(endOfMonth.getDate() - 1); + const projectedRunsCount = Math.round( + runsCount / (new Date().getDate() / endOfMonth.getDate()) + ); + + const { isManagedCloud } = featuresForRequest(request); + const billingPresenter = new BillingService(isManagedCloud); + const plans = await billingPresenter.getPlans(); + + let runCostEstimation: number | undefined = undefined; + let projectedRunCostEstimation: number | undefined = undefined; + + if (plans) { + const estimationResult = estimate({ + usage: { currentRunCount: runsCount }, + plans: [plans.free, plans.paid], + }); + runCostEstimation = estimationResult?.cost.runsCost; + + const projectedEstimationResult = estimate({ + usage: { currentRunCount: projectedRunsCount }, + plans: [plans.free, plans.paid], + }); + projectedRunCostEstimation = projectedEstimationResult?.cost.runsCost; + } - const jobs = await this.#prismaClient.job.findMany({ - where: { - organizationId: organization.id, - deletedAt: null, - internal: false, - }, - select: { - id: true, - slug: true, - _count: { - select: { - runs: { - where: { - createdAt: { - gte: startOfMonth, - }, - }, - }, - }, - }, - project: { - select: { - id: true, - name: true, - slug: true, - }, - }, - }, - }); + const periodStart = new Date(); + periodStart.setDate(1); + periodStart.setHours(0, 0, 0, 0); - const chartDataDisplay = fillInMissingMonthlyData(chartData, 6); + const periodEnd = new Date(); + periodEnd.setDate(1); + periodEnd.setMonth(periodEnd.getMonth() + 1); + periodEnd.setHours(0, 0, 0, 0); return { id: organization.id, runsCount, - runsCountLastMonth, - chartData: chartDataDisplay, - totalJobs, - totalJobsLastMonth, - totalIntegrations, - totalIntegrationsLastMonth, - totalMembers, - jobs, + projectedRunsCount, + monthlyRunsData: monthlyRunsDataDisplay, + hasMonthlyRunData, + concurrencyData: concurrencyChartRawDataFilledIn, + hasConcurrencyData, + runCostEstimation, + projectedRunCostEstimation, + periodStart, + periodEnd, }; } } @@ -163,7 +173,7 @@ export class OrgUsagePresenter { // So for example, if data is [{ name: "2021-01", total: 10 }, { name: "2021-03", total: 30 }] and the totalNumberOfMonths is 6 // And the current month is "2021-04", then this function will return: // [{ name: "2020-11", total: 0 }, { name: "2020-12", total: 0 }, { name: "2021-01", total: 10 }, { name: "2021-02", total: 0 }, { name: "2021-03", total: 30 }, { name: "2021-04", total: 0 }] -function fillInMissingMonthlyData( +function fillInMissingRunMonthlyData( data: Array<{ name: string; total: number }>, totalNumberOfMonths: number ): Array<{ name: string; total: number }> { @@ -187,6 +197,33 @@ function fillInMissingMonthlyData( return completeData; } +function fillInMissingConcurrencyDays( + startDate: Date, + days: number, + data: Array<{ day: Date; max_concurrent_runs: BigInt }> +) { + const outputData: Array<{ date: Date; maxConcurrentRuns: number }> = []; + for (let i = 0; i < days; i++) { + const date = new Date(startDate); + date.setDate(date.getDate() + i); + + const foundData = data.find((d) => d.day.toISOString() === date.toISOString()); + if (!foundData) { + outputData.push({ + date, + maxConcurrentRuns: 0, + }); + } else { + outputData.push({ + date, + maxConcurrentRuns: Number(foundData.max_concurrent_runs), + }); + } + } + + return outputData; +} + // Start month will be like 2023-03 and endMonth will be like 2023-10 // The result should be an array of months between these two months, including the start and end month // So for example, if startMonth is 2023-03 and endMonth is 2023-10, the result should be: @@ -212,11 +249,3 @@ function getMonthsBetween(startMonth: string, endMonth: string): string[] { return result; } - -function getLastSecondOfMonth(endMonth: string) { - const [year, month] = endMonth.split("-").map(Number); - const nextMonthFirstDay = new Date(year, month, 1); - nextMonthFirstDay.setDate(0); - nextMonthFirstDay.setHours(23, 59, 59); - return nextMonthFirstDay; -} diff --git a/apps/webapp/app/root.tsx b/apps/webapp/app/root.tsx index 91eb125d04..e709761f1f 100644 --- a/apps/webapp/app/root.tsx +++ b/apps/webapp/app/root.tsx @@ -25,7 +25,7 @@ export const links: LinksFunction = () => { export const meta: TypedMetaFunction = (args) => { return metaV1(args, { - title: `Trigger.dev${appEnvTitleTag(args.data.appEnv)}`, + title: `Trigger.dev${appEnvTitleTag(args.data?.appEnv)}`, charset: "utf-8", viewport: "width=1024, initial-scale=1", }); @@ -71,7 +71,7 @@ export function ErrorBoundary() { - + 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 new file mode 100644 index 0000000000..a2f85995aa --- /dev/null +++ b/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.canceled/route.tsx @@ -0,0 +1,32 @@ +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 new file mode 100644 index 0000000000..0282294290 --- /dev/null +++ b/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.complete/route.tsx @@ -0,0 +1,32 @@ +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 new file mode 100644 index 0000000000..efe9ddb578 --- /dev/null +++ b/apps/webapp/app/routes/_app.orgs.$organizationId.subscription.failed/route.tsx @@ -0,0 +1,34 @@ +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); +}; 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 new file mode 100644 index 0000000000..f36a496024 --- /dev/null +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing._index/route.tsx @@ -0,0 +1,193 @@ +import { ArrowRightIcon } from "@heroicons/react/20/solid"; +import { ArrowUpCircleIcon } from "@heroicons/react/24/outline"; +import { LoaderFunctionArgs } from "@remix-run/server-runtime"; +import { Bar, BarChart, ResponsiveContainer, Tooltip, TooltipProps, XAxis, YAxis } from "recharts"; +import { typedjson, useTypedLoaderData } from "remix-typedjson"; +import { ConcurrentRunsChart } from "~/components/billing/ConcurrentRunsChart"; +import { UsageBar } from "~/components/billing/UsageBar"; +import { LinkButton } from "~/components/primitives/Buttons"; +import { Callout } from "~/components/primitives/Callout"; +import { Header2, Header3 } from "~/components/primitives/Headers"; +import { Paragraph } from "~/components/primitives/Paragraph"; +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"; +import { DateTime, formatDateTime } from "~/components/primitives/DateTime"; + +export async function loader({ params, request }: LoaderFunctionArgs) { + const userId = await requireUserId(request); + const { organizationSlug } = OrganizationParamsSchema.parse(params); + + const presenter = new OrgUsagePresenter(); + + const data = await presenter.call({ userId, slug: organizationSlug, request }); + + if (!data) { + throw new Response(null, { status: 404 }); + } + + return typedjson(data); +} + +const CustomTooltip = ({ active, payload, label }: TooltipProps) => { + if (active && payload) { + return ( +
    +

    {label}:

    +

    {payload[0].value}

    +
    + ); + } + + return null; +}; + +export default function Page() { + const organization = useOrganization(); + const loaderData = useTypedLoaderData(); + const currentPlan = useCurrentPlan(); + + const hitConcurrencyLimit = currentPlan?.subscription?.limits.concurrentRuns + ? loaderData.concurrencyData.some( + (c) => c.maxConcurrentRuns >= (currentPlan.subscription?.limits.concurrentRuns ?? Infinity) + ) + : false; + + const hitsRunLimit = currentPlan?.usage?.runCountCap + ? currentPlan.usage.currentRunCount > currentPlan.usage.runCountCap + : 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{" "} + . + + + )} +
    +
    + {loaderData.runCostEstimation !== undefined && + loaderData.projectedRunCostEstimation !== undefined && ( +
    +
    + Month-to-date +

    + {formatCurrency(loaderData.runCostEstimation, false)} +

    +
    + +
    + Projected +

    + {formatCurrency(loaderData.projectedRunCostEstimation, false)} +

    +
    +
    + )} + +
    +
    + Monthly runs + {!loaderData.hasMonthlyRunData && ( + + No runs to show + + )} + + + + `${value}`} + /> + } + /> + + + +
    +
    +
    +
    +
    + ); +} 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 new file mode 100644 index 0000000000..613cca3ede --- /dev/null +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx @@ -0,0 +1,85 @@ +import { useForm } from "@conform-to/react"; +import { parse } from "@conform-to/zod"; +import { useActionData } from "@remix-run/react"; +import { LoaderFunctionArgs } from "@remix-run/server-runtime"; +import { SetPlanBodySchema } from "@trigger.dev/billing"; +import { redirect, typedjson, useTypedLoaderData } from "remix-typedjson"; +import { PricingCalculator } from "~/components/billing/PricingCalculator"; +import { PricingTiers, TierEnterprise, TierFree, TierPro } from "~/components/billing/PricingTiers"; +import { RunsVolumeDiscountTable } from "~/components/billing/RunsVolumeDiscountTable"; +import { BreadcrumbLink } from "~/components/navigation/Breadcrumb"; +import { Callout } from "~/components/primitives/Callout"; +import { Header2 } from "~/components/primitives/Headers"; +import { featuresForRequest } from "~/features.server"; +import { useFeatures } from "~/hooks/useFeatures"; +import { OrgBillingPlanPresenter } from "~/presenters/OrgBillingPlanPresenter"; +import { Handle } from "~/utils/handle"; +import { OrganizationParamsSchema, organizationBillingPath } from "~/utils/pathBuilder"; +import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; +import { formatNumberCompact } from "~/utils/numberFormatter"; + +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 const handle: Handle = { + breadcrumb: (match) => , +}; + +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 index 763e995518..9a7063a0d2 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing/route.tsx @@ -1,198 +1,132 @@ -import { ArrowRightIcon } from "@heroicons/react/20/solid"; -import { - ForwardIcon, - SquaresPlusIcon, - UsersIcon, - WrenchScrewdriverIcon, -} from "@heroicons/react/24/solid"; -import { Bar, BarChart, ResponsiveContainer, Tooltip, TooltipProps, XAxis, YAxis } from "recharts"; +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/billing"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; -import { Header2 } from "~/components/primitives/Headers"; -import { Paragraph } from "~/components/primitives/Paragraph"; -import { TextLink } from "~/components/primitives/TextLink"; -import { useOrganization } from "~/hooks/useOrganizations"; -import { - OrganizationParamsSchema, - jobPath, - newProjectPath, - organizationTeamPath, -} from "~/utils/pathBuilder"; -import { Link } from "@remix-run/react/dist/components"; -import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { typedjson, useTypedLoaderData } from "remix-typedjson"; -import { OrgUsagePresenter } from "~/presenters/OrgUsagePresenter.server"; -import { requireUserId } from "~/services/session.server"; +import { BreadcrumbLink } from "~/components/navigation/Breadcrumb"; import { LinkButton } from "~/components/primitives/Buttons"; +import { DateTime } from "~/components/primitives/DateTime"; import { + PageButtons, PageHeader, - PageTitleRow, + PageInfoGroup, + PageInfoProperty, + PageInfoRow, + PageTabs, PageTitle, - PageButtons, + PageTitleRow, } from "~/components/primitives/PageHeader"; +import { useFeatures } from "~/hooks/useFeatures"; +import { useOrganization } from "~/hooks/useOrganizations"; +import { formatDurationInDays } from "~/utils"; import { Handle } from "~/utils/handle"; -import { BreadcrumbLink } from "~/components/navigation/Breadcrumb"; - -export async function loader({ params, request }: LoaderFunctionArgs) { - const userId = await requireUserId(request); - const { organizationSlug } = OrganizationParamsSchema.parse(params); - - const presenter = new OrgUsagePresenter(); +import { plansPath, stripePortalPath, usagePath } from "~/utils/pathBuilder"; +import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; - const data = await presenter.call({ userId, slug: organizationSlug }); +export const handle: Handle = { + breadcrumb: (match) => , +}; - if (!data) { - throw new Response(null, { status: 404 }); +function planLabel(subscription: ActiveSubscription | undefined, periodEnd: Date) { + if (!subscription) { + return "You're currently on the Free plan"; } - - return typedjson(data); -} - -const CustomTooltip = ({ active, payload, label }: TooltipProps) => { - if (active && payload) { + 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 ( -
    -

    {label}:

    -

    {payload[0].value}

    -
    + <> + You're on the {costDescription} {subscription.plan.title} plan until{" "} + when you'll be on the Free plan + ); } - return null; -}; - -export const handle: Handle = { - breadcrumb: (match) => , -}; + return `You're currently on the ${costDescription} ${subscription.plan.title} plan`; +} export default function Page() { const organization = useOrganization(); - const loaderData = useTypedLoaderData(); + const { isManagedCloud } = useFeatures(); + const currentPlan = useCurrentPlan(); return ( - + - - - - -
    -
    -
    - Total Runs this month - -
    -
    -

    {loaderData.runsCount.toLocaleString()}

    - - {loaderData.runsCountLastMonth} runs last month - -
    -
    -
    -
    - Total Jobs - -
    -
    -

    {loaderData.totalJobs.toLocaleString()}

    - - {loaderData.totalJobs === loaderData.totalJobsLastMonth ? ( - <>No change since last month - ) : loaderData.totalJobs > loaderData.totalJobsLastMonth ? ( - <>+{loaderData.totalJobs - loaderData.totalJobsLastMonth} since last month - ) : ( - <>-{loaderData.totalJobsLastMonth - loaderData.totalJobs} since last month - )} - -
    -
    -
    -
    - Total Integrations - -
    -
    -

    {loaderData.totalIntegrations.toLocaleString()}

    - - {loaderData.totalIntegrations === loaderData.totalIntegrationsLastMonth ? ( - <>No change since last month - ) : loaderData.totalIntegrations > loaderData.totalIntegrationsLastMonth ? ( - <> - +{loaderData.totalIntegrations - loaderData.totalIntegrationsLastMonth} since - last month - - ) : ( + + + {isManagedCloud && ( + <> + {currentPlan?.subscription?.isPaying && ( <> - -{loaderData.totalIntegrationsLastMonth - loaderData.totalIntegrations} since - last month + + Invoices + + + Manage card details + )} - -
    -
    -
    -
    - Team members - -
    -
    -

    {loaderData.totalMembers.toLocaleString()}

    - - Manage - - -
    -
    -
    -
    -
    - Job Runs per month - - - - `${value}`} - /> - } /> - - - -
    -
    -
    - Jobs - Runs -
    -
    - {loaderData.jobs.map((job) => ( - -
    -

    {job.slug}

    -

    Project: {job.project.name}

    -
    -
    {job._count.runs.toLocaleString()}
    - - ))} -
    -
    + Upgrade + + + )} + + + + + + {currentPlan?.subscription && ( + } + value={planLabel(currentPlan.subscription, currentPlan.usage.periodEnd)} + /> + )} + {currentPlan?.subscription?.isPaying && ( + } + label={"Billing period"} + value={ + <> + to{" "} + ( + {formatDurationInDays(currentPlan.usage.periodRemainingDuration)} remaining) + + } + /> + )} + + + {isManagedCloud && ( + + )} + + +
    +
    diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.runs/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.runs/route.tsx index 2611781185..f29e8a5e21 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.runs/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.runs/route.tsx @@ -53,7 +53,7 @@ export default function Page() { - + - All Job Runs in this project + All job runs in this project diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug/route.tsx index bb5d0eea2f..4b30c7c65e 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug/route.tsx @@ -1,17 +1,19 @@ -import { Outlet } from "@remix-run/react"; +import { Outlet, UIMatch } from "@remix-run/react"; import type { LoaderFunctionArgs } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; import { RouteErrorDisplay } from "~/components/ErrorDisplay"; +import { UpgradePrompt } from "~/components/billing/UpgradePrompt"; import { Breadcrumb, BreadcrumbLink } from "~/components/navigation/Breadcrumb"; import { PageNavigationIndicator } from "~/components/navigation/PageNavigationIndicator"; import { SideMenu } from "~/components/navigation/SideMenu"; +import { featuresForRequest } from "~/features.server"; import { useOptionalOrganization } from "~/hooks/useOrganizations"; import { useOptionalProject } from "~/hooks/useProject"; -import { useTypedMatchData } from "~/hooks/useTypedMatchData"; +import { useTypedMatchData, useTypedMatchesData } from "~/hooks/useTypedMatchData"; import { useUser } from "~/hooks/useUser"; +import { BillingService } from "~/services/billing.server"; import { OrganizationsPresenter } from "~/presenters/OrganizationsPresenter.server"; -import { getCurrentProjectId } from "~/services/currentProject.server"; import { getImpersonationId } from "~/services/impersonation.server"; import { requireUserId } from "~/services/session.server"; import { telemetry } from "~/services/telemetry.server"; @@ -23,6 +25,14 @@ const ParamsSchema = z.object({ projectParam: z.string().optional(), }); +export function useCurrentPlan(matches?: UIMatch[]) { + const data = useTypedMatchesData({ + id: "routes/_app.orgs.$organizationSlug", + matches, + }); + return data?.currentPlan; +} + export const loader = async ({ request, params }: LoaderFunctionArgs) => { const userId = await requireUserId(request); const impersonationId = await getImpersonationId(request); @@ -39,11 +49,16 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { telemetry.organization.identify({ organization }); + const { isManagedCloud } = featuresForRequest(request); + const billingPresenter = new BillingService(isManagedCloud); + const currentPlan = await billingPresenter.currentPlan(organization.id); + return typedjson({ organizations, organization, currentProject: project, isImpersonating: !!impersonationId, + currentPlan, }); }; @@ -74,9 +89,12 @@ export default function Organization() { organizations={organizations} />
    -
    +
    - +
    + + +
    diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug_.select-plan/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug_.select-plan/route.tsx new file mode 100644 index 0000000000..41bc2a0442 --- /dev/null +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug_.select-plan/route.tsx @@ -0,0 +1,89 @@ +import { ChartBarIcon } from "@heroicons/react/20/solid"; +import { LoaderFunctionArgs } from "@remix-run/server-runtime"; +import { redirect, typedjson, useTypedLoaderData } from "remix-typedjson"; +import { PricingCalculator } from "~/components/billing/PricingCalculator"; +import { PricingTiers } from "~/components/billing/PricingTiers"; +import { RunsVolumeDiscountTable } from "~/components/billing/RunsVolumeDiscountTable"; +import { Button } from "~/components/primitives/Buttons"; +import { Header1 } from "~/components/primitives/Headers"; +import { + Sheet, + SheetBody, + SheetContent, + SheetHeader, + SheetTrigger, +} from "~/components/primitives/Sheet"; +import { featuresForRequest } from "~/features.server"; +import { useOptionalProject, useProject } from "~/hooks/useProject"; +import { OrgBillingPlanPresenter } from "~/presenters/OrgBillingPlanPresenter"; +import { OrganizationsPresenter } from "~/presenters/OrganizationsPresenter.server"; +import { requireUserId } from "~/services/session.server"; +import { + OrganizationParamsSchema, + organizationBillingPath, + organizationPath, + projectPath, +} from "~/utils/pathBuilder"; + +export async function loader({ params, request }: LoaderFunctionArgs) { + const userId = await requireUserId(request); + 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 }); + } + + const orgsPresenter = new OrganizationsPresenter(); + const { organizations, organization, project } = await orgsPresenter.call({ + userId, + request, + organizationSlug, + }); + + return typedjson({ plans: result.plans, organizationSlug, projectSlug: project.slug }); +} + +export default function ChoosePlanPage() { + const { plans, organizationSlug, projectSlug } = useTypedLoaderData(); + const project = useOptionalProject(); + + return ( +
    + Subscribe for full access + + + + + + + + +
    + Estimate your usage +
    +
    + + +
    + +
    +
    +
    +
    +
    + ); +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug_.subscribed/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug_.subscribed/route.tsx new file mode 100644 index 0000000000..27b30e014b --- /dev/null +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug_.subscribed/route.tsx @@ -0,0 +1,96 @@ +import { CheckBadgeIcon } from "@heroicons/react/24/solid"; +import { RunsVolumeDiscountTable } from "~/components/billing/RunsVolumeDiscountTable"; +import { MainCenteredContainer } from "~/components/layout/AppLayout"; +import { LinkButton } from "~/components/primitives/Buttons"; +import { FormButtons } from "~/components/primitives/FormButtons"; +import { FormTitle } from "~/components/primitives/FormTitle"; +import { Paragraph } from "~/components/primitives/Paragraph"; +import { useNewCustomerSubscribed } from "~/hooks/useNewCustomerSubscribed"; +import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; +import { Handle } from "~/utils/handle"; +import { LoaderFunctionArgs } from "@remix-run/server-runtime"; +import { requireUserId } from "~/services/session.server"; +import { OrganizationsPresenter } from "~/presenters/OrganizationsPresenter.server"; +import { OrganizationParamsSchema } from "~/utils/pathBuilder"; +import { typedjson, useTypedLoaderData } from "remix-typedjson"; +import { featuresForRequest } from "~/features.server"; +import { BillingService } from "~/services/billing.server"; + +export const loader = async ({ request, params }: LoaderFunctionArgs) => { + const userId = await requireUserId(request); + + const { organizationSlug } = OrganizationParamsSchema.parse(params); + + const orgsPresenter = new OrganizationsPresenter(); + const { organization } = await orgsPresenter.call({ + userId, + request, + organizationSlug, + }); + + const { isManagedCloud } = featuresForRequest(request); + const billingPresenter = new BillingService(isManagedCloud); + const currentPlan = await billingPresenter.currentPlan(organization.id); + const plans = await billingPresenter.getPlans(); + + return typedjson({ + currentPlan, + plans, + }); +}; + +export const handle: Handle = { + scripts: () => [ + { + src: "https://cdn.jsdelivr.net/npm/canvas-confetti@1.5.1/dist/confetti.browser.min.js", + crossOrigin: "anonymous", + }, + ], +}; + +export default function Subscribed() { + const { currentPlan, plans } = useTypedLoaderData(); + useNewCustomerSubscribed(); + + return ( + + } + title="You're subscribed!" + className="mb-0" + /> +
      + + + +
    + + + + Continue + + } + /> +
    + ); +} + +function PlanItem({ item, value }: { item: string; value: string }) { + return ( +
  • + {item} + + {value} + +
  • + ); +} diff --git a/apps/webapp/app/routes/_app.orgs.new/route.tsx b/apps/webapp/app/routes/_app.orgs.new/route.tsx index fbb2f55a98..61b23c6da0 100644 --- a/apps/webapp/app/routes/_app.orgs.new/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.new/route.tsx @@ -17,12 +17,13 @@ import { Input } from "~/components/primitives/Input"; import { InputGroup } from "~/components/primitives/InputGroup"; import { Label } from "~/components/primitives/Label"; import { RadioGroupItem } from "~/components/primitives/RadioButton"; +import { featuresForRequest } from "~/features.server"; import { useFeatures } from "~/hooks/useFeatures"; import { createOrganization } from "~/models/organization.server"; import { NewOrganizationPresenter } from "~/presenters/NewOrganizationPresenter.server"; import { commitCurrentProjectSession, setCurrentProjectId } from "~/services/currentProject.server"; import { requireUserId } from "~/services/session.server"; -import { projectPath, rootPath } from "~/utils/pathBuilder"; +import { plansPath, projectPath, rootPath, selectPlanPath } from "~/utils/pathBuilder"; const schema = z.object({ orgName: z.string().min(3).max(50), @@ -61,10 +62,20 @@ export const action: ActionFunction = async ({ request }) => { const project = organization.projects[0]; const session = await setCurrentProjectId(project.id, request); + const { isManagedCloud } = featuresForRequest(request); + + const headers = { + "Set-Cookie": await commitCurrentProjectSession(session), + }; + + if (isManagedCloud) { + return redirect(selectPlanPath(organization), { + headers, + }); + } + return redirect(projectPath(organization, project), { - headers: { - "Set-Cookie": await commitCurrentProjectSession(session), - }, + headers, }); } catch (error: any) { return json({ errors: { body: error.message } }, { status: 400 }); diff --git a/apps/webapp/app/routes/_app/route.tsx b/apps/webapp/app/routes/_app/route.tsx index 8d2d20034c..6d8f8e9dc1 100644 --- a/apps/webapp/app/routes/_app/route.tsx +++ b/apps/webapp/app/routes/_app/route.tsx @@ -3,7 +3,6 @@ import type { LoaderFunctionArgs } from "@remix-run/server-runtime"; import { redirect, typedjson } from "remix-typedjson"; import { RouteErrorDisplay } from "~/components/ErrorDisplay"; import { AppContainer, MainCenteredContainer } from "~/components/layout/AppLayout"; -import { useIsOrgChildPage } from "~/hooks/useIsOrgChildPage"; import { clearRedirectTo, commitSession } from "~/services/redirectTo.server"; import { requireUser } from "~/services/session.server"; import { confirmBasicDetailsPath } from "~/utils/pathBuilder"; @@ -22,11 +21,8 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { }; export default function App() { - const isOrgChildPage = useIsOrgChildPage(); - const showBackgroundGradient = !isOrgChildPage; - return ( - + ); @@ -35,7 +31,7 @@ export default function App() { export function ErrorBoundary() { return ( <> - + diff --git a/apps/webapp/app/routes/account/route.tsx b/apps/webapp/app/routes/account/route.tsx index 70d3298b14..01f701c518 100644 --- a/apps/webapp/app/routes/account/route.tsx +++ b/apps/webapp/app/routes/account/route.tsx @@ -123,7 +123,7 @@ export default function Page() { }); return ( - +
    diff --git a/apps/webapp/app/routes/confirm-basic-details.tsx b/apps/webapp/app/routes/confirm-basic-details.tsx index 000498c217..5c1ad1c60e 100644 --- a/apps/webapp/app/routes/confirm-basic-details.tsx +++ b/apps/webapp/app/routes/confirm-basic-details.tsx @@ -137,7 +137,7 @@ export default function Page() { const shouldShowConfirm = user.email !== enteredEmail || user.email === ""; return ( - + { - const host = request.headers.get("X-Forwarded-Host") ?? request.headers.get("host"); - try { - const url = new URL("/", `http://${host}`); - - if (request.headers.get("x-forwarded-proto") === "https") { - url.protocol = "https:"; - } - // if we can connect to the database and make a simple query - // and make a HEAD request to ourselves, then we're good. - await Promise.all([ - prisma.user.count(), - fetch(url.href, { method: "HEAD" }).then((r) => { - if (!r.ok) return Promise.reject(r); - }), - ]); + await prisma.user.count(); return new Response("OK"); } catch (error: unknown) { console.log("healthcheck ❌", { error }); diff --git a/apps/webapp/app/routes/invites.tsx b/apps/webapp/app/routes/invites.tsx index eecb141247..53671bc1f9 100644 --- a/apps/webapp/app/routes/invites.tsx +++ b/apps/webapp/app/routes/invites.tsx @@ -97,7 +97,7 @@ export default function Page() { }); return ( - +
    result.subscription?.plan.runs?.freeAllowance + : false, + periodStart, + periodEnd, + periodRemainingDuration, + }; + + return { ...result, usage }; + } catch (e) { + logger.error("Error getting current plan", { orgId, error: e }); + return undefined; + } + } + + async customerPortalUrl(orgId: string, orgSlug: string) { + if (!this.#billingClient) return undefined; + try { + return this.#billingClient.createPortalSession(orgId, { + returnUrl: `${env.APP_ORIGIN}${organizationBillingPath({ slug: orgSlug })}`, + }); + } catch (e) { + logger.error("Error getting customer portal Url", { orgId, error: e }); + return undefined; + } + } + + async getPlans() { + if (!this.#billingClient) return undefined; + try { + const result = await this.#billingClient.plans(); + if (!result.success) { + logger.error("Error getting plans", { error: result.error }); + return undefined; + } + return result; + } catch (e) { + logger.error("Error getting plans", { error: e }); + return undefined; + } + } + + async setPlan(orgId: string, plan: SetPlanBody) { + if (!this.#billingClient) return undefined; + try { + const result = await this.#billingClient.setPlan(orgId, plan); + return result; + } catch (e) { + logger.error("Error setting plan", { orgId, error: e }); + return undefined; + } + } +} diff --git a/apps/webapp/app/utils.ts b/apps/webapp/app/utils.ts index bf8bbb6216..c49993db88 100644 --- a/apps/webapp/app/utils.ts +++ b/apps/webapp/app/utils.ts @@ -141,6 +141,16 @@ export function formatDurationMilliseconds( return duration; } +export function formatDurationInDays(milliseconds: number): string { + let duration = humanizeDuration(milliseconds, { + maxDecimalPoints: 0, + largest: 2, + units: ["d"], + }); + + return duration; +} + export function titleCase(original: string): string { return original .split(" ") @@ -163,5 +173,5 @@ export function appEnvTitleTag(appEnv?: string): string { return ""; } - return ` (${appEnv})` + return ` (${appEnv})`; } diff --git a/apps/webapp/app/utils/numberFormatter.ts b/apps/webapp/app/utils/numberFormatter.ts new file mode 100644 index 0000000000..4056bfec04 --- /dev/null +++ b/apps/webapp/app/utils/numberFormatter.ts @@ -0,0 +1,21 @@ +const compactFormatter = Intl.NumberFormat("en", { notation: "compact", compactDisplay: "short" }); + +export const formatNumberCompact = (num: number): string => { + return compactFormatter.format(num); +}; + +const roundedCurrencyFormatter = Intl.NumberFormat("en-US", { + style: "currency", + currencyDisplay: "symbol", + maximumFractionDigits: 0, + currency: "USD", +}); +const currencyFormatter = Intl.NumberFormat("en-US", { + style: "currency", + currencyDisplay: "symbol", + currency: "USD", +}); + +export const formatCurrency = (num: number, rounded: boolean): string => { + return rounded ? roundedCurrencyFormatter.format(num) : currencyFormatter.format(num); +}; diff --git a/apps/webapp/app/utils/pathBuilder.ts b/apps/webapp/app/utils/pathBuilder.ts index a9417fb591..855a7183fe 100644 --- a/apps/webapp/app/utils/pathBuilder.ts +++ b/apps/webapp/app/utils/pathBuilder.ts @@ -105,6 +105,10 @@ export function newOrganizationPath() { return `/orgs/new`; } +export function selectPlanPath(organization: OrgForPath) { + return `${organizationPath(organization)}/select-plan`; +} + export function organizationTeamPath(organization: OrgForPath) { return `${organizationPath(organization)}/team`; } @@ -117,6 +121,22 @@ export function organizationBillingPath(organization: OrgForPath) { return `${organizationPath(organization)}/billing`; } +export function usagePath(organization: OrgForPath) { + return `${organizationPath(organization)}/billing`; +} + +export function stripePortalPath(organization: OrgForPath) { + return `/resources/${organization.slug}/subscription/portal`; +} + +export function plansPath(organization: OrgForPath) { + return `${organizationPath(organization)}/billing/plans`; +} + +export function subscribedPath(organization: OrgForPath) { + return `${organizationPath(organization)}/subscribed`; +} + function organizationParam(organization: OrgForPath) { return organization.slug; } diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 448bcaf8e1..c2b2c41c6e 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -53,6 +53,7 @@ "@radix-ui/react-popover": "^1.0.5", "@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-select": "^1.2.1", + "@radix-ui/react-slider": "^1.1.2", "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-tabs": "^1.0.3", "@radix-ui/react-tooltip": "^1.0.5", @@ -63,7 +64,9 @@ "@remix-run/server-runtime": "2.1.0", "@remix-run/v1-meta": "^0.1.3", "@tabler/icons-react": "^2.39.0", + "@tailwindcss/container-queries": "^0.1.1", "@team-plain/typescript-sdk": "^3.5.0", + "@trigger.dev/billing": "^1.0.10", "@trigger.dev/companyicons": "^1.5.32", "@trigger.dev/core": "workspace:*", "@trigger.dev/core-backend": "workspace:*", diff --git a/apps/webapp/remix.config.js b/apps/webapp/remix.config.js index e69db48e02..e1e0c2d40f 100644 --- a/apps/webapp/remix.config.js +++ b/apps/webapp/remix.config.js @@ -14,6 +14,7 @@ module.exports = { "@trigger.dev/core", "@trigger.dev/core-backend", "@trigger.dev/sdk", + "@trigger.dev/billing", "emails", "highlight.run", "random-words", diff --git a/apps/webapp/tailwind.config.js b/apps/webapp/tailwind.config.js index f5e30144fb..c750d9cdf3 100644 --- a/apps/webapp/tailwind.config.js +++ b/apps/webapp/tailwind.config.js @@ -76,7 +76,7 @@ module.exports = { fontFamily: { sans: ["Inter", "sans-serif"], mono: ["Roboto Mono", "monospace"], - title: ["Poppins", "sans-serif"] + title: ["Poppins", "sans-serif"], }, fontSize: { xxs: [ @@ -201,10 +201,11 @@ module.exports = { }, }, plugins: [ + require("@tailwindcss/container-queries"), require("@tailwindcss/forms"), require("@tailwindcss/typography"), require("tailwindcss-animate"), require("tailwind-scrollbar"), - require('tailwind-scrollbar-hide') + require("tailwind-scrollbar-hide"), ], }; diff --git a/packages/database/prisma/migrations/20231204163703_add_composite_index_to_triggerdotdev_events_to_speed_up_organization_queries/migration.sql b/packages/database/prisma/migrations/20231204163703_add_composite_index_to_triggerdotdev_events_to_speed_up_organization_queries/migration.sql new file mode 100644 index 0000000000..64bb60ea55 --- /dev/null +++ b/packages/database/prisma/migrations/20231204163703_add_composite_index_to_triggerdotdev_events_to_speed_up_organization_queries/migration.sql @@ -0,0 +1 @@ +CREATE INDEX idx_event_time_organization_id ON triggerdotdev_events.run_executions (event_time, organization_id); \ No newline at end of file diff --git a/perf/README.md b/perf/README.md index 02025ec53a..f99e026dbc 100644 --- a/perf/README.md +++ b/perf/README.md @@ -4,6 +4,10 @@ You can use this to test the performance locally. ## Setup +``` +cd perf +``` + ```bash pnpm run server ``` diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a953714134..48dae346d5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -101,6 +101,7 @@ importers: '@radix-ui/react-popover': ^1.0.5 '@radix-ui/react-radio-group': ^1.1.3 '@radix-ui/react-select': ^1.2.1 + '@radix-ui/react-slider': ^1.1.2 '@radix-ui/react-switch': ^1.0.3 '@radix-ui/react-tabs': ^1.0.3 '@radix-ui/react-tooltip': ^1.0.5 @@ -127,10 +128,12 @@ importers: '@swc/core': ^1.3.4 '@swc/helpers': ^0.4.11 '@tabler/icons-react': ^2.39.0 + '@tailwindcss/container-queries': ^0.1.1 '@tailwindcss/forms': ^0.5.3 '@tailwindcss/typography': ^0.5.9 '@team-plain/typescript-sdk': ^3.5.0 '@total-typescript/ts-reset': ^0.4.2 + '@trigger.dev/billing': ^1.0.10 '@trigger.dev/companyicons': ^1.5.32 '@trigger.dev/core': workspace:* '@trigger.dev/core-backend': workspace:* @@ -255,6 +258,7 @@ importers: '@radix-ui/react-popover': 1.0.5_bwbutfx4xj25dewzmxso6o3wga '@radix-ui/react-radio-group': 1.1.3_daadhm4ohxobgnrt365as5bhny '@radix-ui/react-select': 1.2.1_bwbutfx4xj25dewzmxso6o3wga + '@radix-ui/react-slider': 1.1.2_daadhm4ohxobgnrt365as5bhny '@radix-ui/react-switch': 1.0.3_daadhm4ohxobgnrt365as5bhny '@radix-ui/react-tabs': 1.0.3_biqbaboplfbrettd7655fr4n2y '@radix-ui/react-tooltip': 1.0.5_bwbutfx4xj25dewzmxso6o3wga @@ -265,7 +269,9 @@ importers: '@remix-run/server-runtime': 2.1.0_typescript@5.2.2 '@remix-run/v1-meta': 0.1.3_ybjp5xbtg4zthziheradfmei64 '@tabler/icons-react': 2.40.0_react@18.2.0 + '@tailwindcss/container-queries': 0.1.1_tailwindcss@3.3.2 '@team-plain/typescript-sdk': 3.5.0 + '@trigger.dev/billing': 1.0.10 '@trigger.dev/companyicons': 1.5.32_biqbaboplfbrettd7655fr4n2y '@trigger.dev/core': link:../../packages/core '@trigger.dev/core-backend': link:../../packages/core-backend @@ -9448,6 +9454,12 @@ packages: '@babel/runtime': 7.22.5 dev: false + /@radix-ui/number/1.0.1: + resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} + dependencies: + '@babel/runtime': 7.22.5 + dev: false + /@radix-ui/primitive/1.0.0: resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==} dependencies: @@ -10070,6 +10082,37 @@ packages: - '@types/react' dev: false + /@radix-ui/react-slider/1.1.2_daadhm4ohxobgnrt365as5bhny: + resolution: {integrity: sha512-NKs15MJylfzVsCagVSWKhGGLNR1W9qWs+HtgbmjjVUB3B9+lb3PYoXxVju3kOrpf0VKyVCtZp+iTwVoqpa1Chw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.22.5 + '@radix-ui/number': 1.0.1 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3_daadhm4ohxobgnrt365as5bhny + '@radix-ui/react-compose-refs': 1.0.1_e74vmjybjy5dsfplslbsgtbvvi + '@radix-ui/react-context': 1.0.1_e74vmjybjy5dsfplslbsgtbvvi + '@radix-ui/react-direction': 1.0.1_e74vmjybjy5dsfplslbsgtbvvi + '@radix-ui/react-primitive': 1.0.3_daadhm4ohxobgnrt365as5bhny + '@radix-ui/react-use-controllable-state': 1.0.1_e74vmjybjy5dsfplslbsgtbvvi + '@radix-ui/react-use-layout-effect': 1.0.1_e74vmjybjy5dsfplslbsgtbvvi + '@radix-ui/react-use-previous': 1.0.1_e74vmjybjy5dsfplslbsgtbvvi + '@radix-ui/react-use-size': 1.0.1_e74vmjybjy5dsfplslbsgtbvvi + '@types/react': 18.2.17 + '@types/react-dom': 18.2.7 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + /@radix-ui/react-slot/1.0.0_react@18.2.0: resolution: {integrity: sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==} peerDependencies: @@ -13280,6 +13323,14 @@ packages: resolution: {integrity: sha512-VqKsBSX159cLFTnCzkCmGhZtSPJHNN0lM2sC4xe0HPOfPUnjiex7rDHDdut4oe4iKRecDDpwXwM9BcU6xCPlCg==} dev: false + /@tailwindcss/container-queries/0.1.1_tailwindcss@3.3.2: + resolution: {integrity: sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==} + peerDependencies: + tailwindcss: '>=3.2.0' + dependencies: + tailwindcss: 3.3.2_ts-node@10.9.1 + dev: false + /@tailwindcss/forms/0.5.3_tailwindcss@3.1.8: resolution: {integrity: sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==} peerDependencies: @@ -13398,6 +13449,12 @@ packages: resolution: {integrity: sha512-vqd7ZUDSrXFVT1n8b2kc3LnklncDQFPvR58yUS1kEP23/nHPAO9l1lMjUfnPrXYYk4Hj54rrLKMW5ipwk7k09A==} dev: true + /@trigger.dev/billing/1.0.10: + resolution: {integrity: sha512-vkPd1UD0fRji4qFP+s2pP26oztYzyQezQqT4+12I/5+7pOKFbOr2j0tVZj/sf7BTWgRE1Ybsk4CKT0gc9yXfKA==} + dependencies: + zod: 3.22.3 + dev: false + /@trigger.dev/companyicons/1.5.32_biqbaboplfbrettd7655fr4n2y: resolution: {integrity: sha512-+slhDClUWJ0hW4laLRvUgJ88tiPwthzI7LHUJopQGpqJV0eF/rkobOdn3fMlAIQFT1SWtcnwYxpman9aFpvtcg==} peerDependencies: