From 9492943c6b2db7dda9d089c67213a6d072cb8d99 Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Tue, 10 Jun 2025 16:08:25 -0700 Subject: [PATCH 01/10] bunch of commerce localizations --- .../OrganizationPlansPage.tsx | 4 +- .../PaymentAttempts/PaymentAttemptPage.tsx | 12 +++-- .../PaymentAttempts/PaymentAttemptsList.tsx | 28 ++++++++++-- .../PaymentSources/AddPaymentSource.tsx | 7 +-- .../PaymentSources/PaymentSources.tsx | 43 +++++++++++------- .../ui/components/Statements/Statement.tsx | 3 ++ .../components/Statements/StatementPage.tsx | 34 ++++++++++---- .../components/Statements/StatementsList.tsx | 26 +++++++++-- .../Subscriptions/SubscriptionsList.tsx | 19 ++++++-- .../ui/components/UserProfile/PlansPage.tsx | 4 +- packages/localizations/src/en-US.ts | 45 ++++++++++++++++++- packages/types/src/localization.ts | 43 ++++++++++++++++++ 12 files changed, 223 insertions(+), 45 deletions(-) diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationPlansPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationPlansPage.tsx index d675adc27ef..93b8acefdc4 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationPlansPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationPlansPage.tsx @@ -22,7 +22,9 @@ const OrganizationPlansPageInternal = () => { paddingBlockEnd: t.space.$4, })} > - void navigate('../', { searchParams: new URLSearchParams('tab=plans') })}> + void navigate('../', { searchParams: new URLSearchParams('tab=subscriptions') })} + > { const { params, navigate } = useRouter(); const { isLoading } = useStatements(); const { getPaymentAttemptById } = usePaymentAttemptsContext(); + const subscriberType = useSubscriberTypeContext(); + const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; const paymentAttempt = params.paymentAttemptId ? getPaymentAttemptById(params.paymentAttemptId) : null; const subscriptionItem = paymentAttempt?.subscriptionItem; @@ -40,7 +42,9 @@ export const PaymentAttemptPage = () => { } if (!paymentAttempt) { - return Payment attempt not found; + return ( + + ); } return ( @@ -56,7 +60,7 @@ export const PaymentAttemptPage = () => { > void navigate('../../', { searchParams: new URLSearchParams('tab=payments') })}> @@ -173,7 +177,7 @@ export const PaymentAttemptPage = () => { > { const { data: paymentAttempts, isLoading } = usePaymentAttempts(); + const subscriberType = useSubscriberTypeContext(); + const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; return ( { pageCount={1} itemsPerPage={10} isLoading={isLoading} - emptyStateLocalizationKey='No payment history' - headers={['Date', 'Amount', 'Status']} + emptyStateLocalizationKey={localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.empty`)} + headers={[ + localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.tableHeaders__date`), + localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.tableHeaders__amount`), + localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.tableHeaders__status`), + ]} rows={(paymentAttempts?.data || []).map(i => ( { const elements = useElements(); const { displayConfig } = useEnvironment(); const { t } = useLocalizations(); + const subscriberType = useSubscriberTypeContext(); + const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; const onSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -260,10 +262,8 @@ const AddPaymentSourceForm = ({ children }: PropsWithChildren) => { try { await onSuccess({ stripeSetupIntent: setupIntent }); } catch (error) { - console.log('catch', error); void handleError(error, [], card.setError); } finally { - console.log('finally'); card.setIdle(); initializePaymentSource(); // resets the payment intent } @@ -311,7 +311,8 @@ const AddPaymentSourceForm = ({ children }: PropsWithChildren) => { void const { close } = useActionContext(); const clerk = useClerk(); const subscriberType = useSubscriberTypeContext(); + const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; const onAddPaymentSourceSuccess = async (context: { stripeSetupIntent?: SetupIntent }) => { const resource = subscriberType === 'org' ? clerk?.organization : clerk.user; @@ -40,9 +41,11 @@ const AddScreen = withCardStateProvider(({ onSuccess }: { onSuccess: () => void onSuccess={onAddPaymentSourceSuccess} cancelAction={close} > - + @@ -62,6 +65,7 @@ const RemoveScreen = ({ const card = useCardState(); const subscriberType = useSubscriberTypeContext(); const { organization } = useOrganization(); + const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; const ref = useRef( `${paymentSource.paymentMethod === 'card' ? paymentSource.cardType : paymentSource.paymentMethod} ${paymentSource.paymentMethod === 'card' ? `⋯ ${paymentSource.last4}` : '-'}`, ); @@ -81,14 +85,22 @@ const RemoveScreen = ({ return ( { const clerk = useClerk(); const subscriberType = useSubscriberTypeContext(); - + const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; const resource = subscriberType === 'org' ? clerk?.organization : clerk.user; const { data, isLoading, mutate: mutatePaymentSources } = usePaymentSources(); @@ -119,7 +131,7 @@ export const PaymentSources = withCardStateProvider(() => { return ( ({ @@ -161,7 +173,7 @@ export const PaymentSources = withCardStateProvider(() => { @@ -188,10 +200,11 @@ const PaymentSourceMenu = ({ const card = useCardState(); const { organization } = useOrganization(); const subscriberType = useSubscriberTypeContext(); + const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; const actions = [ { - label: localizationKeys('userProfile.billingPage.paymentSourcesSection.actionLabel__remove'), + label: localizationKeys(`${localizationRoot}.billingPage.paymentSourcesSection.actionLabel__remove`), isDestructive: true, onClick: () => open(`remove-${paymentSource.id}`), isDisabled: !paymentSource.isRemovable, @@ -200,7 +213,7 @@ const PaymentSourceMenu = ({ if (!paymentSource.isDefault) { actions.unshift({ - label: localizationKeys('userProfile.billingPage.paymentSourcesSection.actionLabel__default'), + label: localizationKeys(`${localizationRoot}.billingPage.paymentSourcesSection.actionLabel__default`), isDestructive: false, onClick: () => { paymentSource diff --git a/packages/clerk-js/src/ui/components/Statements/Statement.tsx b/packages/clerk-js/src/ui/components/Statements/Statement.tsx index ae10b73ff7a..4be1803bda4 100644 --- a/packages/clerk-js/src/ui/components/Statements/Statement.tsx +++ b/packages/clerk-js/src/ui/components/Statements/Statement.tsx @@ -212,6 +212,9 @@ function SectionContentDetailsHeader({ colorScheme='secondary' elementDescriptor={descriptors.statementSectionContentDetailsHeaderDescription} localizationKey={description} + sx={() => ({ + textTransform: 'lowercase', + })} /> { const { params, navigate } = useRouter(); const { isLoading } = useStatements(); const { getStatementById } = useStatementsContext(); - + const subscriberType = useSubscriberTypeContext(); + const { t } = useLocalizations(); + const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; const statement = params.statementId ? getStatementById(params.statementId) : null; if (isLoading) { @@ -26,7 +28,7 @@ export const StatementPage = () => { } if (!statement) { - return Statement not found; + return ; } return ( @@ -44,7 +46,7 @@ export const StatementPage = () => { onClick={() => void navigate('../../', { searchParams: new URLSearchParams('tab=statements') })} > @@ -70,7 +72,7 @@ export const StatementPage = () => { @@ -78,8 +80,20 @@ export const StatementPage = () => { { /> {item.subscription.credit && item.subscription.credit.amount.amount > 0 ? ( ) : null} diff --git a/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx b/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx index 836f85de03a..33378eaebca 100644 --- a/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx +++ b/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx @@ -3,9 +3,22 @@ import React from 'react'; import { Pagination } from '@/ui/elements/Pagination'; -import { useStatements } from '../../../ui/contexts'; +import { useStatements, useSubscriberTypeContext } from '../../../ui/contexts'; import type { LocalizationKey } from '../../customizables'; -import { Col, descriptors, Flex, Spinner, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; +import { + Col, + descriptors, + Flex, + localizationKeys, + Spinner, + Table, + Tbody, + Td, + Text, + Th, + Thead, + Tr, +} from '../../customizables'; import { useRouter } from '../../router'; import type { PropsOfComponent } from '../../styledSystem'; import { truncateWithEndVisible } from '../../utils/truncateTextWithEndVisible'; @@ -16,6 +29,8 @@ import { truncateWithEndVisible } from '../../utils/truncateTextWithEndVisible'; export const StatementsList = () => { const { data: statements, isLoading } = useStatements(); + const subscriberType = useSubscriberTypeContext(); + const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; return ( { pageCount={1} itemsPerPage={10} isLoading={isLoading} - emptyStateLocalizationKey='No statements to display' - headers={['Date', 'Amount']} + emptyStateLocalizationKey={localizationKeys(`${localizationRoot}.billingPage.statementsSection.empty`)} + headers={[ + localizationKeys(`${localizationRoot}.billingPage.statementsSection.tableHeaders__date`), + localizationKeys(`${localizationRoot}.billingPage.statementsSection.tableHeaders__amount`), + ]} rows={(statements?.data || []).map(i => ( has({ permission: 'org:sys_billing:manage' }) || subscriberType === 'user', @@ -79,9 +80,21 @@ export function SubscriptionsList({ - - - + diff --git a/packages/clerk-js/src/ui/components/UserProfile/PlansPage.tsx b/packages/clerk-js/src/ui/components/UserProfile/PlansPage.tsx index 4acf49cb1c8..cf2ea151153 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/PlansPage.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/PlansPage.tsx @@ -19,7 +19,9 @@ const PlansPageInternal = () => { paddingBlockEnd: t.space.$4, })} > - void navigate('../', { searchParams: new URLSearchParams('tab=plans') })}> + void navigate('../', { searchParams: new URLSearchParams('tab=subscriptions') })} + > Date: Wed, 11 Jun 2025 10:39:34 -0700 Subject: [PATCH 02/10] misc --- .changeset/soft-toys-flow.md | 7 +++++++ packages/clerk-js/bundlewatch.config.json | 4 ++-- packages/clerk-js/src/core/resources/CommerceSettings.ts | 9 ++++----- .../ui/components/PaymentSources/AddPaymentSource.tsx | 1 - .../src/ui/components/Statements/StatementPage.tsx | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 .changeset/soft-toys-flow.md diff --git a/.changeset/soft-toys-flow.md b/.changeset/soft-toys-flow.md new file mode 100644 index 00000000000..376ce55a195 --- /dev/null +++ b/.changeset/soft-toys-flow.md @@ -0,0 +1,7 @@ +--- +'@clerk/localizations': patch +'@clerk/clerk-js': patch +'@clerk/types': patch +--- + +Add localizations for some commerce strings, general cleanups diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index a446de1a2ba..d21030aa042 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -4,7 +4,7 @@ { "path": "./dist/clerk.browser.js", "maxSize": "69.2KB" }, { "path": "./dist/clerk.legacy.browser.js", "maxSize": "113KB" }, { "path": "./dist/clerk.headless*.js", "maxSize": "53KB" }, - { "path": "./dist/ui-common*.js", "maxSize": "106.3KB" }, + { "path": "./dist/ui-common*.js", "maxSize": "106.5KB" }, { "path": "./dist/vendors*.js", "maxSize": "40.2KB" }, { "path": "./dist/coinbase*.js", "maxSize": "38KB" }, { "path": "./dist/createorganization*.js", "maxSize": "5KB" }, @@ -22,7 +22,7 @@ { "path": "./dist/keylessPrompt*.js", "maxSize": "6.5KB" }, { "path": "./dist/pricingTable*.js", "maxSize": "4.02KB" }, { "path": "./dist/checkout*.js", "maxSize": "7.25KB" }, - { "path": "./dist/paymentSources*.js", "maxSize": "9.15KB" }, + { "path": "./dist/paymentSources*.js", "maxSize": "9.17KB" }, { "path": "./dist/up-billing-page*.js", "maxSize": "3.0KB" }, { "path": "./dist/op-billing-page*.js", "maxSize": "3.0KB" }, { "path": "./dist/sessionTasks*.js", "maxSize": "1KB" } diff --git a/packages/clerk-js/src/core/resources/CommerceSettings.ts b/packages/clerk-js/src/core/resources/CommerceSettings.ts index bce56538fb1..104ce856c43 100644 --- a/packages/clerk-js/src/core/resources/CommerceSettings.ts +++ b/packages/clerk-js/src/core/resources/CommerceSettings.ts @@ -23,11 +23,10 @@ export class CommerceSettings extends BaseResource implements CommerceSettingsRe return this; } - // TODO(@commerce): Remove `?.` once we launch. - this.billing.stripePublishableKey = data?.billing?.stripe_publishable_key || ''; - this.billing.enabled = data?.billing?.enabled || false; - this.billing.hasPaidUserPlans = data?.billing?.has_paid_user_plans || false; - this.billing.hasPaidOrgPlans = data?.billing?.has_paid_org_plans || false; + this.billing.stripePublishableKey = data.billing.stripe_publishable_key || ''; + this.billing.enabled = data.billing.enabled || false; + this.billing.hasPaidUserPlans = data.billing.has_paid_user_plans || false; + this.billing.hasPaidOrgPlans = data.billing.has_paid_org_plans || false; return this; } diff --git a/packages/clerk-js/src/ui/components/PaymentSources/AddPaymentSource.tsx b/packages/clerk-js/src/ui/components/PaymentSources/AddPaymentSource.tsx index b7ead9ea3df..2c8eba55dd8 100644 --- a/packages/clerk-js/src/ui/components/PaymentSources/AddPaymentSource.tsx +++ b/packages/clerk-js/src/ui/components/PaymentSources/AddPaymentSource.tsx @@ -290,7 +290,6 @@ const AddPaymentSourceForm = ({ children }: PropsWithChildren) => { type: 'tabs', defaultCollapsed: false, }, - // TODO(@COMMERCE): Should this be fetched from the fapi? paymentMethodOrder: paymentMethodOrder || ['card'], applePay: checkout ? { diff --git a/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx b/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx index 1be4403fce5..cf24b2ea0d0 100644 --- a/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx +++ b/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx @@ -116,7 +116,7 @@ export const StatementPage = () => { ))} From 69c40a70c23d8cd448d8375e116ba7a79047f3a0 Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Wed, 11 Jun 2025 11:26:04 -0700 Subject: [PATCH 03/10] new hook for subscriberType localization root --- .../components/PaymentAttempts/PaymentAttemptPage.tsx | 6 +++--- .../components/PaymentAttempts/PaymentAttemptsList.tsx | 5 ++--- .../ui/components/PaymentSources/AddPaymentSource.tsx | 5 ++--- .../ui/components/PaymentSources/PaymentSources.tsx | 10 +++++----- .../src/ui/components/Statements/StatementPage.tsx | 5 ++--- .../src/ui/components/Statements/StatementsList.tsx | 5 ++--- .../ui/components/Subscriptions/SubscriptionsList.tsx | 9 +++++++-- .../src/ui/contexts/components/SubscriberType.ts | 5 +++++ 8 files changed, 28 insertions(+), 22 deletions(-) diff --git a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx index 8a6f5a16b7a..c51b30630ef 100644 --- a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx +++ b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx @@ -1,7 +1,8 @@ import { Header } from '@/ui/elements/Header'; import { LineItems } from '@/ui/elements/LineItems'; -import { usePaymentAttemptsContext, useStatements, useSubscriberTypeContext } from '../../contexts'; +import { usePaymentAttemptsContext, useStatements } from '../../contexts'; +import { useSubscriberTypeLocalizationRoot } from '../../contexts/components'; import { Badge, Box, @@ -23,8 +24,7 @@ export const PaymentAttemptPage = () => { const { params, navigate } = useRouter(); const { isLoading } = useStatements(); const { getPaymentAttemptById } = usePaymentAttemptsContext(); - const subscriberType = useSubscriberTypeContext(); - const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; + const localizationRoot = useSubscriberTypeLocalizationRoot(); const paymentAttempt = params.paymentAttemptId ? getPaymentAttemptById(params.paymentAttemptId) : null; const subscriptionItem = paymentAttempt?.subscriptionItem; diff --git a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx index 42a26f939da..703f646b87d 100644 --- a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx +++ b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { Pagination } from '@/ui/elements/Pagination'; -import { usePaymentAttempts, useSubscriberTypeContext } from '../../../ui/contexts'; +import { usePaymentAttempts, useSubscriberTypeLocalizationRoot } from '../../../ui/contexts'; import type { LocalizationKey } from '../../customizables'; import { Badge, @@ -30,8 +30,7 @@ import { truncateWithEndVisible } from '../../utils/truncateTextWithEndVisible'; export const PaymentAttemptsList = () => { const { data: paymentAttempts, isLoading } = usePaymentAttempts(); - const subscriberType = useSubscriberTypeContext(); - const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; + const localizationRoot = useSubscriberTypeLocalizationRoot(); return ( { const elements = useElements(); const { displayConfig } = useEnvironment(); const { t } = useLocalizations(); - const subscriberType = useSubscriberTypeContext(); - const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; + const localizationRoot = useSubscriberTypeLocalizationRoot(); const onSubmit = async (e: React.FormEvent) => { e.preventDefault(); diff --git a/packages/clerk-js/src/ui/components/PaymentSources/PaymentSources.tsx b/packages/clerk-js/src/ui/components/PaymentSources/PaymentSources.tsx index 3ed7d83cb4a..bab6edafdd6 100644 --- a/packages/clerk-js/src/ui/components/PaymentSources/PaymentSources.tsx +++ b/packages/clerk-js/src/ui/components/PaymentSources/PaymentSources.tsx @@ -10,7 +10,7 @@ import { ThreeDotsMenu } from '@/ui/elements/ThreeDotsMenu'; import { RemoveResourceForm } from '../../common'; import { DevOnly } from '../../common/DevOnly'; -import { usePaymentSources, useSubscriberTypeContext } from '../../contexts'; +import { usePaymentSources, useSubscriberTypeContext, useSubscriberTypeLocalizationRoot } from '../../contexts'; import { localizationKeys } from '../../customizables'; import { Action } from '../../elements/Action'; import { useActionContext } from '../../elements/Action/ActionRoot'; @@ -23,7 +23,7 @@ const AddScreen = withCardStateProvider(({ onSuccess }: { onSuccess: () => void const { close } = useActionContext(); const clerk = useClerk(); const subscriberType = useSubscriberTypeContext(); - const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; + const localizationRoot = useSubscriberTypeLocalizationRoot(); const onAddPaymentSourceSuccess = async (context: { stripeSetupIntent?: SetupIntent }) => { const resource = subscriberType === 'org' ? clerk?.organization : clerk.user; @@ -65,7 +65,7 @@ const RemoveScreen = ({ const card = useCardState(); const subscriberType = useSubscriberTypeContext(); const { organization } = useOrganization(); - const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; + const localizationRoot = useSubscriberTypeLocalizationRoot(); const ref = useRef( `${paymentSource.paymentMethod === 'card' ? paymentSource.cardType : paymentSource.paymentMethod} ${paymentSource.paymentMethod === 'card' ? `⋯ ${paymentSource.last4}` : '-'}`, ); @@ -111,7 +111,7 @@ const RemoveScreen = ({ export const PaymentSources = withCardStateProvider(() => { const clerk = useClerk(); const subscriberType = useSubscriberTypeContext(); - const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; + const localizationRoot = useSubscriberTypeLocalizationRoot(); const resource = subscriberType === 'org' ? clerk?.organization : clerk.user; const { data, isLoading, mutate: mutatePaymentSources } = usePaymentSources(); @@ -200,7 +200,7 @@ const PaymentSourceMenu = ({ const card = useCardState(); const { organization } = useOrganization(); const subscriberType = useSubscriberTypeContext(); - const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; + const localizationRoot = useSubscriberTypeLocalizationRoot(); const actions = [ { diff --git a/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx b/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx index cf24b2ea0d0..565097a8b78 100644 --- a/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx +++ b/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx @@ -1,6 +1,6 @@ import { Header } from '@/ui/elements/Header'; -import { useStatements, useStatementsContext, useSubscriberTypeContext } from '../../contexts'; +import { useStatements, useStatementsContext, useSubscriberTypeLocalizationRoot } from '../../contexts'; import { Box, descriptors, localizationKeys, Spinner, Text, useLocalizations } from '../../customizables'; import { Plus, RotateLeftRight } from '../../icons'; import { useRouter } from '../../router'; @@ -10,9 +10,8 @@ export const StatementPage = () => { const { params, navigate } = useRouter(); const { isLoading } = useStatements(); const { getStatementById } = useStatementsContext(); - const subscriberType = useSubscriberTypeContext(); + const localizationRoot = useSubscriberTypeLocalizationRoot(); const { t } = useLocalizations(); - const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; const statement = params.statementId ? getStatementById(params.statementId) : null; if (isLoading) { diff --git a/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx b/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx index 33378eaebca..2ac8d5c5e88 100644 --- a/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx +++ b/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { Pagination } from '@/ui/elements/Pagination'; -import { useStatements, useSubscriberTypeContext } from '../../../ui/contexts'; +import { useStatements, useSubscriberTypeLocalizationRoot } from '../../../ui/contexts'; import type { LocalizationKey } from '../../customizables'; import { Col, @@ -29,8 +29,7 @@ import { truncateWithEndVisible } from '../../utils/truncateTextWithEndVisible'; export const StatementsList = () => { const { data: statements, isLoading } = useStatements(); - const subscriberType = useSubscriberTypeContext(); - const localizationRoot = subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; + const localizationRoot = useSubscriberTypeLocalizationRoot(); return ( has({ permission: 'org:sys_billing:manage' }) || subscriberType === 'user', diff --git a/packages/clerk-js/src/ui/contexts/components/SubscriberType.ts b/packages/clerk-js/src/ui/contexts/components/SubscriberType.ts index 4e4e7db51c4..0653ece4067 100644 --- a/packages/clerk-js/src/ui/contexts/components/SubscriberType.ts +++ b/packages/clerk-js/src/ui/contexts/components/SubscriberType.ts @@ -4,3 +4,8 @@ const DEFAUlT = 'user'; export const SubscriberTypeContext = createContext<'user' | 'org'>(DEFAUlT); export const useSubscriberTypeContext = () => useContext(SubscriberTypeContext) || DEFAUlT; + +export const useSubscriberTypeLocalizationRoot = () => { + const subscriberType = useSubscriberTypeContext(); + return subscriberType === 'user' ? 'userProfile' : 'organizationProfile'; +}; From 561c74bd606ac12148fbbfd7f475373ec858c710 Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Wed, 11 Jun 2025 11:36:45 -0700 Subject: [PATCH 04/10] bundle bump --- packages/clerk-js/bundlewatch.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index d21030aa042..4c99bd4aafd 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -4,7 +4,7 @@ { "path": "./dist/clerk.browser.js", "maxSize": "69.2KB" }, { "path": "./dist/clerk.legacy.browser.js", "maxSize": "113KB" }, { "path": "./dist/clerk.headless*.js", "maxSize": "53KB" }, - { "path": "./dist/ui-common*.js", "maxSize": "106.5KB" }, + { "path": "./dist/ui-common*.js", "maxSize": "107KB" }, { "path": "./dist/vendors*.js", "maxSize": "40.2KB" }, { "path": "./dist/coinbase*.js", "maxSize": "38KB" }, { "path": "./dist/createorganization*.js", "maxSize": "5KB" }, From cf7483a5b06deebc7dac5fd321fc01e3673b8737 Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Thu, 12 Jun 2025 11:15:50 -0700 Subject: [PATCH 05/10] feedback --- .../core/resources/CommercePaymentSource.ts | 2 +- .../PaymentAttempts/PaymentAttemptPage.tsx | 12 ++++--- .../PaymentAttempts/PaymentAttemptsList.tsx | 18 ++++------- .../PaymentSources/AddPaymentSource.tsx | 2 +- .../ui/components/Statements/Statement.tsx | 4 +-- .../components/Statements/StatementsList.tsx | 4 +-- .../Subscriptions/SubscriptionsList.tsx | 6 ++-- packages/clerk-js/src/ui/utils/index.ts | 1 + packages/localizations/src/en-US.ts | 32 +++++++++---------- packages/types/src/localization.ts | 32 +++++++++---------- 10 files changed, 54 insertions(+), 59 deletions(-) diff --git a/packages/clerk-js/src/core/resources/CommercePaymentSource.ts b/packages/clerk-js/src/core/resources/CommercePaymentSource.ts index 22dfa9aed1f..8a2583ac66d 100644 --- a/packages/clerk-js/src/core/resources/CommercePaymentSource.ts +++ b/packages/clerk-js/src/core/resources/CommercePaymentSource.ts @@ -88,7 +88,7 @@ export class CommerceInitializedPaymentSource extends BaseResource implements Co this.externalClientSecret = data.external_client_secret; this.externalGatewayId = data.external_gateway_id; - this.paymentMethodOrder = data.payment_method_order; + this.paymentMethodOrder = data.payment_method_order ?? ['card']; return this; } } diff --git a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx index c51b30630ef..8172491d415 100644 --- a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx +++ b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx @@ -18,7 +18,7 @@ import { import { useClipboard } from '../../hooks'; import { Check, Copy } from '../../icons'; import { useRouter } from '../../router'; -import { truncateWithEndVisible } from '../../utils/truncateTextWithEndVisible'; +import { formatDate, truncateWithEndVisible } from '../../utils'; export const PaymentAttemptPage = () => { const { params, navigate } = useRouter(); @@ -92,9 +92,10 @@ export const PaymentAttemptPage = () => { ({ @@ -192,8 +193,9 @@ export const PaymentAttemptPage = () => { variant='caption' colorScheme='secondary' elementDescriptor={descriptors.paymentAttemptFooterCurrency} + sx={{ textTransform: 'uppercase' }} > - USD + {paymentAttempt.amount.currency} { isLoading={isLoading} emptyStateLocalizationKey={localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.empty`)} headers={[ - localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.tableHeaders__date`), - localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.tableHeaders__amount`), - localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.tableHeaders__status`), + localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.tableHeader__date`), + localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.tableHeader__amount`), + localizationKeys(`${localizationRoot}.billingPage.paymentHistorySection.tableHeader__status`), ]} rows={(paymentAttempts?.data || []).map(i => ( - - {new Date(paidAt || failedAt || updatedAt).toLocaleString('en-US', { - month: 'long', - day: 'numeric', - year: 'numeric', - })} - + {formatDate(new Date(paidAt || failedAt || updatedAt), 'long')} { type: 'tabs', defaultCollapsed: false, }, - paymentMethodOrder: paymentMethodOrder || ['card'], + paymentMethodOrder: paymentMethodOrder, applePay: checkout ? { recurringPaymentRequest: { diff --git a/packages/clerk-js/src/ui/components/Statements/Statement.tsx b/packages/clerk-js/src/ui/components/Statements/Statement.tsx index 4be1803bda4..e98668d2c93 100644 --- a/packages/clerk-js/src/ui/components/Statements/Statement.tsx +++ b/packages/clerk-js/src/ui/components/Statements/Statement.tsx @@ -212,9 +212,7 @@ function SectionContentDetailsHeader({ colorScheme='secondary' elementDescriptor={descriptors.statementSectionContentDetailsHeaderDescription} localizationKey={description} - sx={() => ({ - textTransform: 'lowercase', - })} + sx={{ textTransform: 'lowercase' }} /> { isLoading={isLoading} emptyStateLocalizationKey={localizationKeys(`${localizationRoot}.billingPage.statementsSection.empty`)} headers={[ - localizationKeys(`${localizationRoot}.billingPage.statementsSection.tableHeaders__date`), - localizationKeys(`${localizationRoot}.billingPage.statementsSection.tableHeaders__amount`), + localizationKeys(`${localizationRoot}.billingPage.statementsSection.tableHeader__date`), + localizationKeys(`${localizationRoot}.billingPage.statementsSection.tableHeader__amount`), ]} rows={(statements?.data || []).map(i => ( diff --git a/packages/clerk-js/src/ui/utils/index.ts b/packages/clerk-js/src/ui/utils/index.ts index 55f1d9cb815..3d801b95b71 100644 --- a/packages/clerk-js/src/ui/utils/index.ts +++ b/packages/clerk-js/src/ui/utils/index.ts @@ -29,3 +29,4 @@ export * from './web3CallbackErrorHandler'; export * from './originPrefersPopup'; export * from './normalizeColorString'; export * from './formatDate'; +export * from './truncateTextWithEndVisible'; diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 62c173826fa..61c657962ec 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -192,9 +192,9 @@ export const enUS: LocalizationResource = { billingPage: { paymentHistorySection: { empty: 'No payment history', - tableHeaders__date: 'Date', - tableHeaders__amount: 'Amount', - tableHeaders__status: 'Status', + tableHeader__date: 'Date', + tableHeader__amount: 'Amount', + tableHeader__status: 'Status', notFound: 'Payment attempt not found', }, paymentSourcesSection: { @@ -227,17 +227,17 @@ export const enUS: LocalizationResource = { itemCaption__proratedCredit: 'Prorated credit for partial usage of previous subscription', itemCaption__subscribedAndPaidForPlan: 'Subscribed and paid for {{plan}} {{period}} plan', notFound: 'Statement not found', - tableHeaders__date: 'Date', - tableHeaders__amount: 'Amount', + tableHeader__date: 'Date', + tableHeader__amount: 'Amount', title: 'Statements', totalPaid: 'Total paid', }, subscriptionsListSection: { actionLabel__newSubscription: 'Subscribe to a plan', actionLabel__switchPlan: 'Switch plans', - tableHeaders__plan: 'Plan', - tableHeaders__startDate: 'Start date', - tableHeaders__edit: 'Edit', + tableHeader__plan: 'Plan', + tableHeader__startDate: 'Start date', + tableHeader__edit: 'Edit', title: 'Subscription', }, subscriptionsSection: { @@ -841,9 +841,9 @@ export const enUS: LocalizationResource = { billingPage: { paymentHistorySection: { empty: 'No payment history', - tableHeaders__date: 'Date', - tableHeaders__amount: 'Amount', - tableHeaders__status: 'Status', + tableHeader__date: 'Date', + tableHeader__amount: 'Amount', + tableHeader__status: 'Status', notFound: 'Payment attempt not found', }, paymentSourcesSection: { @@ -876,17 +876,17 @@ export const enUS: LocalizationResource = { itemCaption__proratedCredit: 'Prorated credit for partial usage of previous subscription', itemCaption__subscribedAndPaidForPlan: 'Subscribed and paid for {{plan}} {{period}} plan', notFound: 'Statement not found', - tableHeaders__date: 'Date', - tableHeaders__amount: 'Amount', + tableHeader__date: 'Date', + tableHeader__amount: 'Amount', title: 'Statements', totalPaid: 'Total paid', }, subscriptionsListSection: { actionLabel__newSubscription: 'Subscribe to a plan', actionLabel__switchPlan: 'Switch plans', - tableHeaders__plan: 'Plan', - tableHeaders__startDate: 'Start date', - tableHeaders__edit: 'Edit', + tableHeader__plan: 'Plan', + tableHeader__startDate: 'Start date', + tableHeader__edit: 'Edit', title: 'Subscription', }, subscriptionsSection: { diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts index b19389194b0..7b932c94ff3 100644 --- a/packages/types/src/localization.ts +++ b/packages/types/src/localization.ts @@ -768,8 +768,8 @@ type _LocalizationResource = { itemCaption__proratedCredit: LocalizationValue; itemCaption__subscribedAndPaidForPlan: LocalizationValue; notFound: LocalizationValue; - tableHeaders__date: LocalizationValue; - tableHeaders__amount: LocalizationValue; + tableHeader__date: LocalizationValue; + tableHeader__amount: LocalizationValue; title: LocalizationValue; totalPaid: LocalizationValue; }; @@ -777,9 +777,9 @@ type _LocalizationResource = { title: LocalizationValue; }; subscriptionsListSection: { - tableHeaders__plan: LocalizationValue; - tableHeaders__startDate: LocalizationValue; - tableHeaders__edit: LocalizationValue; + tableHeader__plan: LocalizationValue; + tableHeader__startDate: LocalizationValue; + tableHeader__edit: LocalizationValue; title: LocalizationValue; actionLabel__newSubscription: LocalizationValue; actionLabel__switchPlan: LocalizationValue; @@ -787,9 +787,9 @@ type _LocalizationResource = { paymentHistorySection: { empty: LocalizationValue; notFound: LocalizationValue; - tableHeaders__date: LocalizationValue; - tableHeaders__amount: LocalizationValue; - tableHeaders__status: LocalizationValue; + tableHeader__date: LocalizationValue; + tableHeader__amount: LocalizationValue; + tableHeader__status: LocalizationValue; }; paymentSourcesSection: { title: LocalizationValue; @@ -994,8 +994,8 @@ type _LocalizationResource = { itemCaption__proratedCredit: LocalizationValue; itemCaption__subscribedAndPaidForPlan: LocalizationValue; notFound: LocalizationValue; - tableHeaders__date: LocalizationValue; - tableHeaders__amount: LocalizationValue; + tableHeader__date: LocalizationValue; + tableHeader__amount: LocalizationValue; title: LocalizationValue; totalPaid: LocalizationValue; }; @@ -1003,9 +1003,9 @@ type _LocalizationResource = { title: LocalizationValue; }; subscriptionsListSection: { - tableHeaders__plan: LocalizationValue; - tableHeaders__startDate: LocalizationValue; - tableHeaders__edit: LocalizationValue; + tableHeader__plan: LocalizationValue; + tableHeader__startDate: LocalizationValue; + tableHeader__edit: LocalizationValue; title: LocalizationValue; actionLabel__newSubscription: LocalizationValue; actionLabel__switchPlan: LocalizationValue; @@ -1013,9 +1013,9 @@ type _LocalizationResource = { paymentHistorySection: { empty: LocalizationValue; notFound: LocalizationValue; - tableHeaders__date: LocalizationValue; - tableHeaders__amount: LocalizationValue; - tableHeaders__status: LocalizationValue; + tableHeader__date: LocalizationValue; + tableHeader__amount: LocalizationValue; + tableHeader__status: LocalizationValue; }; paymentSourcesSection: { title: LocalizationValue; From dcbf62ab743e77c34c4169d543412f2ecaee9224 Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Thu, 12 Jun 2025 12:01:31 -0700 Subject: [PATCH 06/10] DataTable shared component --- .../PaymentAttempts/PaymentAttemptPage.tsx | 258 +++++++++--------- .../PaymentAttempts/PaymentAttemptsList.tsx | 137 +--------- .../components/Statements/StatementPage.tsx | 141 +++++----- .../components/Statements/StatementsList.tsx | 136 +-------- .../clerk-js/src/ui/elements/DataTable.tsx | 122 +++++++++ 5 files changed, 327 insertions(+), 467 deletions(-) create mode 100644 packages/clerk-js/src/ui/elements/DataTable.tsx diff --git a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx index 8172491d415..9056c553ffa 100644 --- a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx +++ b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx @@ -41,12 +41,6 @@ export const PaymentAttemptPage = () => { ); } - if (!paymentAttempt) { - return ( - - ); - } - return ( <> { /> - - ({ - borderWidth: t.borderWidths.$normal, - borderStyle: t.borderStyles.$solid, - borderColor: t.colors.$neutralAlpha100, - borderRadius: t.radii.$lg, - overflow: 'clip', - })} - > + {!paymentAttempt ? ( + + ) : ( ({ - padding: t.space.$4, - background: t.colors.$neutralAlpha25, - display: 'flex', - justifyContent: 'space-between', - alignItems: 'flex-start', + borderWidth: t.borderWidths.$normal, + borderStyle: t.borderStyles.$solid, + borderColor: t.colors.$neutralAlpha100, + borderRadius: t.radii.$lg, + overflow: 'clip', })} > - - - ({ - display: 'flex', - alignItems: 'center', - gap: t.space.$0x25, - color: t.colors.$colorTextSecondary, - })} - > - ({ + padding: t.space.$4, + background: t.colors.$neutralAlpha25, + display: 'flex', + justifyContent: 'space-between', + alignItems: 'flex-start', + })} + > + + - ({ + display: 'flex', + alignItems: 'center', + gap: t.space.$0x25, + color: t.colors.$colorTextSecondary, + })} > - {truncateWithEndVisible(paymentAttempt.id)} - + + + {truncateWithEndVisible(paymentAttempt.id)} + + - - + {paymentAttempt.status} + + + ({ + padding: t.space.$4, + })} > - {paymentAttempt.status} - - - ({ - padding: t.space.$4, - })} - > - {subscriptionItem && ( - - - - - - - - - - {subscriptionItem.credit && subscriptionItem.credit.amount.amount > 0 && ( - - + {subscriptionItem && ( + + + - )} - - )} - - ({ - paddingInline: t.space.$4, - paddingBlock: t.space.$3, - background: t.colors.$neutralAlpha25, - borderBlockStartWidth: t.borderWidths.$normal, - borderBlockStartStyle: t.borderStyles.$solid, - borderBlockStartColor: t.colors.$neutralAlpha100, - display: 'flex', - justifyContent: 'space-between', - })} - > - - + + + + {subscriptionItem.credit && subscriptionItem.credit.amount.amount > 0 && ( + + + + + )} + + )} + + ({ + paddingInline: t.space.$4, + paddingBlock: t.space.$3, + background: t.colors.$neutralAlpha25, + borderBlockStartWidth: t.borderWidths.$normal, + borderBlockStartStyle: t.borderStyles.$solid, + borderBlockStartColor: t.colors.$neutralAlpha100, display: 'flex', - alignItems: 'center', - gap: t.space.$2x5, + justifyContent: 'space-between', })} > - - {paymentAttempt.amount.currency} - + ({ + display: 'flex', + alignItems: 'center', + gap: t.space.$2x5, + })} > - {paymentAttempt.amount.currencySymbol} - {paymentAttempt.amount.amountFormatted} - - + + {paymentAttempt.amount.currency} + + + {paymentAttempt.amount.currencySymbol} + {paymentAttempt.amount.amountFormatted} + + + - + )} ); }; diff --git a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx index c718d8a4b54..42f2bf693d9 100644 --- a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx +++ b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx @@ -1,27 +1,10 @@ import type { CommercePaymentResource } from '@clerk/types'; -import React from 'react'; -import { Pagination } from '@/ui/elements/Pagination'; +import { DataTable, DataTableRow } from '@/ui/elements/DataTable'; import { usePaymentAttempts, useSubscriberTypeLocalizationRoot } from '../../contexts'; -import type { LocalizationKey } from '../../customizables'; -import { - Badge, - Col, - descriptors, - Flex, - localizationKeys, - Spinner, - Table, - Tbody, - Td, - Text, - Th, - Thead, - Tr, -} from '../../customizables'; +import { Badge, localizationKeys, Td, Text } from '../../customizables'; import { useRouter } from '../../router'; -import type { PropsOfComponent } from '../../styledSystem'; import { formatDate, truncateWithEndVisible } from '../../utils'; /* ------------------------------------------------------------------------------------------------- @@ -100,119 +83,3 @@ const PaymentAttemptsListRow = ({ paymentAttempt }: { paymentAttempt: CommercePa ); }; - -/* ------------------------------------------------------------------------------------------------- - * DataTable - * -----------------------------------------------------------------------------------------------*/ - -type DataTableProps = { - headers: (LocalizationKey | string)[]; - rows: React.ReactNode[]; - isLoading?: boolean; - page: number; - onPageChange: (page: number) => void; - itemCount: number; - emptyStateLocalizationKey: LocalizationKey | string; - pageCount: number; - itemsPerPage: number; -}; - -const DataTable = (props: DataTableProps) => { - const { - headers, - page, - onPageChange, - rows, - isLoading, - itemCount, - itemsPerPage, - pageCount, - emptyStateLocalizationKey, - } = props; - - const startingRow = itemCount > 0 ? Math.max(0, (page - 1) * itemsPerPage) + 1 : 0; - const endingRow = Math.min(page * itemsPerPage, itemCount); - - return ( - - ({ overflowX: 'auto', padding: t.space.$1 })}> -
PlanStart dateEdit + +
- - - {headers.map((h, index) => ( - - - - {isLoading ? ( - - - - ) : !rows.length ? ( - - ) : ( - rows - )} - -
- ))} -
- -
- - {pageCount > 1 && ( - - )} - - ); -}; - -const DataTableEmptyRow = (props: { localizationKey: LocalizationKey | string }) => { - return ( - - - - - - ); -}; - -const DataTableRow = (props: PropsOfComponent) => { - return ( - ({ ':hover': { backgroundColor: t.colors.$neutralAlpha50 } })} - /> - ); -}; diff --git a/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx b/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx index 565097a8b78..3e11c7ab307 100644 --- a/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx +++ b/packages/clerk-js/src/ui/components/Statements/StatementPage.tsx @@ -26,10 +26,6 @@ export const StatementPage = () => { ); } - if (!statement) { - return ; - } - return ( <> { />
- - - - {statement.groups.map(group => ( - - - - {group.items.map(item => ( - - - - + + + {statement.groups.map(group => ( + + + + {group.items.map(item => ( + + - {item.subscription.credit && item.subscription.credit.amount.amount > 0 ? ( + - ) : null} - - - ))} - - - ))} - - - + {item.subscription.credit && item.subscription.credit.amount.amount > 0 ? ( + + ) : null} + + + ))} + + + ))} + + + + )} ); }; diff --git a/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx b/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx index c31fdf4f381..d4908fe25cd 100644 --- a/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx +++ b/packages/clerk-js/src/ui/components/Statements/StatementsList.tsx @@ -1,26 +1,10 @@ import type { CommerceStatementResource } from '@clerk/types'; -import React from 'react'; -import { Pagination } from '@/ui/elements/Pagination'; +import { DataTable, DataTableRow } from '@/ui/elements/DataTable'; import { useStatements, useSubscriberTypeLocalizationRoot } from '../../../ui/contexts'; -import type { LocalizationKey } from '../../customizables'; -import { - Col, - descriptors, - Flex, - localizationKeys, - Spinner, - Table, - Tbody, - Td, - Text, - Th, - Thead, - Tr, -} from '../../customizables'; +import { localizationKeys, Td, Text } from '../../customizables'; import { useRouter } from '../../router'; -import type { PropsOfComponent } from '../../styledSystem'; import { truncateWithEndVisible } from '../../utils/truncateTextWithEndVisible'; /* ------------------------------------------------------------------------------------------------- @@ -96,119 +80,3 @@ const StatementsListRow = ({ statement }: { statement: CommerceStatementResource ); }; - -/* ------------------------------------------------------------------------------------------------- - * DataTable - * -----------------------------------------------------------------------------------------------*/ - -type DataTableProps = { - headers: (LocalizationKey | string)[]; - rows: React.ReactNode[]; - isLoading?: boolean; - page: number; - onPageChange: (page: number) => void; - itemCount: number; - emptyStateLocalizationKey: LocalizationKey | string; - pageCount: number; - itemsPerPage: number; -}; - -const DataTable = (props: DataTableProps) => { - const { - headers, - page, - onPageChange, - rows, - isLoading, - itemCount, - itemsPerPage, - pageCount, - emptyStateLocalizationKey, - } = props; - - const startingRow = itemCount > 0 ? Math.max(0, (page - 1) * itemsPerPage) + 1 : 0; - const endingRow = Math.min(page * itemsPerPage, itemCount); - - return ( - - ({ overflowX: 'auto', padding: t.space.$1 })}> - - - - {headers.map((h, index) => ( - - - - {isLoading ? ( - - - - ) : !rows.length ? ( - - ) : ( - rows - )} - -
- ))} -
- -
-
- {pageCount > 1 && ( - - )} - - ); -}; - -const DataTableEmptyRow = (props: { localizationKey: LocalizationKey | string }) => { - return ( - - - - - - ); -}; - -const DataTableRow = (props: PropsOfComponent) => { - return ( - ({ ':hover': { backgroundColor: t.colors.$neutralAlpha50 } })} - /> - ); -}; diff --git a/packages/clerk-js/src/ui/elements/DataTable.tsx b/packages/clerk-js/src/ui/elements/DataTable.tsx new file mode 100644 index 00000000000..3cd5a35060f --- /dev/null +++ b/packages/clerk-js/src/ui/elements/DataTable.tsx @@ -0,0 +1,122 @@ +import React from 'react'; + +import type { LocalizationKey } from '../customizables'; +import { Col, descriptors, Flex, Spinner, Table, Tbody, Td, Text, Th, Thead, Tr } from '../customizables'; +import type { PropsOfComponent } from '../styledSystem'; +import { Pagination } from './Pagination'; + +/* ------------------------------------------------------------------------------------------------- + * DataTable + * -----------------------------------------------------------------------------------------------*/ + +export type DataTableProps = { + headers: (LocalizationKey | string)[]; + rows: React.ReactNode[]; + isLoading?: boolean; + page: number; + onPageChange: (page: number) => void; + itemCount: number; + emptyStateLocalizationKey: LocalizationKey | string; + pageCount: number; + itemsPerPage: number; +}; + +export const DataTable = (props: DataTableProps) => { + const { + headers, + page, + onPageChange, + rows, + isLoading, + itemCount, + itemsPerPage, + pageCount, + emptyStateLocalizationKey, + } = props; + + const startingRow = itemCount > 0 ? Math.max(0, (page - 1) * itemsPerPage) + 1 : 0; + const endingRow = Math.min(page * itemsPerPage, itemCount); + + return ( + + ({ overflowX: 'auto', padding: t.space.$1 })}> + + + + {headers.map((h, index) => ( + + + + {isLoading ? ( + + + + ) : !rows.length ? ( + + ) : ( + rows + )} + +
+ ))} +
+ +
+
+ {pageCount > 1 && ( + + )} + + ); +}; + +export const DataTableEmptyRow = (props: { localizationKey: LocalizationKey | string }) => { + return ( + + + + + + ); +}; + +export const DataTableRow = (props: PropsOfComponent) => { + return ( + ({ ':hover': { backgroundColor: t.colors.$neutralAlpha50 } })} + /> + ); +}; From d1d3530c1c9cc5f2609e273ca6954d6b7849f101 Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Thu, 12 Jun 2025 12:12:51 -0700 Subject: [PATCH 07/10] bundle bump --- packages/clerk-js/bundlewatch.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index 404c4321f08..365ebe317b3 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -4,7 +4,7 @@ { "path": "./dist/clerk.browser.js", "maxSize": "69.3KB" }, { "path": "./dist/clerk.legacy.browser.js", "maxSize": "113KB" }, { "path": "./dist/clerk.headless*.js", "maxSize": "53KB" }, - { "path": "./dist/ui-common*.js", "maxSize": "107KB" }, + { "path": "./dist/ui-common*.js", "maxSize": "107.5KB" }, { "path": "./dist/vendors*.js", "maxSize": "40.2KB" }, { "path": "./dist/coinbase*.js", "maxSize": "38KB" }, { "path": "./dist/createorganization*.js", "maxSize": "5KB" }, From d69ab4db89608bc15d13798191f8b9f3a169f6a3 Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Fri, 13 Jun 2025 09:44:30 -0700 Subject: [PATCH 08/10] date formatting --- .../clerk-js/src/core/resources/CommercePayment.ts | 12 ++++++------ .../src/core/resources/CommerceStatement.ts | 8 ++++---- .../PaymentAttempts/PaymentAttemptPage.tsx | 2 +- .../PaymentAttempts/PaymentAttemptsList.tsx | 2 +- .../components/PaymentSources/AddPaymentSource.tsx | 2 +- .../src/ui/components/Statements/Statement.tsx | 4 ++-- .../src/ui/components/Statements/StatementPage.tsx | 14 ++++---------- .../ui/components/Statements/StatementsList.tsx | 5 ++--- packages/clerk-js/src/ui/utils/formatDate.ts | 10 +++++++--- packages/types/src/commerce.ts | 10 +++++----- 10 files changed, 33 insertions(+), 36 deletions(-) diff --git a/packages/clerk-js/src/core/resources/CommercePayment.ts b/packages/clerk-js/src/core/resources/CommercePayment.ts index 91fa656dfa1..be3ae67eb8b 100644 --- a/packages/clerk-js/src/core/resources/CommercePayment.ts +++ b/packages/clerk-js/src/core/resources/CommercePayment.ts @@ -12,9 +12,9 @@ import { BaseResource, CommercePaymentSource, CommerceSubscription } from './int export class CommercePayment extends BaseResource implements CommercePaymentResource { id!: string; amount!: CommerceMoney; - failedAt?: number; - paidAt?: number; - updatedAt!: number; + failedAt?: Date; + paidAt?: Date; + updatedAt!: Date; paymentSource!: CommercePaymentSource; subscription!: CommerceSubscription; subscriptionItem!: CommerceSubscription; @@ -33,9 +33,9 @@ export class CommercePayment extends BaseResource implements CommercePaymentReso this.id = data.id; this.amount = commerceMoneyFromJSON(data.amount); - this.paidAt = data.paid_at; - this.failedAt = data.failed_at; - this.updatedAt = data.updated_at; + this.paidAt = data.paid_at ? new Date(data.paid_at) : undefined; + this.failedAt = data.failed_at ? new Date(data.failed_at) : undefined; + this.updatedAt = new Date(data.updated_at); this.paymentSource = new CommercePaymentSource(data.payment_source); this.subscription = new CommerceSubscription(data.subscription); this.subscriptionItem = new CommerceSubscription(data.subscription_item); diff --git a/packages/clerk-js/src/core/resources/CommerceStatement.ts b/packages/clerk-js/src/core/resources/CommerceStatement.ts index b51f6b71957..65baaecae5e 100644 --- a/packages/clerk-js/src/core/resources/CommerceStatement.ts +++ b/packages/clerk-js/src/core/resources/CommerceStatement.ts @@ -12,7 +12,7 @@ import { BaseResource, CommercePayment } from './internal'; export class CommerceStatement extends BaseResource implements CommerceStatementResource { id!: string; status!: CommerceStatementStatus; - timestamp!: number; + timestamp!: Date; totals!: CommerceStatementTotals; groups!: CommerceStatementGroup[]; @@ -28,7 +28,7 @@ export class CommerceStatement extends BaseResource implements CommerceStatement this.id = data.id; this.status = data.status; - this.timestamp = data.timestamp; + this.timestamp = new Date(data.timestamp); this.totals = commerceTotalsFromJSON(data.totals); this.groups = data.groups.map(group => new CommerceStatementGroup(group)); return this; @@ -37,7 +37,7 @@ export class CommerceStatement extends BaseResource implements CommerceStatement export class CommerceStatementGroup { id!: string; - timestamp!: number; + timestamp!: Date; items!: CommercePayment[]; constructor(data: CommerceStatementGroupJSON) { @@ -50,7 +50,7 @@ export class CommerceStatementGroup { } this.id = data.id; - this.timestamp = data.timestamp; + this.timestamp = new Date(data.timestamp); this.items = data.items.map(item => new CommercePayment(item)); return this; } diff --git a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx index 9056c553ffa..717c489f978 100644 --- a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx +++ b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx @@ -92,7 +92,7 @@ export const PaymentAttemptPage = () => { elementDescriptor={descriptors.paymentAttemptHeaderTitle} textVariant='h2' localizationKey={formatDate( - new Date(paymentAttempt.paidAt || paymentAttempt.failedAt || paymentAttempt.updatedAt), + paymentAttempt.paidAt || paymentAttempt.failedAt || paymentAttempt.updatedAt, 'long', )} /> diff --git a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx index 42f2bf693d9..ffa3bea2d7a 100644 --- a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx +++ b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptsList.tsx @@ -52,7 +52,7 @@ const PaymentAttemptsListRow = ({ paymentAttempt }: { paymentAttempt: CommercePa cursor: 'pointer', }} > - {formatDate(new Date(paidAt || failedAt || updatedAt), 'long')} + {formatDate(paidAt || failedAt || updatedAt, 'long')} { type: 'tabs', defaultCollapsed: false, }, - paymentMethodOrder: paymentMethodOrder, + paymentMethodOrder, applePay: checkout ? { recurringPaymentRequest: { diff --git a/packages/clerk-js/src/ui/components/Statements/Statement.tsx b/packages/clerk-js/src/ui/components/Statements/Statement.tsx index e98668d2c93..7cb8c5b6957 100644 --- a/packages/clerk-js/src/ui/components/Statements/Statement.tsx +++ b/packages/clerk-js/src/ui/components/Statements/Statement.tsx @@ -175,8 +175,8 @@ function SectionContentDetailsHeader({ }: { title: string | LocalizationKey; description: string | LocalizationKey; - secondaryTitle: string | LocalizationKey; - secondaryDescription: string | LocalizationKey; + secondaryTitle?: string | LocalizationKey; + secondaryDescription?: string | LocalizationKey; }) { return ( { ) : ( {statement.groups.map(group => ( - - + + {group.items.map(item => ( @@ -75,7 +70,6 @@ export const StatementPage = () => { title={item.subscription.plan.name} description={`${item.subscription.amount?.currencySymbol}${item.subscription.amount?.amountFormatted} / ${item.subscription.planPeriod === 'month' ? t(localizationKeys('commerce.month')) : t(localizationKeys('commerce.year'))}`} secondaryTitle={`${item.amount.currencySymbol}${item.amount.amountFormatted}`} - secondaryDescription={``} /> - - {new Date(timestamp).toLocaleString('en-US', { month: 'long', year: 'numeric' })} - + {formatDate(timestamp, 'monthyear')} Date: Fri, 13 Jun 2025 09:56:28 -0700 Subject: [PATCH 09/10] tidy up date format options --- packages/clerk-js/src/ui/utils/formatDate.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/clerk-js/src/ui/utils/formatDate.ts b/packages/clerk-js/src/ui/utils/formatDate.ts index 15d84253dff..3c227f12e91 100644 --- a/packages/clerk-js/src/ui/utils/formatDate.ts +++ b/packages/clerk-js/src/ui/utils/formatDate.ts @@ -5,11 +5,9 @@ export function formatDate( ): string { const options: Intl.DateTimeFormatOptions = { month: format === 'short' ? 'short' : 'long', - day: format === 'monthyear' ? undefined : 'numeric', + ...(format !== 'monthyear' && { day: 'numeric' }), + ...(format !== 'short' && { year: 'numeric' }), }; - if (format === 'long' || format === 'monthyear') { - options.year = 'numeric'; - } return new Intl.DateTimeFormat(locale, options).format(date); } From 7cb2b0f58899b4754138e9a07d64ba849bd615c1 Mon Sep 17 00:00:00 2001 From: Keiran Flanigan Date: Fri, 13 Jun 2025 12:26:52 -0700 Subject: [PATCH 10/10] address feedback --- .../src/core/resources/CommercePayment.ts | 7 ++--- .../src/core/resources/CommerceStatement.ts | 5 ++-- .../ui/components/Statements/Statement.tsx | 26 +++++++++++-------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/packages/clerk-js/src/core/resources/CommercePayment.ts b/packages/clerk-js/src/core/resources/CommercePayment.ts index be3ae67eb8b..d299130799a 100644 --- a/packages/clerk-js/src/core/resources/CommercePayment.ts +++ b/packages/clerk-js/src/core/resources/CommercePayment.ts @@ -7,6 +7,7 @@ import type { } from '@clerk/types'; import { commerceMoneyFromJSON } from '../../utils'; +import { unixEpochToDate } from '../../utils/date'; import { BaseResource, CommercePaymentSource, CommerceSubscription } from './internal'; export class CommercePayment extends BaseResource implements CommercePaymentResource { @@ -33,9 +34,9 @@ export class CommercePayment extends BaseResource implements CommercePaymentReso this.id = data.id; this.amount = commerceMoneyFromJSON(data.amount); - this.paidAt = data.paid_at ? new Date(data.paid_at) : undefined; - this.failedAt = data.failed_at ? new Date(data.failed_at) : undefined; - this.updatedAt = new Date(data.updated_at); + this.paidAt = data.paid_at ? unixEpochToDate(data.paid_at) : undefined; + this.failedAt = data.failed_at ? unixEpochToDate(data.failed_at) : undefined; + this.updatedAt = unixEpochToDate(data.updated_at); this.paymentSource = new CommercePaymentSource(data.payment_source); this.subscription = new CommerceSubscription(data.subscription); this.subscriptionItem = new CommerceSubscription(data.subscription_item); diff --git a/packages/clerk-js/src/core/resources/CommerceStatement.ts b/packages/clerk-js/src/core/resources/CommerceStatement.ts index 65baaecae5e..0ae065dbd63 100644 --- a/packages/clerk-js/src/core/resources/CommerceStatement.ts +++ b/packages/clerk-js/src/core/resources/CommerceStatement.ts @@ -7,6 +7,7 @@ import type { } from '@clerk/types'; import { commerceTotalsFromJSON } from '../../utils'; +import { unixEpochToDate } from '../../utils/date'; import { BaseResource, CommercePayment } from './internal'; export class CommerceStatement extends BaseResource implements CommerceStatementResource { @@ -28,7 +29,7 @@ export class CommerceStatement extends BaseResource implements CommerceStatement this.id = data.id; this.status = data.status; - this.timestamp = new Date(data.timestamp); + this.timestamp = unixEpochToDate(data.timestamp); this.totals = commerceTotalsFromJSON(data.totals); this.groups = data.groups.map(group => new CommerceStatementGroup(group)); return this; @@ -50,7 +51,7 @@ export class CommerceStatementGroup { } this.id = data.id; - this.timestamp = new Date(data.timestamp); + this.timestamp = unixEpochToDate(data.timestamp); this.items = data.items.map(item => new CommercePayment(item)); return this; } diff --git a/packages/clerk-js/src/ui/components/Statements/Statement.tsx b/packages/clerk-js/src/ui/components/Statements/Statement.tsx index 7cb8c5b6957..1192e5328ab 100644 --- a/packages/clerk-js/src/ui/components/Statements/Statement.tsx +++ b/packages/clerk-js/src/ui/components/Statements/Statement.tsx @@ -221,17 +221,21 @@ function SectionContentDetailsHeader({ textAlign: 'right', }} > - - + {secondaryTitle && ( + + )} + {secondaryDescription && ( + + )} );