Add loaders for team and personal account billing pages
A new loader for team and personal account billing pages has been created to fetch subscription and customer data. This change offloads the task of loading account data from the account billing pages to the newly created loaders, thus simplifying the code in the billing pages. Furthermore, caching allows the same requested to be deduped across server components
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import 'server-only';
|
||||
|
||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
||||
|
||||
export const loadPersonalAccountBillingPageData = cache((userId: string) => {
|
||||
const client = getSupabaseServerComponentClient();
|
||||
|
||||
const subscription = client
|
||||
.from('subscriptions')
|
||||
.select('*, items: subscription_items !inner (*)')
|
||||
.eq('account_id', userId)
|
||||
.maybeSingle()
|
||||
.then(({ data }) => data);
|
||||
|
||||
const customer = client
|
||||
.from('billing_customers')
|
||||
.select('customer_id')
|
||||
.eq('account_id', userId)
|
||||
.maybeSingle()
|
||||
.then(({ data }) => data?.customer_id);
|
||||
|
||||
return Promise.all([subscription, customer]);
|
||||
});
|
||||
@@ -1,22 +1,24 @@
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import {
|
||||
BillingPortalCard,
|
||||
CurrentSubscriptionCard,
|
||||
} from '@kit/billing-gateway/components';
|
||||
import { Database } from '@kit/supabase/database';
|
||||
import { requireUser } from '@kit/supabase/require-user';
|
||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { PageBody } from '@kit/ui/page';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { UserAccountHeader } from '~/(dashboard)/home/(user)/_components/user-account-header';
|
||||
import { createPersonalAccountBillingPortalSession } from '~/(dashboard)/home/(user)/billing/server-actions';
|
||||
import billingConfig from '~/config/billing.config';
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { createPersonalAccountBillingPortalSession } from '../billing/server-actions';
|
||||
import { PersonalAccountCheckoutForm } from './_components/personal-account-checkout-form';
|
||||
// user billing imports
|
||||
import { loadPersonalAccountBillingPageData } from './_lib/server/personal-account-billing-page.loader';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
const i18n = await createI18nServerInstance();
|
||||
@@ -29,7 +31,15 @@ export const generateMetadata = async () => {
|
||||
|
||||
async function PersonalAccountBillingPage() {
|
||||
const client = getSupabaseServerComponentClient();
|
||||
const [subscription, customerId] = await loadData(client);
|
||||
const auth = await requireUser(client);
|
||||
|
||||
if (auth.error) {
|
||||
redirect(auth.redirectTo);
|
||||
}
|
||||
|
||||
const [subscription, customerId] = await loadPersonalAccountBillingPageData(
|
||||
auth.data.id,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -79,29 +89,3 @@ function CustomerBillingPortalForm() {
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
async function loadData(client: SupabaseClient<Database>) {
|
||||
const { data, error } = await client.auth.getUser();
|
||||
|
||||
if (error ?? !data?.user) {
|
||||
throw new Error('Authentication required');
|
||||
}
|
||||
|
||||
const user = data.user;
|
||||
|
||||
const subscription = client
|
||||
.from('subscriptions')
|
||||
.select('*, items: subscription_items !inner (*)')
|
||||
.eq('account_id', user.id)
|
||||
.maybeSingle()
|
||||
.then(({ data }) => data);
|
||||
|
||||
const customer = client
|
||||
.from('billing_customers')
|
||||
.select('customer_id')
|
||||
.eq('account_id', user.id)
|
||||
.maybeSingle()
|
||||
.then(({ data }) => data?.customer_id);
|
||||
|
||||
return Promise.all([subscription, customer]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import 'server-only';
|
||||
|
||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
||||
|
||||
export const loadTeamAccountBillingPage = cache((accountId: string) => {
|
||||
const client = getSupabaseServerComponentClient();
|
||||
|
||||
// TODO: improve these queries to only load the necessary data
|
||||
const subscription = client
|
||||
.from('subscriptions')
|
||||
.select('*, items: subscription_items !inner (*)')
|
||||
.eq('account_id', accountId)
|
||||
.maybeSingle()
|
||||
.then(({ data }) => data);
|
||||
|
||||
const customerId = client
|
||||
.from('billing_customers')
|
||||
.select('customer_id')
|
||||
.eq('account_id', accountId)
|
||||
.maybeSingle()
|
||||
.then(({ data }) => data?.customer_id);
|
||||
|
||||
return Promise.all([subscription, customerId]);
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
|
||||
import 'server-only';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { LineItemSchema } from '@kit/billing';
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
BillingPortalCard,
|
||||
CurrentSubscriptionCard,
|
||||
} from '@kit/billing-gateway/components';
|
||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { PageBody, PageHeader } from '@kit/ui/page';
|
||||
@@ -15,6 +14,7 @@ import billingConfig from '~/config/billing.config';
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { loadTeamAccountBillingPage } from '../_lib/server/team-account-billing-page.loader';
|
||||
import { loadTeamWorkspace } from '../_lib/server/team-account-workspace.loader';
|
||||
import { TeamAccountCheckoutForm } from './_components/team-account-checkout-form';
|
||||
|
||||
@@ -36,7 +36,8 @@ export const generateMetadata = async () => {
|
||||
async function TeamAccountBillingPage({ params }: Params) {
|
||||
const workspace = await loadTeamWorkspace(params.account);
|
||||
const accountId = workspace.account.id;
|
||||
const [subscription, customerId] = await loadAccountData(accountId);
|
||||
const [subscription, customerId] =
|
||||
await loadTeamAccountBillingPage(accountId);
|
||||
|
||||
const canManageBilling =
|
||||
workspace.account.permissions.includes('billing.manage');
|
||||
@@ -119,23 +120,3 @@ function CannotManageBillingAlert() {
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
async function loadAccountData(accountId: string) {
|
||||
const client = getSupabaseServerComponentClient();
|
||||
|
||||
const subscription = client
|
||||
.from('subscriptions')
|
||||
.select('*, items: subscription_items !inner (*)')
|
||||
.eq('account_id', accountId)
|
||||
.maybeSingle()
|
||||
.then(({ data }) => data);
|
||||
|
||||
const customerId = client
|
||||
.from('billing_customers')
|
||||
.select('customer_id')
|
||||
.eq('account_id', accountId)
|
||||
.maybeSingle()
|
||||
.then(({ data }) => data?.customer_id);
|
||||
|
||||
return Promise.all([subscription, customerId]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user