pull/715/head
Travis Fischer 2025-06-20 14:00:56 -05:00
rodzic a014881c89
commit b71ce684c0
7 zmienionych plików z 111 dodań i 25 usunięć

Wyświetl plik

@ -173,12 +173,13 @@ export const consumerSelectSchema = createSelectSchema(consumers, {
.any()
.refine(
(deployment): boolean =>
deploymentSelectSchema.safeParse(deployment).success,
!deployment || deploymentSelectSchema.safeParse(deployment).success,
{
message: 'Invalid lastDeployment'
}
)
.transform((deployment): any => {
if (!deployment) return undefined
return deploymentSelectSchema.parse(deployment)
})
.optional()

Wyświetl plik

@ -195,12 +195,14 @@ export const deploymentSelectSchema = createSelectSchema(deployments, {
project: z
.any()
.refine(
(project): boolean => projectSelectSchema.safeParse(project).success,
(project): boolean =>
!project || projectSelectSchema.safeParse(project).success,
{
message: 'Invalid lastDeployment'
}
)
.transform((project): any => {
if (!project) return undefined
return projectSelectSchema.parse(project)
})
.optional()

Wyświetl plik

@ -237,12 +237,14 @@ export const projectSelectSchema = createSelectSchema(projects, {
.any()
.refine(
(deployment): boolean =>
deploymentSelectSchema.safeParse(deployment).success,
!deployment || deploymentSelectSchema.safeParse(deployment).success,
{
message: 'Invalid lastPublishedDeployment'
}
)
.transform((deployment): any => {
if (!deployment) return undefined
return deploymentSelectSchema.parse(deployment)
})
.optional(),
@ -252,12 +254,13 @@ export const projectSelectSchema = createSelectSchema(projects, {
.any()
.refine(
(deployment): boolean =>
deploymentSelectSchema.safeParse(deployment).success,
!deployment || deploymentSelectSchema.safeParse(deployment).success,
{
message: 'Invalid lastDeployment'
}
)
.transform((deployment): any => {
if (!deployment) return undefined
return deploymentSelectSchema.parse(deployment)
})
.optional()

Wyświetl plik

@ -163,27 +163,31 @@ export function MarketplaceProjectIndex({
Pricing Plans
</h2>
{project.lastPublishedDeployment!.pricingPlans.map((plan) => (
<div key={plan.slug} className='grid gap-4'>
<h3 className='text-center text-balance leading-snug md:leading-none text-xl font-bold'>
{plan.name}
</h3>
<div className='flex gap-8'>
{project.lastPublishedDeployment!.pricingPlans.map((plan) => (
<div key={plan.slug} className='flex flex-col gap-4'>
<h3 className='text-center text-balance leading-snug md:leading-none text-xl font-bold'>
{plan.name}
</h3>
<pre className='max-w-lg'>{JSON.stringify(plan, null, 2)}</pre>
<pre className='max-w-lg'>
{JSON.stringify(plan, null, 2)}
</pre>
<Button
onClick={() => onSubscribe(plan.slug)}
// TODO: handle free plans correctly
disabled={consumer?.plan === plan.slug}
>
{consumer?.plan === plan.slug ? (
<span>Currently subscribed to "{plan.name}"</span>
) : (
<span>Subscribe to "{plan.name}"</span>
)}
</Button>
</div>
))}
<Button
onClick={() => onSubscribe(plan.slug)}
// TODO: handle free plans correctly
disabled={consumer?.plan === plan.slug}
>
{consumer?.plan === plan.slug ? (
<span>Currently subscribed to "{plan.name}"</span>
) : (
<span>Subscribe to "{plan.name}"</span>
)}
</Button>
</div>
))}
</div>
</div>
</>
)}

Wyświetl plik

@ -80,5 +80,82 @@ export default defineConfig({
inputSchemaAdditionalProperties: false,
outputSchemaAdditionalProperties: false
}
],
pricingPlans: [
{
name: 'Free',
slug: 'free',
lineItems: [
{
slug: 'base',
usageType: 'licensed',
amount: 0
},
{
slug: 'requests',
usageType: 'metered',
billingScheme: 'per_unit',
unitAmount: 0
}
]
},
{
name: 'Starter',
slug: 'starter',
lineItems: [
{
slug: 'base',
usageType: 'licensed',
amount: 999 // $9.99 USD
},
{
slug: 'requests',
usageType: 'metered',
billingScheme: 'tiered',
tiersMode: 'volume',
// free for first 1000 requests per month
// then $0.00053 USD for unlimited further requests that month
tiers: [
{
upTo: 1000,
unitAmount: 0
},
{
upTo: 'inf',
unitAmount: 0.053
}
]
}
]
},
{
name: 'Pro',
slug: 'pro',
lineItems: [
{
slug: 'base',
usageType: 'licensed',
amount: 2999 // $29.99 USD
},
{
slug: 'requests',
usageType: 'metered',
billingScheme: 'tiered',
tiersMode: 'volume',
// free for first 10000 requests per month
// then $0.00049 USD for unlimited further requests that month
tiers: [
{
upTo: 10_000,
unitAmount: 0
},
{
upTo: 'inf',
unitAmount: 0.049
}
]
}
]
}
]
})

Wyświetl plik

@ -75,7 +75,7 @@ export function errorHandler(
console.error('Error Sentry.captureException failed', err, err_)
}
}
} else {
} else if (status !== 404) {
logger.warn(status, err)
}
}

Wyświetl plik

@ -18,7 +18,6 @@
- consider a PrettyJson component which displays json but links to resources
- stripe
- stripe checkout for changing plans? (need to at least be able to upgrade)
- stripe billing portal
- should we bypass stripe for `free` plans to increase conversions?
- handle browser back/forward with `?next=`
- add some social proof to signup page