@@ -8,6 +8,7 @@ import type {
88import * as React from 'react' ;
99import { useCallback , useContext , useState } from 'react' ;
1010
11+ import { Users } from '@/icons' ;
1112import { useProtect } from '@/ui/common/Gate' ;
1213import {
1314 SubscriptionDetailsContext ,
@@ -36,12 +37,14 @@ import {
3637 Flex ,
3738 Flow ,
3839 Heading ,
40+ Icon ,
3941 localizationKeys ,
4042 Spinner ,
4143 Text ,
4244 useLocalizations ,
4345} from '../../customizables' ;
4446import { SubscriptionBadge } from '../Subscriptions/badge' ;
47+ import { common } from '@/styledSystem' ;
4548
4649const isFreePlan = ( plan : BillingPlanResource ) => ! plan . hasBaseFee ;
4750
@@ -518,19 +521,22 @@ const SubscriptionCard = ({ subscription }: { subscription: BillingSubscriptionI
518521 sx = { t => ( {
519522 borderRadius : t . radii . $md ,
520523 boxShadow : t . shadows . $tableBodyShadow ,
524+ overflow : 'hidden' ,
521525 } ) }
522526 >
523527 < Col
524528 elementDescriptor = { descriptors . subscriptionDetailsCardBody }
525529 gap = { 3 }
526530 sx = { t => ( {
527531 padding : t . space . $3 ,
532+ background : common . mutedBackground ( t ) ,
528533 } ) }
529534 >
530535 { /* Header with name and badge */ }
531536 < Flex
532537 elementDescriptor = { descriptors . subscriptionDetailsCardHeader }
533- align = 'center'
538+ // align='center'
539+ justify = 'between'
534540 gap = { 2 }
535541 >
536542 { subscription . plan . avatarUrl ? (
@@ -543,44 +549,86 @@ const SubscriptionCard = ({ subscription }: { subscription: BillingSubscriptionI
543549 />
544550 ) : null }
545551
546- < Text
547- elementDescriptor = { descriptors . subscriptionDetailsCardTitle }
548- variant = 'h2'
549- sx = { t => ( {
550- fontSize : t . fontSizes . $lg ,
551- fontWeight : t . fontWeights . $semibold ,
552- color : t . colors . $colorForeground ,
553- marginInlineEnd : 'auto' ,
554- } ) }
552+ { /* Pricing details */ }
553+ < Flex
554+ direction = 'col'
555+ justify = 'between'
555556 >
556- { subscription . plan . name }
557- </ Text >
558- < SubscriptionBadge
559- subscription = { subscription . isFreeTrial ? { status : 'free_trial' } : subscription }
560- elementDescriptor = { descriptors . subscriptionDetailsCardBadge }
561- />
562- </ Flex >
563-
564- { /* Pricing details */ }
565- < Flex
566- justify = 'between'
567- align = 'center'
568- >
569- < Text
570- variant = 'body'
571- colorScheme = 'secondary'
572- sx = { t => ( {
573- fontWeight : t . fontWeights . $medium ,
574- textTransform : 'lowercase' ,
575- } ) }
576- >
577- { fee . currencySymbol }
578- { fee . amountFormatted } /{ ' ' }
579- { t ( localizationKeys ( `billing.${ subscription . planPeriod === 'month' ? 'month' : 'year' } ` ) ) }
580- </ Text >
557+ < Text
558+ elementDescriptor = { descriptors . subscriptionDetailsCardTitle }
559+ variant = 'h2'
560+ sx = { t => ( {
561+ fontSize : t . fontSizes . $lg ,
562+ fontWeight : t . fontWeights . $semibold ,
563+ color : t . colors . $colorForeground ,
564+ } ) }
565+ >
566+ { subscription . plan . name }
567+ </ Text >
568+ < Flex >
569+ < Text
570+ variant = 'body'
571+ as = 'span'
572+ sx = { t => ( {
573+ fontWeight : t . fontWeights . $medium ,
574+ textTransform : 'lowercase' ,
575+ } ) }
576+ >
577+ { fee . currencySymbol }
578+ { fee . amountFormatted }
579+ </ Text >
580+ < Text
581+ variant = 'body'
582+ as = 'span'
583+ colorScheme = 'secondary'
584+ sx = { t => ( {
585+ fontWeight : t . fontWeights . $medium ,
586+ textTransform : 'lowercase' ,
587+ whiteSpace : 'pre' ,
588+ } ) }
589+ >
590+ { ` / ${ t ( localizationKeys ( `billing.${ subscription . planPeriod === 'month' ? 'month' : 'year' } ` ) ) } ` }
591+ </ Text >
592+ </ Flex >
593+ </ Flex >
594+ < Flex align = 'start' >
595+ < SubscriptionBadge
596+ subscription = { subscription . isFreeTrial ? { status : 'free_trial' } : subscription }
597+ elementDescriptor = { descriptors . subscriptionDetailsCardBadge }
598+ />
599+ </ Flex >
581600 </ Flex >
582601 </ Col >
583602
603+ { subscription . seats ? (
604+ < DetailRow
605+ variant = 'header'
606+ labelNode = {
607+ < Text
608+ sx = { t => ( {
609+ display : 'inline-flex' ,
610+ alignItems : 'center' ,
611+ gap : t . space . $1 ,
612+ } ) }
613+ >
614+ < Icon
615+ icon = { Users }
616+ size = 'md'
617+ colorScheme = 'neutral'
618+ />
619+ < Text localizationKey = { localizationKeys ( 'billing.seats' ) } />
620+ </ Text >
621+ }
622+ value = {
623+ subscription . seats . quantity === null
624+ ? localizationKeys ( 'billing.pricingTable.seatCost.unlimitedSeats' )
625+ : localizationKeys ( 'billing.pricingTable.seatCost.upToSeats' , {
626+ endsAfterBlock : subscription . seats . quantity ,
627+ } )
628+ }
629+ />
630+ ) : null }
631+
584632 { subscription . pastDueAt ? (
585633 < DetailRow
586634 label = { localizationKeys ( 'billing.subscriptionDetails.pastDueAt' ) }
@@ -627,7 +675,17 @@ const SubscriptionCard = ({ subscription }: { subscription: BillingSubscriptionI
627675} ;
628676
629677// Helper component for detail rows
630- const DetailRow = ( { label, value } : { label : LocalizationKey ; value : string } ) => (
678+ const DetailRow = ( {
679+ label,
680+ labelNode,
681+ value,
682+ variant,
683+ } : {
684+ label ?: LocalizationKey ;
685+ labelNode ?: React . ReactNode ;
686+ value : string | LocalizationKey ;
687+ variant ?: 'header' ;
688+ } ) => (
631689 < Flex
632690 elementDescriptor = { descriptors . subscriptionDetailsDetailRow }
633691 justify = 'between'
@@ -638,17 +696,33 @@ const DetailRow = ({ label, value }: { label: LocalizationKey; value: string })
638696 borderBlockStartWidth : t . borderWidths . $normal ,
639697 borderBlockStartStyle : t . borderStyles . $solid ,
640698 borderBlockStartColor : t . colors . $borderAlpha100 ,
699+ ...( variant === 'header'
700+ ? {
701+ background : common . mutedBackground ( t ) ,
702+ }
703+ : { } ) ,
641704 } ) }
642705 >
643- < Text
644- elementDescriptor = { descriptors . subscriptionDetailsDetailRowLabel }
645- localizationKey = { label }
646- />
647- < Text
648- elementDescriptor = { descriptors . subscriptionDetailsDetailRowValue }
649- colorScheme = 'secondary'
650- >
651- { value }
652- </ Text >
706+ { label ? (
707+ < Text
708+ elementDescriptor = { descriptors . subscriptionDetailsDetailRowLabel }
709+ localizationKey = { label }
710+ />
711+ ) : null }
712+ { labelNode ? labelNode : null }
713+ { typeof value === 'string' ? (
714+ < Text
715+ elementDescriptor = { descriptors . subscriptionDetailsDetailRowValue }
716+ colorScheme = 'secondary'
717+ >
718+ { value }
719+ </ Text >
720+ ) : (
721+ < Text
722+ localizationKey = { value }
723+ elementDescriptor = { descriptors . subscriptionDetailsDetailRowValue }
724+ colorScheme = 'secondary'
725+ />
726+ ) }
653727 </ Flex >
654728) ;
0 commit comments