Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(pnpm typecheck:*)",
"Bash(pnpm build:*)",
"Bash(pnpm typecheck:i18n:*)"
]
}
}
1 change: 1 addition & 0 deletions clients/apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@polar-sh/checkout": "workspace:^",
"@polar-sh/client": "workspace:*",
"@polar-sh/customer-portal": "workspace:*",
"@polar-sh/i18n": "workspace:^",
"@polar-sh/mdx": "workspace:*",
"@polar-sh/sdk": "^0.42.1",
"@polar-sh/ui": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
import Checkout from '@/components/Checkout/Checkout'
import CheckoutLayout from '@/components/Checkout/CheckoutLayout'
import type { ExperimentVariant } from '@/experiments'
import { useCheckout } from '@polar-sh/checkout/providers'
import { type SupportedLocale, useCheckout } from '@polar-sh/checkout/providers'

const ClientPage = ({
embed,
theme,
layoutVariant,
locale,
}: {
embed: boolean
theme?: 'light' | 'dark'
layoutVariant: ExperimentVariant<'checkout_layout_experiment'>
locale: SupportedLocale
}) => {
const { checkout } = useCheckout()

Expand All @@ -23,7 +25,12 @@ const ClientPage = ({
theme={theme}
layoutVariant={layoutVariant}
>
<Checkout embed={embed} theme={theme} layoutVariant={layoutVariant} />
<Checkout
embed={embed}
theme={theme}
layoutVariant={layoutVariant}
locale={locale}
/>
</CheckoutLayout>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CheckoutConfirmation } from '@/components/Checkout/CheckoutConfirmation'
import CheckoutLayout from '@/components/Checkout/CheckoutLayout'
import { resolveCheckoutLocale } from '@/i18n/utils'
import { getServerURL } from '@/utils/api'
import { PolarCore } from '@polar-sh/sdk/core'
import { checkoutsClientGet } from '@polar-sh/sdk/funcs/checkoutsClientGet'
Expand All @@ -13,14 +14,18 @@ export default async function Page(props: {
embed?: string
theme?: 'light' | 'dark'
customer_session_token?: string
locale?: string
}>
}) {
const searchParams = await props.searchParams

const { embed, theme, customer_session_token } = searchParams

const params = await props.params

const {
embed,
theme,
customer_session_token,
locale: searchParamLocale,
} = searchParams
const { clientSecret } = params

const client = new PolarCore({ serverURL: getServerURL() })
Expand Down Expand Up @@ -61,13 +66,20 @@ export default async function Page(props: {
redirect(checkout.url)
}

// Type cast needed until @polar-sh/sdk is updated with locale field
const locale = await resolveCheckoutLocale(
searchParamLocale,
(checkout as typeof checkout & { locale?: string }).locale,
)

return (
<CheckoutLayout checkout={checkout} embed={embed === 'true'} theme={theme}>
<CheckoutConfirmation
checkout={checkout}
embed={embed === 'true'}
theme={theme}
customerSessionToken={customer_session_token}
locale={locale}
/>
</CheckoutLayout>
)
Expand Down
17 changes: 13 additions & 4 deletions clients/apps/web/src/app/checkout/[clientSecret]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getExperiment } from '@/experiments/server'
import { resolveCheckoutLocale } from '@/i18n/utils'
import { getPublicServerURL, getServerURL } from '@/utils/api'
import {
CheckoutFormProvider,
Expand All @@ -13,14 +14,16 @@ import CheckoutPage from './CheckoutPage'

export default async function Page(props: {
params: Promise<{ clientSecret: string }>
searchParams: Promise<{ embed?: string; theme?: 'light' | 'dark' }>
searchParams: Promise<{
embed?: string
theme?: 'light' | 'dark'
locale?: string
}>
}) {
const searchParams = await props.searchParams

const { embed: _embed, theme } = searchParams

const params = await props.params

const { embed: _embed, theme, locale: searchParamLocale } = searchParams
const { clientSecret } = params

const embed = _embed === 'true'
Expand Down Expand Up @@ -76,6 +79,11 @@ export default async function Page(props: {
redirect(`/checkout/${checkout.clientSecret}/confirmation`)
}

// Type cast needed until @polar-sh/sdk is updated with locale field
const locale = await resolveCheckoutLocale(
searchParamLocale,
(checkout as typeof checkout & { locale?: string }).locale,
)
const layoutVariant = await getExperiment('checkout_layout_experiment')

return (
Expand All @@ -88,6 +96,7 @@ export default async function Page(props: {
theme={theme}
embed={embed}
layoutVariant={layoutVariant}
locale={locale}
/>
</CheckoutFormProvider>
</CheckoutProvider>
Expand Down
7 changes: 7 additions & 0 deletions clients/apps/web/src/app/checkout/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function CheckoutLayout({
children,
}: {
children: React.ReactNode
}) {
return children
}
13 changes: 12 additions & 1 deletion clients/apps/web/src/components/Checkout/Checkout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import {
type ProductCheckoutPublic,
} from '@polar-sh/checkout/guards'
import { useCheckoutFulfillmentListener } from '@polar-sh/checkout/hooks'
import { useCheckout, useCheckoutForm } from '@polar-sh/checkout/providers'
import {
type SupportedLocale,
useCheckout,
useCheckoutForm,
} from '@polar-sh/checkout/providers'
import type { CheckoutConfirmStripe } from '@polar-sh/sdk/models/components/checkoutconfirmstripe'
import type { CheckoutPublicConfirmed } from '@polar-sh/sdk/models/components/checkoutpublicconfirmed'
import type { CheckoutUpdatePublic } from '@polar-sh/sdk/models/components/checkoutupdatepublic'
Expand All @@ -39,12 +43,14 @@ export interface CheckoutProps {
embed?: boolean
theme?: 'light' | 'dark'
layoutVariant?: ExperimentVariant<'checkout_layout_experiment'>
locale?: SupportedLocale
}

const Checkout = ({
embed: _embed,
theme: _theme,
layoutVariant = 'control',
locale,
}: CheckoutProps) => {
const isLayoutTreatment = layoutVariant === 'treatment'
const { client } = useCheckout()
Expand Down Expand Up @@ -206,6 +212,7 @@ const Checkout = ({
) => Promise<ProductCheckoutPublic>
}
themePreset={themePreset}
locale={locale}
/>
{checkout.productPrice.amountType === 'custom' && (
<CheckoutPWYWForm
Expand All @@ -228,6 +235,7 @@ const Checkout = ({
themePreset={themePreset}
disabled={shouldBlockCheckout}
isUpdatePending={isUpdatePending}
locale={locale}
/>
</ShadowBox>
)
Expand Down Expand Up @@ -278,6 +286,7 @@ const Checkout = ({
) => Promise<ProductCheckoutPublic>
}
themePreset={themePreset}
locale={locale}
/>
{checkout.productPrice.amountType === 'custom' && (
<CheckoutPWYWForm
Expand All @@ -295,6 +304,7 @@ const Checkout = ({
) => Promise<ProductCheckoutPublic>
}
isLayoutTreatment={isLayoutTreatment}
locale={locale}
/>
</>
)}
Expand All @@ -312,6 +322,7 @@ const Checkout = ({
themePreset={themePreset}
disabled={shouldBlockCheckout}
isUpdatePending={isUpdatePending}
locale={locale}
/>
</div>
</ShadowBoxOnMd>
Expand Down
13 changes: 11 additions & 2 deletions clients/apps/web/src/components/Checkout/CheckoutBenefits.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
'use client'

import { useCustomerBenefitGrants } from '@/hooks/queries/customerPortal'
import { useCustomerSSE } from '@/hooks/sse'
import { createClientSideAPI } from '@/utils/client'
import type { ProductCheckoutPublic } from '@polar-sh/checkout/guards'
import {
getTranslations,
type SupportedLocale,
} from '@polar-sh/checkout/providers'
import { List, ListItem } from '@polar-sh/ui/components/atoms/List'
import { useEffect } from 'react'
import { useEffect, useMemo } from 'react'
import { BenefitGrant } from '../Benefit/BenefitGrant'
import { SpinnerNoMargin } from '../Shared/Spinner'

interface CheckoutBenefitsProps {
checkout: ProductCheckoutPublic
customerSessionToken?: string
maxWaitingTimeMs?: number
locale?: SupportedLocale
}

const CheckoutBenefits = ({
checkout,
customerSessionToken,
maxWaitingTimeMs = 15000,
locale = 'en',
}: CheckoutBenefitsProps) => {
const t = useMemo(() => getTranslations(locale), [locale])
const api = createClientSideAPI(customerSessionToken)
const { data: benefitGrants, refetch } = useCustomerBenefitGrants(api, {
checkout_id: checkout.id,
Expand Down Expand Up @@ -70,7 +79,7 @@ const CheckoutBenefits = ({
<ListItem className="flex flex-row items-center justify-center gap-2">
<SpinnerNoMargin className="h-4 w-4" />
<p className="dark:text-polar-500 text-gray-500">
Granting benefits...
{t.confirmation.grantingBenefits}
</p>
</ListItem>
)}
Expand Down
14 changes: 12 additions & 2 deletions clients/apps/web/src/components/Checkout/CheckoutCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,50 @@ import {
CheckoutSeatSelector,
} from '@polar-sh/checkout/components'
import type { ProductCheckoutPublic } from '@polar-sh/checkout/guards'
import { getTranslations, type SupportedLocale } from '@polar-sh/i18n'
import type { CheckoutUpdatePublic } from '@polar-sh/sdk/models/components/checkoutupdatepublic'
import ShadowBox from '@polar-sh/ui/components/atoms/ShadowBox'
import { useMemo } from 'react'
export interface CheckoutCardProps {
checkout: ProductCheckoutPublic
update?: (body: CheckoutUpdatePublic) => Promise<ProductCheckoutPublic>
disabled?: boolean
isLayoutTreatment?: boolean
locale?: SupportedLocale
}

export const CheckoutCard = ({
checkout,
update,
disabled,
isLayoutTreatment = false,
locale = 'en',
}: CheckoutCardProps) => {
const t = useMemo(() => getTranslations(locale), [locale])
const { product, productPrice } = checkout
const isSeatBased = productPrice && productPrice.amountType === 'seat_based'

return (
<ShadowBox className="dark:bg-polar-900 dark:border-polar-700 flex flex-col gap-6 rounded-3xl! border border-gray-200 bg-white shadow-xs">
{isSeatBased && update ? (
<CheckoutSeatSelector checkout={checkout} update={update} />
<CheckoutSeatSelector
checkout={checkout}
update={update}
locale={locale}
/>
) : (
<CheckoutPricing
checkout={checkout}
update={update}
disabled={disabled}
layout={isLayoutTreatment ? 'stacked' : 'default'}
locale={locale}
/>
)}

{product.benefits.length > 0 ? (
<div className="flex flex-col gap-2">
<h1 className="font-medium dark:text-white">Included</h1>
<h1 className="font-medium dark:text-white">{t.form.included}</h1>
<div className="flex flex-col gap-y-2">
<BenefitList benefits={product.benefits} toggle={true} />
</div>
Expand Down
Loading
Loading