From 4e305bf8c90300f4f41582a3a8a18147fa9348e1 Mon Sep 17 00:00:00 2001 From: giancarlo Date: Mon, 15 Apr 2024 01:18:27 +0800 Subject: [PATCH] Update UI, improve E2E tests and modify trial period configuration The code changes incorporate UI updates for better usability and user experience. E2E test scripts(in `user-billing.spec.ts` and `team-billing.spec.ts`) were also updated for improved efficiency and accuracy, primarily replacing 'Active' status check with 'Trial'. Changes have been made in the trialDays configuration, with the term 'trialPeriod' now replaced by 'trialDays' across different components. Notably, a new error handling case is included in `lemon-squeezy-billing-strategy.service.ts` for failed subscription cancellation attempts. --- .../tests/team-billing/team-billing.spec.ts | 4 +- .../tests/user-billing/user-billing.spec.ts | 4 +- apps/e2e/tests/utils/billing.po.ts | 4 +- .../home/[account]/billing/page.tsx | 11 +++-- .../home/[account]/billing/return/page.tsx | 30 ++++++------- apps/web/config/billing.sample.config.ts | 2 +- .../billing/core/src/create-billing-schema.ts | 2 +- .../schema/create-billing-checkout.schema.ts | 1 - .../src/components/billing-session-status.tsx | 36 +++------------- .../gateway/src/components/plan-picker.tsx | 6 +-- .../lemon-squeezy-billing-strategy.service.ts | 2 +- .../src/services/create-stripe-checkout.ts | 2 +- .../src/components/account-selector.tsx | 42 ++++++++++++------- .../members/account-members-table.tsx | 9 +--- 14 files changed, 69 insertions(+), 86 deletions(-) diff --git a/apps/e2e/tests/team-billing/team-billing.spec.ts b/apps/e2e/tests/team-billing/team-billing.spec.ts index f16ff0564..b1651eaff 100644 --- a/apps/e2e/tests/team-billing/team-billing.spec.ts +++ b/apps/e2e/tests/team-billing/team-billing.spec.ts @@ -13,7 +13,7 @@ test.describe('Team Billing', () => { await po.teamAccounts.goToBilling(); }); - test('a team can subscribe to a plan', async ({page}) => { + test('a team can subscribe to a plan', async () => { await po.billing.selectPlan(0); await po.billing.proceedToCheckout(); @@ -25,7 +25,7 @@ test.describe('Team Billing', () => { await po.teamAccounts.goToBilling(); - await expect(await po.billing.getStatus()).toContainText('Active'); + await expect(await po.billing.getStatus()).toContainText('Trial'); await expect(po.billing.manageBillingButton()).toBeVisible(); }); }); \ No newline at end of file diff --git a/apps/e2e/tests/user-billing/user-billing.spec.ts b/apps/e2e/tests/user-billing/user-billing.spec.ts index eabbfffee..5a3526f5e 100644 --- a/apps/e2e/tests/user-billing/user-billing.spec.ts +++ b/apps/e2e/tests/user-billing/user-billing.spec.ts @@ -22,15 +22,13 @@ test.describe('User Billing', () => { await expect(po.billing.successStatus()).toBeVisible(); await po.billing.returnToHome(); - await page.waitForURL('http://localhost:3000/home'); - const link = page.locator('button', { hasText: 'Billing' }); await link.click(); - await expect(await po.billing.getStatus()).toContainText('Active'); + await expect(await po.billing.getStatus()).toContainText('Trial'); await expect(po.billing.manageBillingButton()).toBeVisible(); }); }); \ No newline at end of file diff --git a/apps/e2e/tests/utils/billing.po.ts b/apps/e2e/tests/utils/billing.po.ts index 4087a44a3..ff2f137f8 100644 --- a/apps/e2e/tests/utils/billing.po.ts +++ b/apps/e2e/tests/utils/billing.po.ts @@ -1,4 +1,4 @@ -import { expect, Page } from '@playwright/test'; +import { Page } from '@playwright/test'; import { StripePageObject } from './stripe.po'; export class BillingPageObject { @@ -32,7 +32,7 @@ export class BillingPageObject { // wait a bit for the webhook to be processed await this.page.waitForTimeout(1000); - return this.page.locator('[data-test="checkout-success-back-link"]').click(); + return this.page.locator('[data-test="checkout-success-back-link"] button').click(); } proceedToCheckout() { diff --git a/apps/web/app/(dashboard)/home/[account]/billing/page.tsx b/apps/web/app/(dashboard)/home/[account]/billing/page.tsx index 28217edfd..6562ee9c7 100644 --- a/apps/web/app/(dashboard)/home/[account]/billing/page.tsx +++ b/apps/web/app/(dashboard)/home/[account]/billing/page.tsx @@ -8,6 +8,7 @@ import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; import { If } from '@kit/ui/if'; import { PageBody, PageHeader } from '@kit/ui/page'; import { Trans } from '@kit/ui/trans'; +import { cn } from '@kit/ui/utils'; import { createBillingPortalSession } from '~/(dashboard)/home/[account]/billing/server-actions'; import billingConfig from '~/config/billing.config'; @@ -75,13 +76,17 @@ async function TeamAccountBillingPage({ params }: Params) { /> -
+
+
- +
} > {(subscription) => ( diff --git a/apps/web/app/(dashboard)/home/[account]/billing/return/page.tsx b/apps/web/app/(dashboard)/home/[account]/billing/return/page.tsx index e7c5a7bf6..70f04ab34 100644 --- a/apps/web/app/(dashboard)/home/[account]/billing/return/page.tsx +++ b/apps/web/app/(dashboard)/home/[account]/billing/return/page.tsx @@ -1,3 +1,4 @@ +import { revalidatePath } from 'next/cache'; import dynamic from 'next/dynamic'; import { notFound, redirect } from 'next/navigation'; @@ -7,17 +8,12 @@ import { requireUser } from '@kit/supabase/require-user'; import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client'; import billingConfig from '~/config/billing.config'; -import pathsConfig from '~/config/paths.config'; import { withI18n } from '~/lib/i18n/with-i18n'; interface SessionPageProps { searchParams: { session_id: string; }; - - params: { - account: string; - }; } const LazyEmbeddedCheckout = dynamic( @@ -31,10 +27,7 @@ const LazyEmbeddedCheckout = dynamic( }, ); -async function ReturnCheckoutSessionPage({ - searchParams, - params, -}: SessionPageProps) { +async function ReturnCheckoutSessionPage({ searchParams }: SessionPageProps) { const sessionId = searchParams.session_id; if (!sessionId) { @@ -52,17 +45,12 @@ async function ReturnCheckoutSessionPage({ ); } - const redirectPath = pathsConfig.app.accountHome.replace( - '[account]', - params.account, - ); - return ( <>
@@ -106,3 +94,15 @@ async function loadCheckoutSession(sessionId: string) { checkoutToken, }; } + +// eslint-disable-next-line @typescript-eslint/require-await +async function onRedirect() { + 'use server'; + + // revalidate the home page to update cached pages + // which may have changed due to the billing session + revalidatePath('/home', 'layout'); + + // redirect back + redirect('../'); +} diff --git a/apps/web/config/billing.sample.config.ts b/apps/web/config/billing.sample.config.ts index 0b613fcdc..1b34b1a30 100644 --- a/apps/web/config/billing.sample.config.ts +++ b/apps/web/config/billing.sample.config.ts @@ -26,7 +26,7 @@ export default createBillingSchema({ { name: 'Starter Monthly', id: 'starter-monthly', - trialPeriod: 7, + trialDays: 7, paymentType: 'recurring', interval: 'month', lineItems: [ diff --git a/packages/billing/core/src/create-billing-schema.ts b/packages/billing/core/src/create-billing-schema.ts index c85d4305d..d811ca4da 100644 --- a/packages/billing/core/src/create-billing-schema.ts +++ b/packages/billing/core/src/create-billing-schema.ts @@ -81,7 +81,7 @@ export const PlanSchema = z .min(1), interval: BillingIntervalSchema.optional(), lineItems: z.array(LineItemSchema), - trialPeriod: z + trialDays: z .number({ description: 'Number of days for the trial period. Leave empty for no trial.', diff --git a/packages/billing/core/src/schema/create-billing-checkout.schema.ts b/packages/billing/core/src/schema/create-billing-checkout.schema.ts index 7d1f527bd..3af9724e1 100644 --- a/packages/billing/core/src/schema/create-billing-checkout.schema.ts +++ b/packages/billing/core/src/schema/create-billing-checkout.schema.ts @@ -6,7 +6,6 @@ export const CreateBillingCheckoutSchema = z.object({ returnUrl: z.string().url(), accountId: z.string().uuid(), plan: PlanSchema, - trialDays: z.number().optional(), customerId: z.string().optional(), customerEmail: z.string().email().optional(), enableDiscountField: z.boolean().optional(), diff --git a/packages/billing/gateway/src/components/billing-session-status.tsx b/packages/billing/gateway/src/components/billing-session-status.tsx index 5760deab7..9ffd239bc 100644 --- a/packages/billing/gateway/src/components/billing-session-status.tsx +++ b/packages/billing/gateway/src/components/billing-session-status.tsx @@ -1,7 +1,3 @@ -'use client'; - -import Link from 'next/link'; - import { Check, ChevronRight } from 'lucide-react'; import { Button } from '@kit/ui/button'; @@ -12,33 +8,13 @@ import { Trans } from '@kit/ui/trans'; * Retrieves the session status for a Stripe checkout session. * Since we should only arrive here for a successful checkout, we only check * for the `paid` status. - * - * @param {Stripe.Checkout.Session['status']} status - The status of the Stripe checkout session. - * @param {string} customerEmail - The email address of the customer associated with the session. - * - * @returns {ReactElement} - The component to render based on the session status. - */ + **/ export function BillingSessionStatus({ customerEmail, - redirectPath, + onRedirect, }: React.PropsWithChildren<{ customerEmail: string; - redirectPath: string; -}>) { - return ( - - ); -} - -function SuccessSessionStatus({ - customerEmail, - redirectPath, -}: React.PropsWithChildren<{ - customerEmail: string; - redirectPath: string; + onRedirect: () => void; }>) { return (
- - - +
); diff --git a/packages/billing/gateway/src/components/plan-picker.tsx b/packages/billing/gateway/src/components/plan-picker.tsx index 79a91c38b..196340523 100644 --- a/packages/billing/gateway/src/components/plan-picker.tsx +++ b/packages/billing/gateway/src/components/plan-picker.tsx @@ -288,7 +288,7 @@ export function PlanPicker( >
@@ -296,7 +296,7 @@ export function PlanPicker( @@ -356,7 +356,7 @@ export function PlanPicker( ) : ( <> {t(`startTrial`)} diff --git a/packages/billing/lemon-squeezy/src/services/lemon-squeezy-billing-strategy.service.ts b/packages/billing/lemon-squeezy/src/services/lemon-squeezy-billing-strategy.service.ts index 44a871e1f..02db394d7 100644 --- a/packages/billing/lemon-squeezy/src/services/lemon-squeezy-billing-strategy.service.ts +++ b/packages/billing/lemon-squeezy/src/services/lemon-squeezy-billing-strategy.service.ts @@ -119,7 +119,7 @@ export class LemonSqueezyBillingStrategyService 'Failed to cancel subscription', ); - throw error; + throw new Error('Failed to cancel subscription'); } logger.info(ctx, 'Subscription cancelled successfully'); diff --git a/packages/billing/stripe/src/services/create-stripe-checkout.ts b/packages/billing/stripe/src/services/create-stripe-checkout.ts index ee22ddc25..948a17cdc 100644 --- a/packages/billing/stripe/src/services/create-stripe-checkout.ts +++ b/packages/billing/stripe/src/services/create-stripe-checkout.ts @@ -33,7 +33,7 @@ export async function createStripeCheckout( | Stripe.Checkout.SessionCreateParams.SubscriptionData | undefined = isSubscription ? { - trial_period_days: params.trialDays, + trial_period_days: params.plan.trialDays, metadata: { accountId: params.accountId, }, diff --git a/packages/features/accounts/src/components/account-selector.tsx b/packages/features/accounts/src/components/account-selector.tsx index 5d9aed023..e7f97e08e 100644 --- a/packages/features/accounts/src/components/account-selector.tsx +++ b/packages/features/accounts/src/components/account-selector.tsx @@ -191,7 +191,12 @@ export function AccountSelector({ data-test={'account-selector-team'} data-name={account.label} data-slug={account.value} - className={'group flex space-x-2'} + className={cn( + 'group flex justify-between transition-colors', + { + ['bg-muted']: value === account.value, + }, + )} key={account.value} value={account.value ?? ''} onSelect={(currentValue) => { @@ -203,23 +208,28 @@ export function AccountSelector({ } }} > - - +
+ + - - {account.label ? account.label[0] : ''} - - + + {account.label ? account.label[0] : ''} + + - - {account.label} - + + {account.label} + +
diff --git a/packages/features/team-accounts/src/components/members/account-members-table.tsx b/packages/features/team-accounts/src/components/members/account-members-table.tsx index 06ad00da8..1866d0021 100644 --- a/packages/features/team-accounts/src/components/members/account-members-table.tsx +++ b/packages/features/team-accounts/src/components/members/account-members-table.tsx @@ -7,6 +7,7 @@ import { Ellipsis } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { Database } from '@kit/supabase/database'; +import { Badge } from '@kit/ui/badge'; import { Button } from '@kit/ui/button'; import { DataTable } from '@kit/ui/data-table'; import { @@ -121,13 +122,7 @@ function useGetColumns( {displayName} - - {t('youLabel')} - + {t('youLabel')} );