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')} );