Update billing page data handling and version bump to 2.12.0 (#300)
* Update billing page data handling and version bump to 2.12.0 - Refactored billing page components to streamline data loading for subscriptions and orders. - Introduced `getProductPlan` function to encapsulate product plan resolution logic. - Updated `package.json` version from 2.11.0 to 2.12.0.
This commit is contained in:
committed by
GitHub
parent
abcf1ae3d7
commit
da8a3a903d
@@ -2,26 +2,9 @@ import 'server-only';
|
||||
|
||||
import { cache } from 'react';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { createAccountsApi } from '@kit/accounts/api';
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
|
||||
/**
|
||||
* The variable BILLING_MODE represents the billing mode for a service. It can
|
||||
* have either the value 'subscription' or 'one-time'. If not provided, the default
|
||||
* value is 'subscription'. The value can be overridden by the environment variable
|
||||
* BILLING_MODE.
|
||||
*
|
||||
* If the value is 'subscription', we fetch the subscription data for the user.
|
||||
* If the value is 'one-time', we fetch the orders data for the user.
|
||||
* if none of these suits your needs, please override the below function.
|
||||
*/
|
||||
const BILLING_MODE = z
|
||||
.enum(['subscription', 'one-time'])
|
||||
.default('subscription')
|
||||
.parse(process.env.BILLING_MODE);
|
||||
|
||||
/**
|
||||
* Load the personal account billing page data for the given user.
|
||||
* @param userId
|
||||
@@ -36,12 +19,9 @@ function personalAccountBillingPageDataLoader(userId: string) {
|
||||
const client = getSupabaseServerClient();
|
||||
const api = createAccountsApi(client);
|
||||
|
||||
const data =
|
||||
BILLING_MODE === 'subscription'
|
||||
? api.getSubscription(userId)
|
||||
: api.getOrder(userId);
|
||||
|
||||
const subscription = api.getSubscription(userId);
|
||||
const order = api.getOrder(userId);
|
||||
const customerId = api.getCustomerId(userId);
|
||||
|
||||
return Promise.all([data, customerId]);
|
||||
return Promise.all([subscription, order, customerId]);
|
||||
}
|
||||
|
||||
@@ -35,24 +35,21 @@ export const generateMetadata = async () => {
|
||||
async function PersonalAccountBillingPage() {
|
||||
const user = await requireUserInServerComponent();
|
||||
|
||||
const [data, customerId] = await loadPersonalAccountBillingPageData(user.id);
|
||||
const [subscription, order, customerId] =
|
||||
await loadPersonalAccountBillingPageData(user.id);
|
||||
|
||||
let productPlan: {
|
||||
product: ProductSchema;
|
||||
plan: z.infer<typeof PlanSchema>;
|
||||
} | null = null;
|
||||
const subscriptionProductPlan = subscription
|
||||
? await getProductPlan(
|
||||
subscription.items[0]?.variant_id,
|
||||
subscription.currency,
|
||||
)
|
||||
: undefined;
|
||||
|
||||
if (data) {
|
||||
const firstLineItem = data.items[0];
|
||||
const orderProductPlan = order
|
||||
? await getProductPlan(order.items[0]?.variant_id, order.currency)
|
||||
: undefined;
|
||||
|
||||
if (firstLineItem) {
|
||||
productPlan = await resolveProductPlan(
|
||||
billingConfig,
|
||||
firstLineItem.variant_id,
|
||||
data.currency,
|
||||
);
|
||||
}
|
||||
}
|
||||
const hasBillingData = subscription || order;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -63,7 +60,7 @@ async function PersonalAccountBillingPage() {
|
||||
|
||||
<PageBody>
|
||||
<div className={'flex flex-col space-y-4'}>
|
||||
<If condition={!data}>
|
||||
<If condition={!hasBillingData}>
|
||||
<PersonalAccountCheckoutForm customerId={customerId} />
|
||||
|
||||
<If condition={customerId}>
|
||||
@@ -71,32 +68,36 @@ async function PersonalAccountBillingPage() {
|
||||
</If>
|
||||
</If>
|
||||
|
||||
<If condition={data}>
|
||||
{(data) => (
|
||||
<If condition={hasBillingData}>
|
||||
<div className={'flex w-full max-w-2xl flex-col space-y-6'}>
|
||||
{'active' in data ? (
|
||||
<If condition={subscription}>
|
||||
{(subscription) => {
|
||||
return (
|
||||
<CurrentSubscriptionCard
|
||||
subscription={data}
|
||||
product={productPlan!.product}
|
||||
plan={productPlan!.plan}
|
||||
subscription={subscription}
|
||||
product={subscriptionProductPlan!.product}
|
||||
plan={subscriptionProductPlan!.plan}
|
||||
/>
|
||||
) : (
|
||||
<CurrentLifetimeOrderCard
|
||||
order={data}
|
||||
product={productPlan!.product}
|
||||
plan={productPlan!.plan}
|
||||
/>
|
||||
)}
|
||||
|
||||
<If condition={!data}>
|
||||
<PersonalAccountCheckoutForm customerId={customerId} />
|
||||
);
|
||||
}}
|
||||
</If>
|
||||
|
||||
<If condition={customerId}>
|
||||
<CustomerBillingPortalForm />
|
||||
<If condition={order}>
|
||||
{(order) => {
|
||||
return (
|
||||
<CurrentLifetimeOrderCard
|
||||
order={order}
|
||||
product={orderProductPlan!.product}
|
||||
plan={orderProductPlan!.plan}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</If>
|
||||
</div>
|
||||
)}
|
||||
</If>
|
||||
|
||||
<If condition={customerId}>
|
||||
<CustomerBillingPortalForm />
|
||||
</If>
|
||||
</div>
|
||||
</PageBody>
|
||||
@@ -113,3 +114,20 @@ function CustomerBillingPortalForm() {
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
async function getProductPlan(
|
||||
variantId: string | undefined,
|
||||
currency: string,
|
||||
): Promise<
|
||||
| {
|
||||
product: ProductSchema;
|
||||
plan: z.infer<typeof PlanSchema>;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
if (!variantId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return resolveProductPlan(billingConfig, variantId, currency);
|
||||
}
|
||||
|
||||
@@ -815,7 +815,7 @@ export function PageViewsChart() {
|
||||
desktop: chartData.reduce((acc, curr) => acc + curr.desktop, 0),
|
||||
mobile: chartData.reduce((acc, curr) => acc + curr.mobile, 0),
|
||||
}),
|
||||
[],
|
||||
[chartData],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,26 +2,9 @@ import 'server-only';
|
||||
|
||||
import { cache } from 'react';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
import { createTeamAccountsApi } from '@kit/team-accounts/api';
|
||||
|
||||
/**
|
||||
* The variable BILLING_MODE represents the billing mode for a service. It can
|
||||
* have either the value 'subscription' or 'one-time'. If not provided, the default
|
||||
* value is 'subscription'. The value can be overridden by the environment variable
|
||||
* BILLING_MODE.
|
||||
*
|
||||
* If the value is 'subscription', we fetch the subscription data for the user.
|
||||
* If the value is 'one-time', we fetch the orders data for the user.
|
||||
* if none of these suits your needs, please override the below function.
|
||||
*/
|
||||
const BILLING_MODE = z
|
||||
.enum(['subscription', 'one-time'])
|
||||
.default('subscription')
|
||||
.parse(process.env.BILLING_MODE);
|
||||
|
||||
/**
|
||||
* @name loadTeamAccountBillingPage
|
||||
* @description Load the team account billing page data for the given account.
|
||||
@@ -32,12 +15,9 @@ function teamAccountBillingPageLoader(accountId: string) {
|
||||
const client = getSupabaseServerClient();
|
||||
const api = createTeamAccountsApi(client);
|
||||
|
||||
const data =
|
||||
BILLING_MODE === 'subscription'
|
||||
? api.getSubscription(accountId)
|
||||
: api.getOrder(accountId);
|
||||
|
||||
const subscription = api.getSubscription(accountId);
|
||||
const order = api.getOrder(accountId);
|
||||
const customerId = api.getCustomerId(accountId);
|
||||
|
||||
return Promise.all([data, customerId]);
|
||||
return Promise.all([subscription, order, customerId]);
|
||||
}
|
||||
|
||||
@@ -44,24 +44,21 @@ async function TeamAccountBillingPage({ params }: TeamAccountBillingPageProps) {
|
||||
const workspace = await loadTeamWorkspace(account);
|
||||
const accountId = workspace.account.id;
|
||||
|
||||
const [data, customerId] = await loadTeamAccountBillingPage(accountId);
|
||||
const [subscription, order, customerId] =
|
||||
await loadTeamAccountBillingPage(accountId);
|
||||
|
||||
let productPlan: {
|
||||
product: ProductSchema;
|
||||
plan: z.infer<typeof PlanSchema>;
|
||||
} | null = null;
|
||||
const subscriptionProductPlan = subscription
|
||||
? await getProductPlan(
|
||||
subscription.items[0]?.variant_id,
|
||||
subscription.currency,
|
||||
)
|
||||
: undefined;
|
||||
|
||||
if (data) {
|
||||
const firstLineItem = data.items[0];
|
||||
const orderProductPlan = order
|
||||
? await getProductPlan(order.items[0]?.variant_id, order.currency)
|
||||
: undefined;
|
||||
|
||||
if (firstLineItem) {
|
||||
productPlan = await resolveProductPlan(
|
||||
billingConfig,
|
||||
firstLineItem.variant_id,
|
||||
data.currency,
|
||||
);
|
||||
}
|
||||
}
|
||||
const hasBillingData = subscription || order;
|
||||
|
||||
const canManageBilling =
|
||||
workspace.account.permissions.includes('billing.manage');
|
||||
@@ -102,33 +99,32 @@ async function TeamAccountBillingPage({ params }: TeamAccountBillingPageProps) {
|
||||
<PageBody>
|
||||
<div
|
||||
className={cn(`flex w-full flex-col space-y-4`, {
|
||||
'max-w-2xl': data,
|
||||
'max-w-2xl': hasBillingData,
|
||||
})}
|
||||
>
|
||||
<If
|
||||
condition={data}
|
||||
fallback={
|
||||
<div>
|
||||
<If condition={!hasBillingData}>
|
||||
<Checkout />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{(data) => {
|
||||
if ('active' in data) {
|
||||
</If>
|
||||
|
||||
<If condition={subscription}>
|
||||
{(subscription) => {
|
||||
return (
|
||||
<CurrentSubscriptionCard
|
||||
subscription={data}
|
||||
product={productPlan!.product}
|
||||
plan={productPlan!.plan}
|
||||
subscription={subscription}
|
||||
product={subscriptionProductPlan!.product}
|
||||
plan={subscriptionProductPlan!.plan}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}}
|
||||
</If>
|
||||
|
||||
<If condition={order}>
|
||||
{(order) => {
|
||||
return (
|
||||
<CurrentLifetimeOrderCard
|
||||
order={data}
|
||||
product={productPlan!.product}
|
||||
plan={productPlan!.plan}
|
||||
order={order}
|
||||
product={orderProductPlan!.product}
|
||||
plan={orderProductPlan!.plan}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
@@ -158,3 +154,20 @@ function CannotManageBillingAlert() {
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
async function getProductPlan(
|
||||
variantId: string | undefined,
|
||||
currency: string,
|
||||
): Promise<
|
||||
| {
|
||||
product: ProductSchema;
|
||||
plan: z.infer<typeof PlanSchema>;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
if (!variantId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return resolveProductPlan(billingConfig, variantId, currency);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "next-supabase-saas-kit-turbo",
|
||||
"version": "2.11.0",
|
||||
"version": "2.12.0",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"engines": {
|
||||
|
||||
Reference in New Issue
Block a user