Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions .changeset/slow-foxes-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@clerk/localizations': patch
'@clerk/clerk-js': patch
'@clerk/types': patch
---

Refactor billing statement page and payment attempt page data loading
6 changes: 3 additions & 3 deletions packages/clerk-js/bundlewatch.config.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"files": [
{ "path": "./dist/clerk.js", "maxSize": "618KB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "72.2KB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "72.45KB" },
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "115.08KB" },
{ "path": "./dist/clerk.headless*.js", "maxSize": "55KB" },
{ "path": "./dist/clerk.headless*.js", "maxSize": "55.2KB" },
{ "path": "./dist/ui-common*.js", "maxSize": "113KB" },
{ "path": "./dist/ui-common*.legacy.*.js", "maxSize": "118KB" },
{ "path": "./dist/vendors*.js", "maxSize": "40.2KB" },
Expand All @@ -23,7 +23,7 @@
{ "path": "./dist/waitlist*.js", "maxSize": "1.5KB" },
{ "path": "./dist/keylessPrompt*.js", "maxSize": "6.5KB" },
{ "path": "./dist/pricingTable*.js", "maxSize": "4.02KB" },
{ "path": "./dist/checkout*.js", "maxSize": "8.45KB" },
{ "path": "./dist/checkout*.js", "maxSize": "8.5KB" },
{ "path": "./dist/up-billing-page*.js", "maxSize": "3.0KB" },
{ "path": "./dist/op-billing-page*.js", "maxSize": "3.0KB" },
{ "path": "./dist/up-plans-page*.js", "maxSize": "1.0KB" },
Expand Down
22 changes: 22 additions & 0 deletions packages/clerk-js/src/core/modules/commerce/CommerceBilling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ export class CommerceBilling implements CommerceBillingNamespace {
});
};

getStatement = async (params: { id: string; orgId?: string }): Promise<CommerceStatementResource> => {
const statement = (
await BaseResource._fetch({
path: params.orgId
? `/organizations/${params.orgId}/commerce/statements/${params.id}`
: `/me/commerce/statements/${params.id}`,
method: 'GET',
})
)?.response as unknown as CommerceStatementJSON;
return new CommerceStatement(statement);
};

getPaymentAttempts = async (
params: GetPaymentAttemptsParams,
): Promise<ClerkPaginatedResponse<CommercePaymentResource>> => {
Expand All @@ -122,6 +134,16 @@ export class CommerceBilling implements CommerceBillingNamespace {
});
};

getPaymentAttempt = async (params: { id: string; orgId?: string }): Promise<CommercePaymentResource> => {
const paymentAttempt = (await BaseResource._fetch({
path: params.orgId
? `/organizations/${params.orgId}/commerce/payment_attempts/${params.id}`
: `/me/commerce/payment_attempts/${params.id}`,
method: 'GET',
})) as unknown as CommercePaymentJSON;
return new CommercePayment(paymentAttempt);
};

