From b59010ec054ec9c5048609432bfb907d05403371 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Sun, 3 Dec 2023 18:13:05 +0000 Subject: [PATCH] Pricing slider working --- .../components/billing/PricingCalculator.tsx | 137 +++++++++++++----- .../route.tsx | 2 +- apps/webapp/package.json | 2 +- pnpm-lock.yaml | 8 +- 4 files changed, 105 insertions(+), 44 deletions(-) diff --git a/apps/webapp/app/components/billing/PricingCalculator.tsx b/apps/webapp/app/components/billing/PricingCalculator.tsx index 620686756..97fe9f29e 100644 --- a/apps/webapp/app/components/billing/PricingCalculator.tsx +++ b/apps/webapp/app/components/billing/PricingCalculator.tsx @@ -1,28 +1,57 @@ import * as Slider from "@radix-ui/react-slider"; +import { Plans } from "@trigger.dev/billing"; +import { useCallback, useState } from "react"; +import { DefinitionTip } from "../DefinitionTooltip"; import { Header2 } from "../primitives/Headers"; import { Paragraph } from "../primitives/Paragraph"; -import { DefinitionTip } from "../DefinitionTooltip"; +import { formatNumberCompact } from "~/utils/numberFormatter"; + +export function PricingCalculator({ plans }: { plans: Plans }) { + const [selectedConcurrencyIndex, setSelectedConcurrencyIndex] = useState(0); + const concurrentRunTiers = [ + { code: "free", upto: plans.free.concurrentRuns?.freeAllowance! }, + ...(plans.paid.concurrentRuns?.pricing?.tiers ?? []), + ]; + const [runs, setRuns] = useState(0); + const runBrackets = [ + ...(plans.paid.runs?.pricing?.brackets.map((b, index, arr) => ({ + unitCost: b.unitCost, + from: index === 0 ? 0 : arr[index - 1].upto! + 1, + upto: b.upto ?? arr[index - 1].upto! * 10, + })) ?? []), + ]; + + console.log("Paid", plans.paid); -export function PricingCalculator() { return (
- - + +
); } -const concurrentRuns = [ - { value: 5, label: "Up to 5" }, - { value: 20, label: "Up to 20" }, - { value: 50, label: "Up to 50" }, - { value: 100, label: "Up to 100" }, -]; +function ConcurrentRunsSlider({ + options, + selectedIndex, + setSelectedIndex, +}: { + options: { + code: string; + upto: number; + }[]; + selectedIndex: number; + setSelectedIndex: (index: number) => void; +}) { + const selectedOption = options[selectedIndex]; -function ConcurrentRunsSlider() { return ( -
+
@@ -34,12 +63,13 @@ function ConcurrentRunsSlider() { Concurrent Runs - Up to 5 + Up to {selectedOption.upto}
setSelectedIndex(value[0])} + max={options.length - 1} step={1} > @@ -51,11 +81,10 @@ function ConcurrentRunsSlider() { />
- {concurrentRuns.map((run, i) => { - const concurrrentRunsLabels = Object.values(run)[0]; + {options.map((tier, i) => { return ( - {concurrrentRunsLabels} + {tier.upto} ); })} @@ -67,24 +96,34 @@ function ConcurrentRunsSlider() {

- +
); } -const Runs = [ - { value: 10_000, label: "10k" }, - { value: 20_000, label: "20k" }, - { value: 150_000, label: "150k" }, - { value: 500_000, label: "500k" }, - { value: 1_000_000, label: "1m" }, - { value: 2_500_000, label: "2.5m" }, - { value: 6_250_000, label: "6.25m" }, - { value: 6_250_001, label: "6.25m+" }, -]; +const runIncrements = 10_000; +function RunsSlider({ + brackets, + runs, + setRuns, +}: { + brackets: { + from: number; + upto: number; + unitCost: number; + }[]; + runs: number; + setRuns: (value: number) => void; +}) { + const [value, setValue] = useState(0); + + const updateRuns = useCallback((value: number) => { + setValue(value); + const r = calculateRuns(value / runIncrements, brackets); + setRuns(r); + }, []); -function RunsSlider() { return ( -
+
@@ -93,12 +132,13 @@ function RunsSlider() { Runs - 10k + {formatNumberCompact(runs)}
updateRuns(value[0])} + max={runIncrements} step={1} > @@ -110,14 +150,16 @@ function RunsSlider() { />
- {Runs.map((run, i) => { - const RunsLabels = Object.values(run)[0]; + {brackets.map((bracket, i) => { return ( - {RunsLabels} + {formatNumberCompact(bracket.from)} ); })} + + {formatNumberCompact(brackets[brackets.length - 1].upto)} +
@@ -126,10 +168,29 @@ function RunsSlider() {

- +
); } +function calculateRuns(percentage: number, brackets: { from: number; upto: number }[]) { + //first we find which bucket we're in + const buckets = brackets.length; + const bucket = Math.min(Math.floor(percentage * buckets), brackets.length - 1); + const percentagePerBucket = 1 / buckets; + + //relevant bracket + let bracket = brackets[bucket]; + const from = bracket.from; + const upto = bracket.upto; + + //how far as we into the bracket + const percentageIntoBracket = (percentage - bucket * percentagePerBucket) / percentagePerBucket; + + //calculate the runs + const runs = Math.floor(from + (upto - from) * percentageIntoBracket); + return runs; +} + function GrandTotal() { return (
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 index 14072d1d0..8e373f35c 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.billing.plans/route.tsx @@ -51,7 +51,7 @@ export default function Page() {
Estimate your usage
- +
diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 1931d16e5..118faa4b2 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -65,7 +65,7 @@ "@tabler/icons-react": "^2.39.0", "@tailwindcss/container-queries": "^0.1.1", "@team-plain/typescript-sdk": "^3.5.0", - "@trigger.dev/billing": "^1.0.8", + "@trigger.dev/billing": "^1.0.9", "@trigger.dev/companyicons": "^1.5.32", "@trigger.dev/core": "workspace:*", "@trigger.dev/core-backend": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f63b5dd3b..99305f3c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,7 +130,7 @@ importers: '@tailwindcss/typography': ^0.5.9 '@team-plain/typescript-sdk': ^3.5.0 '@total-typescript/ts-reset': ^0.4.2 - '@trigger.dev/billing': ^1.0.8 + '@trigger.dev/billing': ^1.0.9 '@trigger.dev/companyicons': ^1.5.32 '@trigger.dev/core': workspace:* '@trigger.dev/core-backend': workspace:* @@ -268,7 +268,7 @@ importers: '@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.8 + '@trigger.dev/billing': 1.0.9 '@trigger.dev/companyicons': 1.5.32_biqbaboplfbrettd7655fr4n2y '@trigger.dev/core': link:../../packages/core '@trigger.dev/core-backend': link:../../packages/core-backend @@ -14333,8 +14333,8 @@ packages: resolution: {integrity: sha512-vqd7ZUDSrXFVT1n8b2kc3LnklncDQFPvR58yUS1kEP23/nHPAO9l1lMjUfnPrXYYk4Hj54rrLKMW5ipwk7k09A==} dev: true - /@trigger.dev/billing/1.0.8: - resolution: {integrity: sha512-vqSsbqCHn7yAjjLY4kqRizEID6bZ0p73l5VoeSeplO+qDJGZDzL8aM104+Vqd3t2kCy7o+MtEwJ/cZNStKroKA==} + /@trigger.dev/billing/1.0.9: + resolution: {integrity: sha512-N7xxNz61FXHNKm5MJH5785XvE61HI2duHwera7Lf8hNOamkA82Ru+HcsX6gnnRJlXwJcxL913hR/7C5qHX+2vQ==} dependencies: zod: 3.22.3 dev: false