Add detailed plan info and additional subscription types
Implemented detailed pricing information for various subscription types in the plan-picker component. Two additional subscription types, 'per-seat' and 'metered', have been added to the billing configuration, providing more flexibility for customers to choose different billing methods that suit their needs. The billing schema has also been refined to allow plans without a 'base' line item.
This commit is contained in:
@@ -28,6 +28,22 @@ export default createBillingSchema({
|
|||||||
cost: 9.99,
|
cost: 9.99,
|
||||||
type: 'base',
|
type: 'base',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'price_1NNwYHI1i3VnbZTqI2UzaHIe6',
|
||||||
|
name: 'Per Seat',
|
||||||
|
description: 'Add-on plan',
|
||||||
|
cost: 1.99,
|
||||||
|
type: 'per-seat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'price_1NNwYHI1i3VnbZTqI2UzaHIe7',
|
||||||
|
name: 'Metered',
|
||||||
|
description: 'Metered plan',
|
||||||
|
cost: 0.99,
|
||||||
|
type: 'metered',
|
||||||
|
unit: 'GB',
|
||||||
|
included: 10,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -302,7 +302,78 @@ export function PlanPicker(
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={'flex flex-col'}>
|
<div className={'flex flex-col space-y-1'}>
|
||||||
|
<span className={'font-semibold'}>Details</span>
|
||||||
|
|
||||||
|
<div className={'flex flex-col divide-y'}>
|
||||||
|
{selectedPlan?.lineItems.map((item) => {
|
||||||
|
switch (item.type) {
|
||||||
|
case 'base':
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={item.id}
|
||||||
|
className={
|
||||||
|
'flex items-center justify-between py-1.5 text-sm'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>{item.name}</span>
|
||||||
|
<span className={'font-semibold'}>
|
||||||
|
{formatCurrency(
|
||||||
|
selectedProduct?.currency.toLowerCase(),
|
||||||
|
item.cost,
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'per-seat':
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={item.id}
|
||||||
|
className={
|
||||||
|
'flex items-center justify-between py-1.5 text-sm'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>Per team member</span>
|
||||||
|
<span className={'font-semibold'}>
|
||||||
|
{formatCurrency(
|
||||||
|
selectedProduct?.currency.toLowerCase(),
|
||||||
|
item.cost,
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'metered':
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={item.id}
|
||||||
|
className={
|
||||||
|
'flex items-center justify-between py-1.5 text-sm'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
Per {item.unit}
|
||||||
|
{item.included
|
||||||
|
? ` (${item.included} included)`
|
||||||
|
: ''}
|
||||||
|
</span>
|
||||||
|
<span className={'font-semibold'}>
|
||||||
|
{formatCurrency(
|
||||||
|
selectedProduct?.currency.toLowerCase(),
|
||||||
|
item.cost,
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={'flex flex-col space-y-2'}>
|
||||||
|
<span className={'font-semibold'}>Features</span>
|
||||||
|
|
||||||
{selectedProduct?.features.map((item) => {
|
{selectedProduct?.features.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -316,8 +387,6 @@ export function PlanPicker(
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Separator />
|
|
||||||
</div>
|
</div>
|
||||||
</If>
|
</If>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -40,10 +40,6 @@ export const PlanSchema = z
|
|||||||
message: 'Plans must have at least one line item',
|
message: 'Plans must have at least one line item',
|
||||||
path: ['lineItems'],
|
path: ['lineItems'],
|
||||||
})
|
})
|
||||||
.refine((data) => data.lineItems.some((item) => item.type === 'base'), {
|
|
||||||
message: 'Plans must include a base line item',
|
|
||||||
path: ['lineItems'],
|
|
||||||
})
|
|
||||||
.refine(
|
.refine(
|
||||||
(data) => data.paymentType !== 'one-time' || data.interval === undefined,
|
(data) => data.paymentType !== 'one-time' || data.interval === undefined,
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user