startCheckout = async (params: CreateCheckoutParams) => {
const { orgId, ...rest } = params;
const json = (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { useClerk, useOrganization } from '@clerk/shared/react';
import useSWR from 'swr';

import { Header } from '@/ui/elements/Header';
import { LineItems } from '@/ui/elements/LineItems';
import { formatDate } from '@/ui/utils/formatDate';
import { truncateWithEndVisible } from '@/ui/utils/truncateTextWithEndVisible';

import { usePaymentAttemptsContext, useStatements } from '../../contexts';
import { useSubscriberTypeLocalizationRoot } from '../../contexts/components';
import { useSubscriberTypeContext, useSubscriberTypeLocalizationRoot } from '../../contexts/components';
import {
Badge,
Box,
Expand All @@ -23,11 +25,20 @@ import { useRouter } from '../../router';

export const PaymentAttemptPage = () => {
const { params, navigate } = useRouter();
const { isLoading } = useStatements();
const { getPaymentAttemptById } = usePaymentAttemptsContext();
Comment thread
aeliox marked this conversation as resolved.
const subscriberType = useSubscriberTypeContext();
const { organization } = useOrganization();
const localizationRoot = useSubscriberTypeLocalizationRoot();
const clerk = useClerk();

const { data: paymentAttempt, isLoading } = useSWR(
params.paymentAttemptId ? { type: 'payment-attempt', id: params.paymentAttemptId } : null,
Comment thread
aeliox marked this conversation as resolved.
Outdated
() =>
clerk.billing.getPaymentAttempt({
id: params.paymentAttemptId,
orgId: subscriberType === 'org' ? organization?.id : undefined,
}),
);

const paymentAttempt = params.paymentAttemptId ? getPaymentAttemptById(params.paymentAttemptId) : null;
const subscriptionItem = paymentAttempt?.subscriptionItem;

if (isLoading) {
Expand Down
8 changes: 6 additions & 2 deletions packages/clerk-js/src/ui/components/Statements/Statement.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import React from 'react';

import type { LocalizationKey } from '../../customizables';
import { Badge, Box, Button, descriptors, Heading, Icon, Span, Text } from '../../customizables';
import { useClipboard } from '../../hooks';
Expand Down Expand Up @@ -279,7 +281,7 @@ function SectionContentDetailsListItem({
icon?: React.ReactNode;
label: string | LocalizationKey;
labelIcon?: React.ComponentType;
value: string | LocalizationKey;
value: string | LocalizationKey | React.ReactElement;
valueTruncated?: boolean;
valueCopyable?: boolean;
}) {
Expand Down Expand Up @@ -348,12 +350,14 @@ function SectionContentDetailsListItem({
{valueTruncated ? truncateWithEndVisible(value) : value}
</Text>
</>
) : React.isValidElement(value) ? (
value
) : (
<Text
elementDescriptor={descriptors.statementSectionContentDetailsListItemValue}
colorScheme='secondary'
variant='caption'
localizationKey={value}
localizationKey={value as LocalizationKey}
/>
)}
</Span>
Expand Down
55 changes: 46 additions & 9 deletions packages/clerk-js/src/ui/components/Statements/StatementPage.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,41 @@
import { useClerk, useOrganization } from '@clerk/shared/react';
import useSWR from 'swr';

import { Header } from '@/ui/elements/Header';
import { formatDate } from '@/ui/utils/formatDate';

import { useStatements, useStatementsContext, useSubscriberTypeLocalizationRoot } from '../../contexts';
import { Box, descriptors, localizationKeys, Spinner, Text, useLocalizations } from '../../customizables';
import { Plus, RotateLeftRight } from '../../icons';
import { useSubscriberTypeContext, useSubscriberTypeLocalizationRoot } from '../../contexts/components';
import {
Box,
descriptors,
Icon,
localizationKeys,
SimpleButton,
Span,
Spinner,
Text,
useLocalizations,
} from '../../customizables';
import { ArrowRightIcon, Plus, RotateLeftRight } from '../../icons';
import { useRouter } from '../../router';
import { Statement } from './Statement';

export const StatementPage = () => {
const { params, navigate } = useRouter();
const { isLoading } = useStatements();
const { getStatementById } = useStatementsContext();
const subscriberType = useSubscriberTypeContext();
const { organization } = useOrganization();
const localizationRoot = useSubscriberTypeLocalizationRoot();
const { t } = useLocalizations();
const statement = params.statementId ? getStatementById(params.statementId) : null;
const clerk = useClerk();

const { data: statement, isLoading } = useSWR(
params.statementId ? { type: 'statement', id: params.statementId } : null,
() =>
clerk.billing.getStatement({
id: params.statementId,
orgId: subscriberType === 'org' ? organization?.id : undefined,
}),
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

if (isLoading) {
return (
Expand Down Expand Up @@ -91,9 +113,24 @@ export const StatementPage = () => {
)
}
labelIcon={item.chargeType === 'recurring' ? RotateLeftRight : Plus}
value={item.id}
valueTruncated
valueCopyable
value={
<SimpleButton
onClick={() => void navigate(`../../payment-attempt/${item.id}`)}
variant='link'
colorScheme='primary'
textVariant='buttonSmall'
sx={t => ({
gap: t.space.$1,
})}
>
<Span localizationKey={localizationKeys('commerce.viewPayment')} />
<Icon
icon={ArrowRightIcon}
size='sm'
aria-hidden
/>
</SimpleButton>
}
/>
{item.subscriptionItem.credit && item.subscriptionItem.credit.amount.amount > 0 ? (
<Statement.SectionContentDetailsListItem
Expand Down
1 change: 1 addition & 0 deletions packages/localizations/src/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export const enUS: LocalizationResource = {
totalDue: 'Total due',
totalDueToday: 'Total Due Today',
viewFeatures: 'View features',
viewPayment: 'View payment',
Comment thread
aeliox marked this conversation as resolved.
year: 'Year',
},
createOrganization: {
Expand Down
20 changes: 20 additions & 0 deletions packages/types/src/commerce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ export interface CommerceBillingNamespace {
*/
getPaymentAttempts: (params: GetPaymentAttemptsParams) => Promise<ClerkPaginatedResponse<CommercePaymentResource>>;

/**
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
getPaymentAttempt: (params: { id: string; orgId?: string }) => Promise<CommercePaymentResource>;

/**
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
Expand Down Expand Up @@ -79,6 +89,16 @@ export interface CommerceBillingNamespace {
*/
getStatements: (params: GetStatementsParams) => Promise<ClerkPaginatedResponse<CommerceStatementResource>>;

/**
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
getStatement: (params: { id: string; orgId?: string }) => Promise<CommerceStatementResource>;

/**
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/localization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ export type __internal_LocalizationResource = {
defaultFreePlanActive: LocalizationValue;
viewFeatures: LocalizationValue;
seeAllFeatures: LocalizationValue;
viewPayment: LocalizationValue;
availableFeatures: LocalizationValue;
subtotal: LocalizationValue;
credit: LocalizationValue;
Expand Down
Loading