diff --git a/frontend/public/locales/en/billings.json b/frontend/public/locales/en/billings.json new file mode 100644 index 0000000000..fb706e002f --- /dev/null +++ b/frontend/public/locales/en/billings.json @@ -0,0 +1,14 @@ +{ + "days_remaining": "days remaining in your billing period.", + "billing": "Billing", + "manage_billing_and_costs": "Manage your billing information, invoices, and monitor costs.", + "enterprise_cloud": "Enterprise Cloud", + "enterprise": "Enterprise", + "card_details_recieved_and_billing_info": "We have received your card details, your billing will only start after the end of your free trial period.", + "upgrade_plan": "Upgrade Plan", + "manage_billing": "Manage Billing", + "upgrade_now_text": "Upgrade now to have uninterrupted access", + "billing_start_info": "Your billing will start only after the trial period", + "checkout_plans": "Check out features in paid plans", + "here": "here" +} diff --git a/frontend/src/api/billing/getUsage.ts b/frontend/src/api/billing/getUsage.ts index 1cb5be5640..da7b6ebd63 100644 --- a/frontend/src/api/billing/getUsage.ts +++ b/frontend/src/api/billing/getUsage.ts @@ -13,6 +13,7 @@ export interface UsageResponsePayloadProps { billTotal: number; }; discount: number; + subscriptionStatus?: string; } const getUsage = async ( diff --git a/frontend/src/container/BillingContainer/BillingContainer.test.tsx b/frontend/src/container/BillingContainer/BillingContainer.test.tsx index cd447e5d60..1988df313b 100644 --- a/frontend/src/container/BillingContainer/BillingContainer.test.tsx +++ b/frontend/src/container/BillingContainer/BillingContainer.test.tsx @@ -56,14 +56,14 @@ describe('BillingContainer', () => { expect(cost).toBeInTheDocument(); const manageBilling = screen.getByRole('button', { - name: /manage billing/i, + name: 'manage_billing', }); expect(manageBilling).toBeInTheDocument(); const dollar = screen.getByText(/\$0/i); expect(dollar).toBeInTheDocument(); - const currentBill = screen.getByText('Billing'); + const currentBill = screen.getByText('billing'); expect(currentBill).toBeInTheDocument(); }); @@ -75,7 +75,7 @@ describe('BillingContainer', () => { const freeTrailText = await screen.findByText('Free Trial'); expect(freeTrailText).toBeInTheDocument(); - const currentBill = screen.getByText('Billing'); + const currentBill = screen.getByText('billing'); expect(currentBill).toBeInTheDocument(); const dollar0 = await screen.findByText(/\$0/i); @@ -85,18 +85,14 @@ describe('BillingContainer', () => { ); expect(onTrail).toBeInTheDocument(); - const numberOfDayRemaining = await screen.findByText( - /1 days remaining in your billing period./i, - ); + const numberOfDayRemaining = await screen.findByText(/1 days_remaining/i); expect(numberOfDayRemaining).toBeInTheDocument(); const upgradeButton = await screen.findAllByRole('button', { - name: /upgrade/i, + name: /upgrade_plan/i, }); expect(upgradeButton[1]).toBeInTheDocument(); expect(upgradeButton.length).toBe(2); - const checkPaidPlan = await screen.findByText( - /Check out features in paid plans/i, - ); + const checkPaidPlan = await screen.findByText(/checkout_plans/i); expect(checkPaidPlan).toBeInTheDocument(); const link = screen.getByRole('link', { name: /here/i }); @@ -114,7 +110,7 @@ describe('BillingContainer', () => { render(); }); - const currentBill = screen.getByText('Billing'); + const currentBill = screen.getByText('billing'); expect(currentBill).toBeInTheDocument(); const dollar0 = await screen.findByText(/\$0/i); @@ -126,17 +122,17 @@ describe('BillingContainer', () => { expect(onTrail).toBeInTheDocument(); const receivedCardDetails = await screen.findByText( - /We have received your card details, your billing will only start after the end of your free trial period./i, + /card_details_recieved_and_billing_info/i, ); expect(receivedCardDetails).toBeInTheDocument(); const manageBillingButton = await screen.findByRole('button', { - name: /manage billing/i, + name: /manage_billing/i, }); expect(manageBillingButton).toBeInTheDocument(); const dayRemainingInBillingPeriod = await screen.findByText( - /1 days remaining in your billing period./i, + /1 days_remaining/i, ); expect(dayRemainingInBillingPeriod).toBeInTheDocument(); }); @@ -156,7 +152,7 @@ describe('BillingContainer', () => { const billingPeriod = await findByText(billingPeriodText); expect(billingPeriod).toBeInTheDocument(); - const currentBill = screen.getByText('Billing'); + const currentBill = screen.getByText('billing'); expect(currentBill).toBeInTheDocument(); const dollar0 = await screen.findByText(/\$1,278.3/i); @@ -181,7 +177,7 @@ describe('BillingContainer', () => { ); render(); const dayRemainingInBillingPeriod = await screen.findByText( - /11 days remaining in your billing period./i, + /11 days_remaining/i, ); expect(dayRemainingInBillingPeriod).toBeInTheDocument(); }); diff --git a/frontend/src/container/BillingContainer/BillingContainer.tsx b/frontend/src/container/BillingContainer/BillingContainer.tsx index b31f9c4745..fe784a0c57 100644 --- a/frontend/src/container/BillingContainer/BillingContainer.tsx +++ b/frontend/src/container/BillingContainer/BillingContainer.tsx @@ -17,7 +17,7 @@ import { } from 'antd'; import { ColumnsType } from 'antd/es/table'; import updateCreditCardApi from 'api/billing/checkout'; -import getUsage from 'api/billing/getUsage'; +import getUsage, { UsageResponsePayloadProps } from 'api/billing/getUsage'; import manageCreditCardApi from 'api/billing/manage'; import Spinner from 'components/Spinner'; import { SOMETHING_WENT_WRONG } from 'constants/api'; @@ -28,6 +28,7 @@ import useLicense from 'hooks/useLicense'; import { useNotifications } from 'hooks/useNotifications'; import { pick } from 'lodash-es'; import { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useMutation, useQuery } from 'react-query'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; @@ -49,6 +50,11 @@ interface DataType { cost: string; } +enum SubscriptionStatus { + PastDue = 'past_due', + Active = 'active', +} + const renderSkeletonInput = (): JSX.Element => ( = [ }, ]; +// eslint-disable-next-line sonarjs/cognitive-complexity export default function BillingContainer(): JSX.Element { - const daysRemainingStr = 'days remaining in your billing period.'; + const { t } = useTranslation(['billings']); + const daysRemainingStr = t('days_remaining'); const [headerText, setHeaderText] = useState(''); const [billAmount, setBillAmount] = useState(0); const [activeLicense, setActiveLicense] = useState(null); const [daysRemaining, setDaysRemaining] = useState(0); const [isFreeTrial, setIsFreeTrial] = useState(false); const [data, setData] = useState([]); - const [apiResponse, setApiResponse] = useState({}); + const [apiResponse, setApiResponse] = useState< + Partial + >({}); const { trackEvent } = useAnalytics(); @@ -186,6 +196,9 @@ export default function BillingContainer(): JSX.Element { [licensesData?.payload?.onTrial], ); + const isSubscriptionPastDue = + apiResponse.subscriptionStatus === SubscriptionStatus.PastDue; + const { isLoading, isFetching: isFetchingBillingData } = useQuery( [REACT_QUERY_KEY.GET_BILLING_USAGE, user?.userId], { @@ -342,14 +355,27 @@ export default function BillingContainer(): JSX.Element { [apiResponse, billAmount, isLoading, isFetchingBillingData], ); + const { Text } = Typography; + const subscriptionPastDueMessage = (): JSX.Element => ( + + {`We were not able to process payments for your account. Please update your card details `} + + {t('here')} + + {` if your payment information has changed. Email us at `} + cloud-support@signoz.io + {` otherwise. Be sure to provide this information immediately to avoid interruption to your service.`} + + ); + return (
- Billing + {t('billing')} - Manage your billing information, invoices, and monitor costs. + {t('manage_billing_and_costs')} @@ -361,7 +387,7 @@ export default function BillingContainer(): JSX.Element { - {isCloudUserVal ? 'Enterprise Cloud' : 'Enterprise'}{' '} + {isCloudUserVal ? t('enterprise_cloud') : t('enterprise')}{' '} {isFreeTrial ? Free Trial : ''} {!isLoading && !isFetchingBillingData ? ( @@ -378,8 +404,8 @@ export default function BillingContainer(): JSX.Element { onClick={handleBilling} > {isFreeTrial && !licensesData?.payload?.trialConvertedToSubscription - ? 'Upgrade Plan' - : 'Manage Billing'} + ? t('upgrade_plan') + : t('manage_billing')} @@ -389,8 +415,7 @@ export default function BillingContainer(): JSX.Element { ellipsis style={{ fontWeight: '300', color: '#49aa19', fontSize: 12 }} > - We have received your card details, your billing will only start after - the end of your free trial period. + {t('card_details_recieved_and_billing_info')} )} @@ -404,6 +429,18 @@ export default function BillingContainer(): JSX.Element { ) : ( )} + + {isSubscriptionPastDue && + (!isLoading && !isFetchingBillingData ? ( + + ) : ( + + ))} @@ -434,16 +471,16 @@ export default function BillingContainer(): JSX.Element { - Upgrade now to have uninterrupted access + {t('upgrade_now_text')} - Your billing will start only after the trial period + {t('Your billing will start only after the trial period')} - Check out features in paid plans   + {t('checkout_plans')}   - here + {t('here')} @@ -464,7 +501,7 @@ export default function BillingContainer(): JSX.Element { loading={isLoadingBilling || isLoadingManageBilling} onClick={handleBilling} > - Upgrade Plan + {t('upgrade_plan')}