Skip to content

Commit

Permalink
feat(plan details): display progressive billing details (#1690)
Browse files Browse the repository at this point in the history
* misc(DetailsTableDisplay): allow to have no header

* misc(plan details) update spacing

* feat(plan details): display progressive billing details
  • Loading branch information
ansmonjol authored Aug 29, 2024
1 parent d62816b commit 7bca650
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 31 deletions.
37 changes: 22 additions & 15 deletions src/components/details/DetailsTableDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,33 @@ import styled from 'styled-components'
import { HEADER_TABLE_HEIGHT, theme } from '~/styles'

type DetailsTableDisplayData = {
header: Array<string | ReactNode>
header?: Array<string | ReactNode>
body?: Array<Array<string | number | ReactNode>>
className?: string
}

const DetailsTableDisplay = (data: DetailsTableDisplayData) => {
return (
<StyledTable $dataLength={data.header.length || 1} $hasBodyData={!!data.body?.length}>
<thead>
<tr>
{data.header.map((header, index) => (
<th key={`details-table-display-header-${index}`}>
{typeof header === 'object' ? (
header
) : (
<Typography variant="captionHl">{header}</Typography>
)}
</th>
))}
</tr>
</thead>
<StyledTable
$dataLength={data.header?.length || 0}
$hasBodyData={!!data.body?.length}
className={data?.className}
>
{!!data.header?.length && (
<thead>
<tr>
{data.header?.map((header, index) => (
<th key={`details-table-display-header-${index}`}>
{typeof header === 'object' ? (
header
) : (
<Typography variant="captionHl">{header}</Typography>
)}
</th>
))}
</tr>
</thead>
)}
{!!data.body?.length && (
<tbody>
{data.body.map((values, i) => (
Expand Down
114 changes: 109 additions & 5 deletions src/components/plans/details/PlanDetailsAdvancedSettingsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Stack } from '@mui/material'
import styled from 'styled-components'

import { Accordion, Typography } from '~/components/designSystem'
import { mapChargeIntervalCopy } from '~/components/plans/ChargeAccordion'
import { PROGRESSIVE_BILLING_DOC_URL } from '~/core/constants/externalUrls'
import { getIntervalTranslationKey } from '~/core/constants/form'
import { intlFormatNumber } from '~/core/formats/intlFormatNumber'
import { deserializeAmount } from '~/core/serializers/serializeAmount'
Expand All @@ -21,18 +23,109 @@ const PlanDetailsAdvancedSettingsSection = ({
const { translate } = useInternationalization()
const hasMinimumCommitment =
!!plan?.minimumCommitment?.amountCents && !isNaN(Number(plan?.minimumCommitment?.amountCents))
const hasProgressiveBilling = !!plan?.usageThresholds?.length

if (!hasMinimumCommitment) return null
if (!hasMinimumCommitment && !hasProgressiveBilling) return null

return (
<section>
<Container>
<DetailsSectionTitle variant="subhead" noWrap>
{translate('text_65d601bffb11e0f9d1d9f567')}
{translate('text_6661fc17337de3591e29e44d')}
</DetailsSectionTitle>

<Stack direction="column" gap={12}>
{hasProgressiveBilling && (
<Stack direction="column" gap={6}>
<div>
<Typography variant="bodyHl" color="grey700">
{translate('text_1724179887722baucvj7bvc1')}
</Typography>
<Typography
variant="caption"
color="grey600"
html={translate('text_1724179887723kdf3nisf6hp', {
href: PROGRESSIVE_BILLING_DOC_URL,
})}
/>
</div>

<Accordion
summary={
<Typography variant="bodyHl" color="grey700">
{translate('text_1724179887722baucvj7bvc1')}
</Typography>
}
>
<Stack direction="column" spacing={4}>
<DetailsTableDisplay
className="details-table-display-last-cell-ellipsis"
header={[
'',
translate('text_1724179887723eh12a0kqbdw'),
translate('text_17241798877234jhvoho4ci9'),
]}
body={[
...(plan?.usageThresholds
?.filter((t) => !t.recurring)
.map((threshold, i) => [
i === 0
? translate('text_1724179887723hi673zmbvdj')
: translate('text_1724179887723917j8ezkd9v'),
intlFormatNumber(
deserializeAmount(
threshold.amountCents,
plan?.amountCurrency || CurrencyEnum.Usd,
),
{
currency: currency,
},
),
threshold.thresholdDisplayName ||
`${translate('text_1724179887723pt34ivcecdt')} ${i + 1}`,
]) || []),
]}
/>

<DetailsInfoGrid
grid={[
{
label: translate('text_17241798877230y851fdxzqt'),
value: plan?.usageThresholds?.some((threshold) => threshold.recurring)
? translate('text_65251f46339c650084ce0d57')
: translate('text_65251f4cd55aeb004e5aa5ef'),
},
]}
/>

{plan?.usageThresholds?.some((threshold) => threshold.recurring) && (
<DetailsTableDisplay
className="details-table-display-last-cell-ellipsis"
// Only take the first recurring threshold
body={[
...([plan?.usageThresholds?.find((t) => t.recurring)]?.map((threshold) => [
translate('text_17241798877230y851fdxzqu'),
intlFormatNumber(
deserializeAmount(
threshold?.amountCents,
plan?.amountCurrency || CurrencyEnum.Usd,
),
{
currency: currency,
},
),
threshold?.thresholdDisplayName ||
translate('text_17241798877230y851fdxzqv'),
]) || []),
]}
/>
)}
</Stack>
</Accordion>
</Stack>
)}

{hasMinimumCommitment && (
<Stack direction="column" gap={4}>
<Stack direction="column" gap={6}>
<div>
<Typography variant="bodyHl" color="grey700">
{translate('text_65d601bffb11e0f9d1d9f569')}
Expand Down Expand Up @@ -104,8 +197,19 @@ const PlanDetailsAdvancedSettingsSection = ({
</Stack>
)}
</Stack>
</section>
</Container>
)
}

export default PlanDetailsAdvancedSettingsSection

const Container = styled.section`
.details-table-display-last-cell-ellipsis {
tr > td:last-child {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
`
9 changes: 5 additions & 4 deletions src/components/plans/details/PlanDetailsChargesSection.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Stack } from '@mui/material'
import styled from 'styled-components'

import { Accordion, Typography } from '~/components/designSystem'
Expand Down Expand Up @@ -56,7 +57,7 @@ const PlanDetailsChargesSection = ({
return (
<Container>
{!!meteredCharges?.length && (
<ChargeSectionWrapper>
<Stack direction="column" gap={6}>
<div>
<Typography variant="bodyHl" color="grey700">
{translate('text_64d2713ec021c6005ef64e03')}
Expand Down Expand Up @@ -168,10 +169,10 @@ const PlanDetailsChargesSection = ({
</ChargeSectionWrapper>
</Accordion>
))}
</ChargeSectionWrapper>
</Stack>
)}
{!!recurringCharges?.length && (
<ChargeSectionWrapper>
<Stack direction="column" gap={6}>
<div>
<Typography variant="bodyHl" color="grey700">
{translate('text_64d271e20a9c11005bd6688a')}
Expand Down Expand Up @@ -279,7 +280,7 @@ const PlanDetailsChargesSection = ({
</ChargeSectionWrapper>
</Accordion>
))}
</ChargeSectionWrapper>
</Stack>
)}
</Container>
)
Expand Down
3 changes: 3 additions & 0 deletions src/core/constants/externalUrls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export const FEATURE_REQUESTS_URL = 'https://getlago.canny.io/feature-requests'
export const ADYEN_SUCCESS_LINK_SPEC_URL =
'https://docs.adyen.com/api-explorer/Checkout/latest/post/payments#request-returnUrl'
export const ROLE_ACCESS_LEVEL_DOC_URL = 'https://getlago.com/docs/guide/security/rbac'
// TODO: Update this URL
export const PROGRESSIVE_BILLING_DOC_URL =
'https://getlago.com/docs/guide/billing/progressive-billing'
export const buildNetsuiteCustomerUrl = (
connectionAccountId?: string | null,
netsuiteCustomerId?: string | null,
Expand Down
Loading

0 comments on commit 7bca650

Please sign in to comment.