@@ -2,6 +2,7 @@ import type { CommerceSubscriptionResource } from '@clerk/types';
22
33import { useProtect } from '../../common' ;
44import { usePlansContext , useSubscriberTypeContext } from '../../contexts' ;
5+ import type { LocalizationKey } from '../../customizables' ;
56import {
67 Badge ,
78 Button ,
@@ -18,14 +19,25 @@ import {
1819 Thead ,
1920 Tr ,
2021} from '../../customizables' ;
21- import { CogFilled , Plans } from '../../icons' ;
22+ import { ProfileSection } from '../../elements' ;
23+ import { ArrowsUpDown , CogFilled , Plans , Plus } from '../../icons' ;
24+ import { useRouter } from '../../router' ;
2225
23- export function SubscriptionsList ( ) {
26+ export function SubscriptionsList ( {
27+ title,
28+ arrowButtonText,
29+ arrowButtonEmptyText,
30+ } : {
31+ title : LocalizationKey ;
32+ arrowButtonText : LocalizationKey ;
33+ arrowButtonEmptyText : LocalizationKey ;
34+ } ) {
2435 const { subscriptions, handleSelectPlan, captionForSubscription, canManageSubscription } = usePlansContext ( ) ;
2536 const subscriberType = useSubscriberTypeContext ( ) ;
2637 const canManageBilling = useProtect (
2738 has => has ( { permission : 'org:sys_billing:manage' } ) || subscriberType === 'user' ,
2839 ) ;
40+ const { navigate } = useRouter ( ) ;
2941 const handleSelectSubscription = (
3042 subscription : CommerceSubscriptionResource ,
3143 event ?: React . MouseEvent < HTMLElement > ,
@@ -52,117 +64,146 @@ export function SubscriptionsList() {
5264 } ) ;
5365
5466 return (
55- < Table tableHeadVisuallyHidden >
56- < Thead >
57- < Tr >
58- < Th > Plan</ Th >
59- < Th > Start date</ Th >
60- < Th > Edit</ Th >
61- </ Tr >
62- </ Thead >
63- < Tbody >
64- { sortedSubscriptions . map ( subscription => (
65- < Tr key = { subscription . id } >
66- < Td >
67- < Col gap = { 1 } >
68- < Flex
69- align = 'center'
70- gap = { 1 }
67+ < ProfileSection . Root
68+ id = 'subscriptionsList'
69+ title = { title }
70+ centered = { false }
71+ sx = { t => ( {
72+ borderTop : 'none' ,
73+ paddingTop : t . space . $1 ,
74+ } ) }
75+ >
76+ { subscriptions . length > 0 && (
77+ < Table tableHeadVisuallyHidden >
78+ < Thead >
79+ < Tr >
80+ < Th > Plan</ Th >
81+ < Th > Start date</ Th >
82+ < Th > Edit</ Th >
83+ </ Tr >
84+ </ Thead >
85+ < Tbody >
86+ { sortedSubscriptions . map ( subscription => (
87+ < Tr key = { subscription . id } >
88+ < Td >
89+ < Col gap = { 1 } >
90+ < Flex
91+ align = 'center'
92+ gap = { 1 }
93+ >
94+ < Icon
95+ icon = { Plans }
96+ sx = { t => ( {
97+ width : t . sizes . $4 ,
98+ height : t . sizes . $4 ,
99+ opacity : t . opacity . $inactive ,
100+ } ) }
101+ />
102+ < Text
103+ variant = 'subtitle'
104+ sx = { t => ( { marginRight : t . sizes . $1 } ) }
105+ >
106+ { subscription . plan . name }
107+ </ Text >
108+ { sortedSubscriptions . length > 1 || ! ! subscription . canceledAt ? (
109+ < Badge
110+ colorScheme = { subscription . status === 'active' ? 'secondary' : 'primary' }
111+ localizationKey = {
112+ subscription . status === 'active'
113+ ? localizationKeys ( 'badge__activePlan' )
114+ : localizationKeys ( 'badge__upcomingPlan' )
115+ }
116+ />
117+ ) : null }
118+ </ Flex >
119+ { ( ! subscription . plan . isDefault || subscription . status === 'upcoming' ) && (
120+ < Text
121+ variant = 'caption'
122+ colorScheme = 'secondary'
123+ localizationKey = { captionForSubscription ( subscription ) }
124+ />
125+ ) }
126+ </ Col >
127+ </ Td >
128+ < Td
129+ sx = { _ => ( {
130+ textAlign : 'right' ,
131+ } ) }
71132 >
72- < Icon
73- icon = { Plans }
74- sx = { t => ( {
75- width : t . sizes . $4 ,
76- height : t . sizes . $4 ,
77- opacity : t . opacity . $inactive ,
78- } ) }
79- />
80- < Text
81- variant = 'subtitle'
82- sx = { t => ( { marginRight : t . sizes . $1 } ) }
83- >
84- { subscription . plan . name }
133+ < Text variant = 'subtitle' >
134+ { subscription . plan . currencySymbol }
135+ { subscription . planPeriod === 'annual'
136+ ? subscription . plan . annualAmountFormatted
137+ : subscription . plan . amountFormatted }
138+ { ( subscription . plan . amount > 0 || subscription . plan . annualAmount > 0 ) && (
139+ < Span
140+ sx = { t => ( {
141+ color : t . colors . $colorTextSecondary ,
142+ textTransform : 'lowercase' ,
143+ ':before' : {
144+ content : '"/"' ,
145+ marginInline : t . space . $1 ,
146+ } ,
147+ } ) }
148+ localizationKey = {
149+ subscription . planPeriod === 'annual'
150+ ? localizationKeys ( 'commerce.year' )
151+ : localizationKeys ( 'commerce.month' )
152+ }
153+ />
154+ ) }
85155 </ Text >
86- { sortedSubscriptions . length > 1 || ! ! subscription . canceledAt ? (
87- < Badge
88- colorScheme = { subscription . status === 'active' ? 'secondary' : 'primary' }
89- localizationKey = {
90- subscription . status === 'active'
91- ? localizationKeys ( 'badge__activePlan' )
92- : localizationKeys ( 'badge__upcomingPlan' )
93- }
94- />
95- ) : null }
96- </ Flex >
97- { ( ! subscription . plan . isDefault || subscription . status === 'upcoming' ) && (
98- < Text
99- variant = 'caption'
100- colorScheme = 'secondary'
101- localizationKey = { captionForSubscription ( subscription ) }
102- />
103- ) }
104- </ Col >
105- </ Td >
106- < Td
107- sx = { _ => ( {
108- textAlign : 'right' ,
109- } ) }
110- >
111- < Text variant = 'subtitle' >
112- { subscription . plan . currencySymbol }
113- { subscription . planPeriod === 'annual'
114- ? subscription . plan . annualAmountFormatted
115- : subscription . plan . amountFormatted }
116- { ( subscription . plan . amount > 0 || subscription . plan . annualAmount > 0 ) && (
117- < Span
118- sx = { t => ( {
119- color : t . colors . $colorTextSecondary ,
120- textTransform : 'lowercase' ,
121- ':before' : {
122- content : '"/"' ,
123- marginInline : t . space . $1 ,
124- } ,
125- } ) }
126- localizationKey = {
127- subscription . planPeriod === 'annual'
128- ? localizationKeys ( 'commerce.year' )
129- : localizationKeys ( 'commerce.month' )
130- }
131- />
132- ) }
133- </ Text >
134- </ Td >
135- < Td
136- sx = { _ => ( {
137- textAlign : 'right' ,
138- } ) }
139- >
140- { canManageSubscription ( { subscription } ) && subscription . id && ! subscription . plan . isDefault && (
141- < Button
142- aria-label = 'Manage subscription'
143- onClick = { event => handleSelectSubscription ( subscription , event ) }
144- variant = 'bordered'
145- colorScheme = 'secondary'
146- isDisabled = { ! canManageBilling }
147- sx = { t => ( {
148- width : t . sizes . $6 ,
149- height : t . sizes . $6 ,
156+ </ Td >
157+ < Td
158+ sx = { _ => ( {
159+ textAlign : 'right' ,
150160 } ) }
151161 >
152- < Icon
153- icon = { CogFilled }
154- sx = { t => ( {
155- width : t . sizes . $4 ,
156- height : t . sizes . $4 ,
157- opacity : t . opacity . $inactive ,
158- } ) }
159- />
160- </ Button >
161- ) }
162- </ Td >
163- </ Tr >
164- ) ) }
165- </ Tbody >
166- </ Table >
162+ { canManageSubscription ( { subscription } ) && subscription . id && ! subscription . plan . isDefault && (
163+ < Button
164+ aria-label = 'Manage subscription'
165+ onClick = { event => handleSelectSubscription ( subscription , event ) }
166+ variant = 'bordered'
167+ colorScheme = 'secondary'
168+ isDisabled = { ! canManageBilling }
169+ sx = { t => ( {
170+ width : t . sizes . $6 ,
171+ height : t . sizes . $6 ,
172+ } ) }
173+ >
174+ < Icon
175+ icon = { CogFilled }
176+ sx = { t => ( {
177+ width : t . sizes . $4 ,
178+ height : t . sizes . $4 ,
179+ opacity : t . opacity . $inactive ,
180+ } ) }
181+ />
182+ </ Button >
183+ ) }
184+ </ Td >
185+ </ Tr >
186+ ) ) }
187+ </ Tbody >
188+ </ Table >
189+ ) }
190+
191+ < ProfileSection . ArrowButton
192+ id = 'subscriptionsList'
193+ textLocalizationKey = { subscriptions . length > 0 ? arrowButtonText : arrowButtonEmptyText }
194+ sx = { [
195+ t => ( {
196+ justifyContent : 'start' ,
197+ height : t . sizes . $8 ,
198+ } ) ,
199+ ] }
200+ leftIcon = { subscriptions . length > 0 ? ArrowsUpDown : Plus }
201+ leftIconSx = { t => ( {
202+ width : t . sizes . $4 ,
203+ height : t . sizes . $4 ,
204+ } ) }
205+ onClick = { ( ) => void navigate ( 'plans' ) }
206+ />
207+ </ ProfileSection . Root >
167208 ) ;
168209}
0 commit comments