Skip to content

Commit 82c907d

Browse files
committed
Merge branch 'dev-987-asset-usage-limit-offset-pagination' into anji/dev-1013-mirgrate-to-universal-table
2 parents 307c0cc + 8c71fd2 commit 82c907d

File tree

17 files changed

+139
-287
lines changed

17 files changed

+139
-287
lines changed

jsapp/js/account/plans/confirmChangeModal.component.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import styles from './confirmChangeModal.module.scss'
1717
export interface ConfirmChangeProps {
1818
newPrice: Price | null
1919
products: Product[] | null
20-
quantity?: number
2120
currentSubscription: SubscriptionInfo | null
2221
}
2322

@@ -37,11 +36,10 @@ const ConfirmChangeModal = ({
3736
currentSubscription,
3837
onRequestClose,
3938
setIsBusy,
40-
quantity = 1,
4139
}: ConfirmChangeModalProps) => {
4240
const [isLoading, setIsLoading] = useState(false)
4341
const [pendingChange, setPendingChange] = useState(false)
44-
const displayPrice = useDisplayPrice(newPrice, quantity)
42+
const displayPrice = useDisplayPrice(newPrice)
4543

4644
const shouldShow = useMemo(() => !!(currentSubscription && newPrice), [newPrice, currentSubscription])
4745

@@ -106,7 +104,7 @@ const ConfirmChangeModal = ({
106104
}
107105
setIsLoading(true)
108106
setPendingChange(true)
109-
changeSubscription(newPrice.id, currentSubscription.id, quantity)
107+
changeSubscription(newPrice.id, currentSubscription.id)
110108
.then((data) => {
111109
processChangePlanResponse(data)
112110
setPendingChange(false)

jsapp/js/account/plans/plan.component.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ export default function Plan(props: PlanProps) {
9999
newPrice: null,
100100
products: [],
101101
currentSubscription: null,
102-
quantity: 1,
103102
})
104103
const [visiblePlanTypes, setVisiblePlanTypes] = useState(['default'])
105104

@@ -299,7 +298,7 @@ export default function Plan(props: PlanProps) {
299298
const getSubscribedProduct = useCallback(getSubscriptionsForProductId, [])
300299

301300
const isSubscribedProduct = useCallback(
302-
(product: SinglePricedProduct, quantity: number | undefined) => {
301+
(product: SinglePricedProduct) => {
303302
if (!product.price?.unit_amount && !hasActiveSubscription) {
304303
return true
305304
}
@@ -309,10 +308,7 @@ export default function Plan(props: PlanProps) {
309308
if (subscriptions && subscriptions.length > 0) {
310309
return subscriptions.some(
311310
(subscription: SubscriptionInfo) =>
312-
subscription.items[0].price.id === product.price.id &&
313-
hasManageableStatus(subscription) &&
314-
quantity !== undefined &&
315-
quantity === subscription.quantity,
311+
subscription.items[0].price.id === product.price.id && hasManageableStatus(subscription),
316312
)
317313
}
318314
return false
@@ -326,31 +322,30 @@ export default function Plan(props: PlanProps) {
326322
})
327323
}
328324

329-
const buySubscription = (price: Price, quantity = 1) => {
325+
const buySubscription = (price: Price) => {
330326
if (!price.id) return
331327
if (isDisabled) return
332328

333329
setIsBusy(true)
334330
if (activeSubscriptions.length) {
335-
if (isDowngrade(activeSubscriptions, price, quantity)) {
331+
if (isDowngrade(activeSubscriptions, price)) {
336332
// if the user is downgrading prices, open a confirmation dialog and downgrade from kpi
337333
// this will downgrade the subscription at the end of the current billing period
338334
setConfirmModal({
339335
products: products.products,
340336
newPrice: price,
341337
currentSubscription: activeSubscriptions[0],
342-
quantity: quantity,
343338
})
344339
} else {
345340
// if the user is upgrading prices, send them to the customer portal
346341
// this will immediately change their subscription
347-
postCustomerPortal(organization.id, price.id, quantity)
342+
postCustomerPortal(organization.id, price.id)
348343
.then(processCheckoutResponse)
349344
.catch(() => setIsBusy(false))
350345
}
351346
} else {
352347
// just send the user to the checkout page
353-
postCheckout(price.id, organization.id, quantity)
348+
postCheckout(price.id, organization.id)
354349
.then(processCheckoutResponse)
355350
.catch(() => setIsBusy(false))
356351
}

jsapp/js/account/plans/planButton.component.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ import { processCheckoutResponse } from '#/account/stripe.utils'
55
import { useOrganizationAssumed } from '#/api/useOrganizationAssumed'
66

77
interface PlanButtonProps {
8-
buySubscription: (price: Price, quantity?: number) => void
8+
buySubscription: (price: Price) => void
99
downgrading: boolean
1010
isBusy: boolean
1111
isSubscribedToPlan: boolean
1212
showManage: boolean
1313
product: SinglePricedProduct
14-
quantity: number
1514
setIsBusy: (value: boolean) => void
1615
}
1716

@@ -26,7 +25,6 @@ export const PlanButton = ({
2625
setIsBusy,
2726
buySubscription,
2827
showManage,
29-
quantity,
3028
isSubscribedToPlan,
3129
}: PlanButtonProps) => {
3230
const [organization] = useOrganizationAssumed()
@@ -37,7 +35,7 @@ export const PlanButton = ({
3735

3836
const manageSubscription = (subscriptionPrice?: Price) => {
3937
setIsBusy(true)
40-
postCustomerPortal(organization.id, subscriptionPrice?.id, quantity)
38+
postCustomerPortal(organization.id, subscriptionPrice?.id)
4139
.then(processCheckoutResponse)
4240
.catch(() => setIsBusy(false))
4341
}
@@ -46,7 +44,7 @@ export const PlanButton = ({
4644
return (
4745
<BillingButton
4846
label={t('Upgrade')}
49-
onClick={() => buySubscription(product.price, quantity)}
47+
onClick={() => buySubscription(product.price)}
5048
aria-label={`upgrade to ${product.name}`}
5149
isDisabled={isBusy}
5250
/>
@@ -67,7 +65,7 @@ export const PlanButton = ({
6765
return (
6866
<BillingButton
6967
label={t('Change plan')}
70-
onClick={() => buySubscription(product.price, quantity)}
68+
onClick={() => buySubscription(product.price)}
7169
aria-label={`change your subscription to ${product.name}`}
7270
isDisabled={isBusy}
7371
/>

jsapp/js/account/plans/planContainer.component.tsx

Lines changed: 10 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,26 @@
1-
import React, { useCallback, useEffect, useMemo, useState } from 'react'
1+
import React, { useCallback, useMemo } from 'react'
22

33
import classnames from 'classnames'
44
import type { FreeTierOverride, PlanState } from '#/account/plans/plan.component'
55
import styles from '#/account/plans/plan.module.scss'
66
import { PlanButton } from '#/account/plans/planButton.component'
77
import { useDisplayPrice } from '#/account/plans/useDisplayPrice.hook'
88
import type { Price, SinglePricedProduct, SubscriptionInfo } from '#/account/stripe.types'
9-
import {
10-
getAdjustedQuantityForPrice,
11-
getSubscriptionsForProductId,
12-
isChangeScheduled,
13-
isDowngrade,
14-
} from '#/account/stripe.utils'
9+
import { getSubscriptionsForProductId, isChangeScheduled, isDowngrade } from '#/account/stripe.utils'
1510
import Icon from '#/components/common/icon'
16-
import KoboSelect, { type KoboSelectOption } from '#/components/common/koboSelect'
1711
import { recordKeys } from '#/utils'
1812

1913
interface PlanContainerProps {
2014
product: SinglePricedProduct
2115
isDisabled: boolean
22-
isSubscribedProduct: (product: SinglePricedProduct, quantity: number) => boolean
16+
isSubscribedProduct: (product: SinglePricedProduct) => boolean
2317
freeTierOverride: FreeTierOverride | null
2418
expandComparison: boolean
2519
state: PlanState
2620
filteredPriceProducts: SinglePricedProduct[]
2721
setIsBusy: (isBusy: boolean) => void
2822
hasManageableStatus: (sub: SubscriptionInfo) => boolean
29-
buySubscription: (price: Price, quantity?: number) => void
23+
buySubscription: (price: Price) => void
3024
activeSubscriptions: SubscriptionInfo[]
3125
}
3226

@@ -43,9 +37,8 @@ export const PlanContainer = ({
4337
buySubscription,
4438
activeSubscriptions,
4539
}: PlanContainerProps) => {
46-
const [submissionQuantity, setSubmissionQuantity] = useState(1)
47-
// display price for the plan/price/quantity we're currently displaying
48-
const displayPrice = useDisplayPrice(product.price, submissionQuantity)
40+
// display price for the plan/price we're currently displaying
41+
const displayPrice = useDisplayPrice(product.price)
4942
const shouldShowManage = useCallback(
5043
(product: SinglePricedProduct) => {
5144
const subscriptions = getSubscriptionsForProductId(product.id, state.subscribedProduct)
@@ -65,34 +58,7 @@ export const PlanContainer = ({
6558
[hasManageableStatus, state.subscribedProduct],
6659
)
6760

68-
const isDowngrading = useMemo(
69-
() => isDowngrade(activeSubscriptions, product.price, submissionQuantity),
70-
[activeSubscriptions, product, submissionQuantity],
71-
)
72-
73-
// The adjusted quantity is the number we multiply the price by to get the total price
74-
const adjustedQuantity = useMemo(
75-
() => getAdjustedQuantityForPrice(submissionQuantity, product.price.transform_quantity),
76-
[product, submissionQuantity],
77-
)
78-
79-
// Populate submission dropdown with the submission quantity from the customer's plan
80-
// Default to this price's base submission quantity, if applicable
81-
useEffect(() => {
82-
const subscribedQuantity = activeSubscriptions.length && activeSubscriptions?.[0].items[0].quantity
83-
if (subscribedQuantity && isSubscribedProduct(product, subscribedQuantity)) {
84-
setSubmissionQuantity(subscribedQuantity)
85-
} else if (
86-
// if there's no active subscription, check if this price has a default quantity
87-
product.price.transform_quantity &&
88-
Boolean(Number(product.metadata?.submission_limit) || Number(product.price.metadata?.submission_limit))
89-
) {
90-
// prioritize the submission limit from the price over the submission limit from the product
91-
setSubmissionQuantity(
92-
Number.parseInt(product.price.metadata.submission_limit) || Number.parseInt(product.metadata.submission_limit),
93-
)
94-
}
95-
}, [isSubscribedProduct, activeSubscriptions, product])
61+
const isDowngrading = useMemo(() => isDowngrade(activeSubscriptions, product.price), [activeSubscriptions, product])
9662

9763
const getFeatureMetadata = (product: SinglePricedProduct, featureItem: string) => {
9864
if (product.price.unit_amount === 0 && freeTierOverride && freeTierOverride.hasOwnProperty(featureItem)) {
@@ -171,57 +137,12 @@ export const PlanContainer = ({
171137
return renderFeaturesList(items, featureTitle)
172138
}
173139

174-
const submissionOptions = useMemo((): KoboSelectOption[] => {
175-
const options = []
176-
const submissionsPerUnit = product.price.metadata?.submission_limit || product.metadata?.submission_limit
177-
const maxPlanQuantity = Number.parseInt(product.price.metadata?.max_purchase_quantity || '1')
178-
if (submissionsPerUnit) {
179-
for (let i = 1; i <= maxPlanQuantity; i++) {
180-
const submissionCount = Number.parseInt(submissionsPerUnit) * i
181-
options.push({
182-
label: '##submissions## submissions /month'.replace('##submissions##', submissionCount.toLocaleString()),
183-
value: submissionCount.toString(),
184-
})
185-
}
186-
}
187-
return options
188-
}, [product])
189-
190-
const onSubmissionsChange = (value: string | null) => {
191-
if (value === null) {
192-
return
193-
}
194-
const submissions = Number.parseInt(value)
195-
if (submissions) {
196-
setSubmissionQuantity(submissions)
197-
}
198-
}
199-
200-
const asrMinutes = useMemo(
201-
() =>
202-
(adjustedQuantity *
203-
(Number.parseInt(product.metadata?.asr_seconds_limit || '0') ||
204-
Number.parseInt(product.price.metadata?.asr_seconds_limit || '0'))) /
205-
60,
206-
[adjustedQuantity, product],
207-
)
208-
209-
const mtCharacters = useMemo(
210-
() =>
211-
adjustedQuantity *
212-
(Number.parseInt(product.metadata?.mt_characters_limit || '0') ||
213-
Number.parseInt(product.price.metadata?.mt_characters_limit || '0')),
214-
[adjustedQuantity, product],
215-
)
216-
217140
return (
218141
<>
219-
{isSubscribedProduct(product, submissionQuantity) ? (
220-
<div className={styles.currentPlan}>{t('Your plan')}</div>
221-
) : null}
142+
{isSubscribedProduct(product) ? <div className={styles.currentPlan}>{t('Your plan')}</div> : null}
222143
<div
223144
className={classnames({
224-
[styles.planContainerWithBadge]: isSubscribedProduct(product, submissionQuantity),
145+
[styles.planContainerWithBadge]: isSubscribedProduct(product),
225146
[styles.planContainer]: true,
226147
})}
227148
>
@@ -231,37 +152,6 @@ export const PlanContainer = ({
231152
</h1>
232153
<div className={styles.priceTitle}>{displayPrice}</div>
233154
<ul className={styles.featureContainer}>
234-
{product.price.transform_quantity && (
235-
<>
236-
<li className={styles.selectableFeature}>
237-
<Icon name='check' size='m' color='teal' />
238-
<KoboSelect
239-
name={t('Total Submissions per Month')}
240-
options={submissionOptions}
241-
size={'s'}
242-
type={'outline'}
243-
onChange={onSubmissionsChange}
244-
selectedOption={submissionQuantity.toString()}
245-
/>
246-
</li>
247-
<li>
248-
<div className={styles.iconContainer}>
249-
<Icon name='check' size='m' color={product.price.unit_amount ? 'teal' : 'storm'} />
250-
</div>
251-
{t('##asr_minutes## minutes of automated transcription /##plan_interval##')
252-
.replace('##asr_minutes##', asrMinutes.toLocaleString())
253-
.replace('##plan_interval##', product.price.recurring!.interval)}
254-
</li>
255-
<li>
256-
<div className={styles.iconContainer}>
257-
<Icon name='check' size='m' color={product.price.unit_amount ? 'teal' : 'storm'} />
258-
</div>
259-
{t('##mt_characters## characters of machine translation /##plan_interval##')
260-
.replace('##mt_characters##', mtCharacters.toLocaleString())
261-
.replace('##plan_interval##', product.price.recurring!.interval)}
262-
</li>
263-
</>
264-
)}
265155
{recordKeys(product.metadata).map(
266156
(featureItem: string) =>
267157
featureItem.includes('feature_list_') && (
@@ -293,8 +183,7 @@ export const PlanContainer = ({
293183
<PlanButton
294184
product={product}
295185
downgrading={isDowngrading}
296-
quantity={submissionQuantity}
297-
isSubscribedToPlan={isSubscribedProduct(product, submissionQuantity)}
186+
isSubscribedToPlan={isSubscribedProduct(product)}
298187
buySubscription={buySubscription}
299188
showManage={shouldShowManage(product)}
300189
isBusy={isDisabled}
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { useMemo } from 'react'
22

33
import type { Price } from '#/account/stripe.types'
4-
import { getAdjustedQuantityForPrice } from '#/account/stripe.utils'
54

6-
export const useDisplayPrice = (price?: Price | null, submissionQuantity = 1) =>
5+
export const useDisplayPrice = (price?: Price | null) =>
76
useMemo(() => {
87
if (!price?.unit_amount) {
98
return t('Free')
@@ -12,9 +11,8 @@ export const useDisplayPrice = (price?: Price | null, submissionQuantity = 1) =>
1211
if (price?.recurring?.interval === 'year') {
1312
totalPrice /= 12
1413
}
15-
totalPrice *= getAdjustedQuantityForPrice(submissionQuantity, price.transform_quantity)
1614
if (!price?.recurring?.interval) {
1715
return t('$##price##').replace('##price##', totalPrice.toFixed(2))
1816
}
1917
return t('$##price## USD/month').replace('##price##', totalPrice.toFixed(2))
20-
}, [submissionQuantity, price])
18+
}, [price])

0 commit comments

Comments
 (0)