Skip to content

Commit d8f67be

Browse files
authored
fix(clerk-js): Minor UI fixes for commerce (#5690)
1 parent e32b21e commit d8f67be

8 files changed

Lines changed: 58 additions & 25 deletions

File tree

.changeset/icy-lemons-fall.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Minor UI fixes for Billing pages in `<UserProfile/>` and `<OrganizationProfile/>`.

packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationBillingPage.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
__experimental_PaymentSourcesContext,
33
__experimental_PricingTableContext,
44
InvoicesContextProvider,
5-
usePlansContext,
5+
useSubscriptions,
66
withPlans,
77
} from '../../contexts';
88
import { Col, descriptors, Heading, Hr, localizationKeys } from '../../customizables';
@@ -32,9 +32,13 @@ const orgTabMap = {
3232
export const OrganizationBillingPage = withPlans(
3333
withCardStateProvider(() => {
3434
const card = useCardState();
35-
const { subscriptions } = usePlansContext();
35+
const { data: subscriptions } = useSubscriptions('org');
3636
const { selectedTab, handleTabChange } = useTabState(orgTabMap);
3737

38+
if (!Array.isArray(subscriptions?.data)) {
39+
return null;
40+
}
41+
3842
return (
3943
<Col
4044
elementDescriptor={descriptors.page}
@@ -61,7 +65,7 @@ export const OrganizationBillingPage = withPlans(
6165
<TabsList sx={t => ({ gap: t.space.$6 })}>
6266
<Tab
6367
localizationKey={
64-
subscriptions.length > 0
68+
subscriptions.data.length > 0
6569
? localizationKeys('userProfile.__experimental_billingPage.start.headerTitle__subscriptions')
6670
: localizationKeys('userProfile.__experimental_billingPage.start.headerTitle__plans')
6771
}
@@ -77,7 +81,7 @@ export const OrganizationBillingPage = withPlans(
7781
</TabsList>
7882
<TabPanels>
7983
<TabPanel sx={{ width: '100%', flexDirection: 'column' }}>
80-
{subscriptions.length > 0 && (
84+
{subscriptions.data.length > 0 && (
8185
<>
8286
<SubscriptionsList />
8387
<Hr sx={t => ({ marginBlock: t.space.$6 })} />

packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export const OrganizationProfileRoutes = () => {
6868
</Suspense>
6969
</Route>
7070
<Route path='invoice/:invoiceId'>
71+
{/* TODO(@commerce): Should this be lazy loaded ? */}
7172
<Suspense fallback={''}>
7273
<InvoicesContextProvider subscriberType='org'>
7374
<InvoicePage />

packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function SubscriptionsList() {
2525
</Thead>
2626
<Tbody>
2727
{subscriptions.map(subscription => (
28-
<Tr key={subscription.plan.id}>
28+
<Tr key={subscription.id}>
2929
<Td>
3030
<Box
3131
sx={t => ({
@@ -72,7 +72,7 @@ export function SubscriptionsList() {
7272
size='xs'
7373
textVariant='buttonSmall'
7474
onClick={() => handleSelectSubscription(subscription)}
75-
{...buttonPropsForPlan({ plan: subscription.plan })}
75+
{...buttonPropsForPlan({ subscription })}
7676
/>
7777
</Td>
7878
</Tr>

packages/clerk-js/src/ui/components/UserProfile/BillingPage.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
__experimental_PaymentSourcesContext,
33
__experimental_PricingTableContext,
44
InvoicesContextProvider,
5-
usePlansContext,
5+
useSubscriptions,
66
withPlans,
77
} from '../../contexts';
88
import { Col, descriptors, Heading, Hr, localizationKeys } from '../../customizables';
@@ -32,9 +32,14 @@ const tabMap = {
3232
export const BillingPage = withPlans(
3333
withCardStateProvider(() => {
3434
const card = useCardState();
35-
const { subscriptions } = usePlansContext();
35+
const { data: subscriptions } = useSubscriptions();
36+
3637
const { selectedTab, handleTabChange } = useTabState(tabMap);
3738

39+
if (!Array.isArray(subscriptions?.data)) {
40+
return null;
41+
}
42+
3843
return (
3944
<Col
4045
elementDescriptor={descriptors.page}
@@ -61,7 +66,7 @@ export const BillingPage = withPlans(
6166
<TabsList sx={t => ({ gap: t.space.$6 })}>
6267
<Tab
6368
localizationKey={
64-
subscriptions.length > 0
69+
subscriptions.data.length > 0
6570
? localizationKeys('userProfile.__experimental_billingPage.start.headerTitle__subscriptions')
6671
: localizationKeys('userProfile.__experimental_billingPage.start.headerTitle__plans')
6772
}
@@ -77,7 +82,7 @@ export const BillingPage = withPlans(
7782
</TabsList>
7883
<TabPanels>
7984
<TabPanel sx={_ => ({ width: '100%', flexDirection: 'column' })}>
80-
{subscriptions.length > 0 && (
85+
{subscriptions.data.length > 0 && (
8186
<>
8287
<SubscriptionsList />
8388
<Hr sx={t => ({ marginBlock: t.space.$6 })} />

packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const UserProfileRoutes = () => {
6666
</Suspense>
6767
</Route>
6868
<Route path='invoice/:invoiceId'>
69+
{/* TODO(@commerce): Should this be lazy loaded ? */}
6970
<Suspense fallback={''}>
7071
<InvoicesContextProvider>
7172
<InvoicePage />

packages/clerk-js/src/ui/contexts/components/Plans.tsx

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
__experimental_CommercePlanResource,
44
__experimental_CommerceSubscriberType,
55
__experimental_CommerceSubscriptionPlanPeriod,
6+
__experimental_CommerceSubscriptionResource,
67
} from '@clerk/types';
78
import type { ComponentType, ReactNode } from 'react';
89
import { createContext, useCallback, useContext, useMemo } from 'react';
@@ -19,25 +20,31 @@ type PlansContextProviderProps = {
1920

2021
const PlansContext = createContext<__experimental_PlansCtx | null>(null);
2122

23+
export const useSubscriptions = (subscriberType?: __experimental_CommerceSubscriberType) => {
24+
const { __experimental_commerce } = useClerk();
25+
const { organization } = useOrganization();
26+
27+
return useFetch(
28+
__experimental_commerce?.__experimental_billing.getSubscriptions,
29+
{ orgId: subscriberType === 'org' ? organization?.id : undefined },
30+
undefined,
31+
'commerce-subscriptions',
32+
);
33+
};
34+
2235
export const PlansContextProvider = ({
2336
subscriberType = 'user',
2437
children,
2538
}: PlansContextProviderProps & {
2639
children: ReactNode;
2740
}) => {
2841
const { __experimental_commerce } = useClerk();
29-
const { organization } = useOrganization();
3042

3143
const {
3244
data: subscriptions,
3345
isLoading: isLoadingSubscriptions,
3446
revalidate: revalidateSubscriptions,
35-
} = useFetch(
36-
__experimental_commerce?.__experimental_billing.getSubscriptions,
37-
{ orgId: subscriberType === 'org' ? organization?.id : undefined },
38-
undefined,
39-
'commerce-subscriptions',
40-
);
47+
} = useSubscriptions(subscriberType);
4148

4249
const {
4350
data: plans,
@@ -133,12 +140,14 @@ export const usePlansContext = () => {
133140
const buttonPropsForPlan = useCallback(
134141
({
135142
plan,
143+
subscription: sub,
136144
isCompact = false,
137145
}: {
138-
plan: __experimental_CommercePlanResource;
146+
plan?: __experimental_CommercePlanResource;
147+
subscription?: __experimental_CommerceSubscriptionResource;
139148
isCompact?: boolean;
140149
}): { localizationKey: LocalizationKey; variant: 'bordered' | 'solid'; colorScheme: 'secondary' | 'primary' } => {
141-
const subscription = activeOrUpcomingSubscription(plan);
150+
const subscription = sub ?? (plan ? activeOrUpcomingSubscription(plan) : undefined);
142151

143152
return {
144153
localizationKey: subscription
@@ -162,7 +171,11 @@ export const usePlansContext = () => {
162171
clerk.__internal_openSubscriptionDetails({
163172
subscription,
164173
subscriberType: ctx.subscriberType,
165-
onSubscriptionCancel: onSubscriptionChange,
174+
onSubscriptionCancel: () => {
175+
ctx.revalidate();
176+
onSubscriptionChange?.();
177+
clerk.__internal_closeSubscriptionDetails();
178+
},
166179
portalId:
167180
mode === 'modal'
168181
? ctx.subscriberType === 'user'

packages/clerk-js/src/ui/hooks/useFetch.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,16 @@ export const useCache = <K = any, V = any>(
3838
serializer = serialize,
3939
): {
4040
getCache: () => State<V> | undefined;
41-
setCache: (state: State<V>) => void;
41+
setCache: (state: State<V> | ((params: State<V>) => State<V>)) => void;
4242
clearCache: () => void;
4343
subscribeCache: (callback: () => void) => () => void;
4444
} => {
4545
const serializedKey = serializer(key);
4646
const get = useCallback(() => requestCache.get(serializedKey), [serializedKey]);
4747
const set = useCallback(
48-
(data: State) => {
49-
requestCache.set(serializedKey, data);
48+
(data: State | ((params: State) => State)) => {
49+
// @ts-ignore
50+
requestCache.set(serializedKey, typeof data === 'function' ? data(get()) : data);
5051
subscribers.forEach(callback => callback());
5152
},
5253
[serializedKey],
@@ -106,9 +107,12 @@ export const useFetch = <K, T>(
106107
const cached = useSyncExternalStore(subscribeCache, getCache);
107108

108109
const revalidate = useCallback(() => {
109-
clearCache();
110+
setCache(d => ({
111+
...d,
112+
cachedAt: 0,
113+
}));
110114
setRevalidationCounter(prev => prev + 1);
111-
}, [clearCache]);
115+
}, [setCache]);
112116

113117
useEffect(() => {
114118
const fetcherMissing = !fetcherRef.current;

0 commit comments

Comments
 (0)