Skip to content

Commit 174a6f0

Browse files
authored
feat(ui): Render seat limits in SubscriptionDetails (#8115)
1 parent 0104ebd commit 174a6f0

1 file changed

Lines changed: 120 additions & 46 deletions

File tree

  • packages/ui/src/components/SubscriptionDetails

packages/ui/src/components/SubscriptionDetails/index.tsx

Lines changed: 120 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
import * as React from 'react';
99
import { useCallback, useContext, useState } from 'react';
1010

11+
import { Users } from '@/icons';
1112
import { useProtect } from '@/ui/common/Gate';
1213
import {
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';
4446
import { SubscriptionBadge } from '../Subscriptions/badge';
47+
import { common } from '@/styledSystem';
4548

4649
const 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

Comments
 (0)