Skip to content

Commit

Permalink
feat(CustomerRevamp): Refactor customer subscription list, customer c…
Browse files Browse the repository at this point in the history
…oupons and customer details (#1997)

* feat(icons): add arrow-indent icon

* chore(translations): add translations

* chore(graphql): generate types

* feat(TimezoneDate): add className prop

* feat(Status): add support for downgrade and scheduled statuses

* feat(icons): add arrow-indent icon

* feat(layoutsSection): create reusable page title component

* feat(CouponCaption): add className prop

* refactor(CustomerPage): unify section heights

* refactor(CustomerCoupons): list -> table

* refactor(CustomerSubscriptionsList): list -> table

* refactor(CustomerMainInfos): always show all the customer details

* refactor(CustomerOverview): re-use PageSectionTitle

* refactor(CustomerDetails): replace customer details drawer with tab

* fix: linting

* fix(translations): remove unused translations

* feat(translations): add other languages

* refactor(Chip): remove clsx dependency

* refactor(CustomerSubscriptionsList): remove unused fragment

* fix(Status): fix types

* refactor(CustomerSubscriptionsList): add padding to rows

* delete: remove SubscriptionLine

* refactor(CustomerSubscriptionsList): update graphql

* chore(graphql): generate types

* refactor(CustomerSubscriptionsList): update fragment for next subscription

* chore(translations): add translations

* feat(CustomerDetails): update label

* feat(TimezoneDate): add typographyClassName prop

* refactor(CustomerSubscriptionsList): scheduled status. column order. date border

* chore(graphql): generate types

* fix(CustomerSubscriptionList): enable clicking on downgrade. fix downgrade status

* fix: add table paddings

* fix(CustomerSubscriptionList): pass subscription status to terminate dialog

* feat: move customer overview to the invoices tab

* chore(translations): add translations

* feat(PageSectionTitle): add support for a custom action

* refactor(CustomerWalletsList): unify title

* refactor(CustomerUsage): unify title

* refactor(CustomerOverview): add subtitle

* refactor(CustomerInvoicesTab): improve logic clarity. unify design

* refactor(Settings): update spacing

* chore(translations): add translations

* refactor(CustomerInvoicesTab): do not show overview for 0 invoices. improve typography for 0 invoices

* feat(tailwind): add first-child helper

* chore(translations): add translations

* refactor(CustomerMainInfos): separate into sections

* refactor(CustomerMainInfos): render metadata and connections conditionally

* refactor(CustomerMainInfos): do not render border for last element

* chore(translations): add translations

* feat(CreditNotesTable): add filtersContainerClassName prop

* fix(CustomerCreditNotesList): remove legacy ScrollContainer

* refactor(CreditNotesTable): remove legacy onKeyDown

* refactor(CustomerCreditNotesList): remove unused import

* fix(e2e): add missing data-test attributes

* fix(CustomerSettings): remove bottom padding

* chore(translations): update translations

* fix(CustomerMainInfos): render billing information conditionally

* chore(translations): update translations

* fix(navigation): navigate to information tab after save

* refactor(overdueBalance): refresh from card

* refactor(useCreateEditCustomer): redirect to overview

* refactor(loadingState): improve loading state

* refactor(CustomerInvoicesTab): loading state

* fix(translations): remove unused translations
  • Loading branch information
stephenlago99 authored Jan 30, 2025
1 parent ca63148 commit bae94f8
Show file tree
Hide file tree
Showing 33 changed files with 1,768 additions and 1,827 deletions.
27 changes: 19 additions & 8 deletions src/components/OverviewCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface OverviewCardProps {
caption: string
isAccentContent?: boolean
isLoading?: boolean
refresh?: () => void
}

export const OverviewCard: FC<OverviewCardProps> = ({
Expand All @@ -18,26 +19,36 @@ export const OverviewCard: FC<OverviewCardProps> = ({
caption,
isAccentContent,
isLoading,
refresh,
}) => {
return (
<Card className="flex-1 gap-4 p-6">
{isLoading ? (
<div className="h-22">
<Skeleton className="mb-8 w-22" variant="text" />
<Skeleton className="w-22" variant="text" />
<div className="flex flex-col gap-4">
<Skeleton className="w-50" variant="text" />
<Skeleton className="w-12" variant="text" />
</div>
</div>
) : (
<>
<div className="flex items-center gap-2">
<Typography variant="captionHl">{title}</Typography>
{tooltipContent && (
<Tooltip className="flex h-5 items-end" placement="top-start" title={tooltipContent}>
<Icon name="info-circle" />
</Tooltip>
)}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Typography variant="captionHl">{title}</Typography>

{tooltipContent && (
<Tooltip
className="flex h-5 items-end"
placement="top-start"
title={tooltipContent}
>
<Icon name="info-circle" />
</Tooltip>
)}
</div>

{refresh && <Icon name="reload" onClick={refresh} />}
</div>

<div className="flex flex-col gap-1">
Expand Down
7 changes: 5 additions & 2 deletions src/components/TimezoneDate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { formatDateToTZ, getTimezoneConfig } from '~/core/timezone'
import { TimezoneEnum } from '~/generated/graphql'
import { useInternationalization } from '~/hooks/core/useInternationalization'
import { useOrganizationInfos } from '~/hooks/useOrganizationInfos'
import { tw } from '~/styles/utils'

enum MainTimezoneEnum {
utc0 = 'utc0',
Expand All @@ -15,8 +16,9 @@ interface TimezoneDateProps {
mainDateFormat?: string
mainTimezone?: keyof typeof MainTimezoneEnum
customerTimezone?: TimezoneEnum
mainTypographyProps?: Pick<TypographyProps, 'variant' | 'color'>
mainTypographyProps?: Pick<TypographyProps, 'variant' | 'color' | 'className'>
className?: string
typographyClassName?: string
}

export const TimezoneDate = ({
Expand All @@ -25,6 +27,7 @@ export const TimezoneDate = ({
mainTimezone = MainTimezoneEnum.organization,
customerTimezone,
mainTypographyProps,
typographyClassName,
className,
}: TimezoneDateProps) => {
const { translate } = useInternationalization()
Expand Down Expand Up @@ -69,7 +72,7 @@ export const TimezoneDate = ({
placement="top-end"
>
<Typography
className="w-max border-b-2 border-dotted border-grey-400"
className={tw('w-max border-b-2 border-dotted border-grey-400', typographyClassName)}
color="grey700"
{...mainTypographyProps}
noWrap
Expand Down
187 changes: 103 additions & 84 deletions src/components/coupons/CouponCaption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,104 +44,123 @@ export interface CouponMixedType extends CouponItemFragment {
interface CouponCaptionProps {
coupon: CouponMixedType
variant?: TypographyProps['variant']
className?: string
}

export const CouponCaption = memo(({ coupon, variant = 'caption' }: CouponCaptionProps) => {
const { translate } = useInternationalization()
export const CouponCaption = memo(
({ coupon, variant = 'caption', className }: CouponCaptionProps) => {
const { translate } = useInternationalization()

const getCaption = () => {
const {
amountCurrency,
amountCents,
amountCentsRemaining,
percentageRate,
frequency,
frequencyDuration,
frequencyDurationRemaining,
} = coupon
const couponType = amountCents ? CouponTypeEnum.FixedAmount : CouponTypeEnum.Percentage
const getCaption = () => {
const {
amountCurrency,
amountCents,
amountCentsRemaining,
percentageRate,
frequency,
frequencyDuration,
frequencyDurationRemaining,
} = coupon
const couponType = amountCents ? CouponTypeEnum.FixedAmount : CouponTypeEnum.Percentage

if (couponType === CouponTypeEnum.FixedAmount && frequency === CouponFrequency.Once) {
return translate(
amountCentsRemaining ? 'text_637b4da08cd0118cd0c4486f' : 'text_632d68358f1fedc68eed3e70',
{
amount: intlFormatNumber(
deserializeAmount(
Number(amountCentsRemaining) || Number(amountCents),
amountCurrency || CurrencyEnum.Usd,
) || 0,
{
currencyDisplay: 'symbol',
currency: amountCurrency || undefined,
},
),
},
)
} else if (couponType === CouponTypeEnum.Percentage && frequency === CouponFrequency.Once) {
return translate('text_632d68358f1fedc68eed3eb5', {
rate: intlFormatNumber(Number(percentageRate) / 100 || 0, {
style: 'percent',
}),
})
} else if (
couponType === CouponTypeEnum.FixedAmount &&
frequency === CouponFrequency.Recurring
) {
return translate(
'text_632d68358f1fedc68eed3ede',
{
if (couponType === CouponTypeEnum.FixedAmount && frequency === CouponFrequency.Once) {
return translate(
amountCentsRemaining ? 'text_637b4da08cd0118cd0c4486f' : 'text_632d68358f1fedc68eed3e70',
{
amount: intlFormatNumber(
deserializeAmount(
Number(amountCentsRemaining) || Number(amountCents),
amountCurrency || CurrencyEnum.Usd,
) || 0,
{
currencyDisplay: 'symbol',
currency: amountCurrency || undefined,
},
),
},
)
} else if (couponType === CouponTypeEnum.Percentage && frequency === CouponFrequency.Once) {
return translate('text_632d68358f1fedc68eed3eb5', {
rate: intlFormatNumber(Number(percentageRate) / 100 || 0, {
style: 'percent',
}),
})
} else if (
couponType === CouponTypeEnum.FixedAmount &&
frequency === CouponFrequency.Recurring
) {
return translate(
'text_632d68358f1fedc68eed3ede',
{
amount: intlFormatNumber(
deserializeAmount(
Number(amountCentsRemaining) || Number(amountCents),
amountCurrency || CurrencyEnum.Usd,
) || 0,
{
currencyDisplay: 'symbol',
currency: amountCurrency || undefined,
},
),
duration: frequencyDurationRemaining || frequencyDuration,
},
frequencyDurationRemaining || frequencyDuration || 1,
)
} else if (
couponType === CouponTypeEnum.Percentage &&
frequency === CouponFrequency.Recurring
) {
return translate(
'text_632d68358f1fedc68eed3ef9',
{
rate: intlFormatNumber(Number(percentageRate) / 100 || 0, {
style: 'percent',
}),
duration: frequencyDurationRemaining || frequencyDuration,
},
frequencyDurationRemaining || frequencyDuration || 1,
)
} else if (
couponType === CouponTypeEnum.FixedAmount &&
frequency === CouponFrequency.Forever
) {
return translate('text_63c946e8bef768ead2fee35c', {
amount: intlFormatNumber(
deserializeAmount(
Number(amountCentsRemaining) || Number(amountCents),
amountCurrency || CurrencyEnum.Usd,
) || 0,
deserializeAmount(Number(amountCents), amountCurrency || CurrencyEnum.Usd) || 0,
{
currencyDisplay: 'symbol',
currency: amountCurrency || undefined,
},
),
duration: frequencyDurationRemaining || frequencyDuration,
},
frequencyDurationRemaining || frequencyDuration || 1,
)
} else if (
couponType === CouponTypeEnum.Percentage &&
frequency === CouponFrequency.Recurring
) {
return translate(
'text_632d68358f1fedc68eed3ef9',
{
})
} else if (
couponType === CouponTypeEnum.Percentage &&
frequency === CouponFrequency.Forever
) {
return translate('text_63c96b18bfbf40e9ef600e99', {
rate: intlFormatNumber(Number(percentageRate) / 100 || 0, {
style: 'percent',
}),
duration: frequencyDurationRemaining || frequencyDuration,
},
frequencyDurationRemaining || frequencyDuration || 1,
)
} else if (couponType === CouponTypeEnum.FixedAmount && frequency === CouponFrequency.Forever) {
return translate('text_63c946e8bef768ead2fee35c', {
amount: intlFormatNumber(
deserializeAmount(Number(amountCents), amountCurrency || CurrencyEnum.Usd) || 0,
{
currencyDisplay: 'symbol',
currency: amountCurrency || undefined,
},
),
})
} else if (couponType === CouponTypeEnum.Percentage && frequency === CouponFrequency.Forever) {
return translate('text_63c96b18bfbf40e9ef600e99', {
rate: intlFormatNumber(Number(percentageRate) / 100 || 0, {
style: 'percent',
}),
})
})
}
}
}

return (
<Typography variant={variant} color="grey600" noWrap data-test="coupon-caption">
{getCaption()}
</Typography>
)
})
return (
<>
{!className && (
<Typography variant={variant} color="grey600" noWrap data-test="coupon-caption">
{getCaption()}
</Typography>
)}

{className && (
<Typography className={className} data-test="coupon-caption">
{getCaption()}
</Typography>
)}
</>
)
},
)

CouponCaption.displayName = 'CouponCaption'
Loading

0 comments on commit bae94f8

Please sign in to comment.