mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-25 00:34:26 +08:00
feat: [SIG-566]: Added message to alert user about their past due - subscription status (#4724)
* feat: [SIG-566]: Added message to alert user about their past due - subscription status * feat: [SIG-566]: Added message string to billings.json * feat: [SIG-566]: Added strings to billings.json * feat: [SIG-566]: updated test cases * feat: [SIG-566]: updated message text * feat: [SIG-566]: code fix * feat: [SIG-566]: code fix
This commit is contained in:
parent
e1679790f7
commit
ad1b01f225
14
frontend/public/locales/en/billings.json
Normal file
14
frontend/public/locales/en/billings.json
Normal file
@ -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"
|
||||
}
|
@ -13,6 +13,7 @@ export interface UsageResponsePayloadProps {
|
||||
billTotal: number;
|
||||
};
|
||||
discount: number;
|
||||
subscriptionStatus?: string;
|
||||
}
|
||||
|
||||
const getUsage = async (
|
||||
|
@ -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(<BillingContainer />);
|
||||
});
|
||||
|
||||
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(<BillingContainer />);
|
||||
const dayRemainingInBillingPeriod = await screen.findByText(
|
||||
/11 days remaining in your billing period./i,
|
||||
/11 days_remaining/i,
|
||||
);
|
||||
expect(dayRemainingInBillingPeriod).toBeInTheDocument();
|
||||
});
|
||||
|
@ -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 => (
|
||||
<Skeleton.Input
|
||||
style={{ marginTop: '10px', height: '40px', width: '100%' }}
|
||||
@ -116,15 +122,19 @@ const dummyColumns: ColumnsType<DataType> = [
|
||||
},
|
||||
];
|
||||
|
||||
// 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<License | null>(null);
|
||||
const [daysRemaining, setDaysRemaining] = useState(0);
|
||||
const [isFreeTrial, setIsFreeTrial] = useState(false);
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [apiResponse, setApiResponse] = useState<any>({});
|
||||
const [apiResponse, setApiResponse] = useState<
|
||||
Partial<UsageResponsePayloadProps>
|
||||
>({});
|
||||
|
||||
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 => (
|
||||
<Typography>
|
||||
{`We were not able to process payments for your account. Please update your card details `}
|
||||
<Text type="danger" onClick={handleBilling} style={{ cursor: 'pointer' }}>
|
||||
{t('here')}
|
||||
</Text>
|
||||
{` if your payment information has changed. Email us at `}
|
||||
<Text type="secondary">cloud-support@signoz.io</Text>
|
||||
{` otherwise. Be sure to provide this information immediately to avoid interruption to your service.`}
|
||||
</Typography>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="billing-container">
|
||||
<Flex vertical style={{ marginBottom: 16 }}>
|
||||
<Typography.Text style={{ fontWeight: 500, fontSize: 18 }}>
|
||||
Billing
|
||||
{t('billing')}
|
||||
</Typography.Text>
|
||||
<Typography.Text color={Color.BG_VANILLA_400}>
|
||||
Manage your billing information, invoices, and monitor costs.
|
||||
{t('manage_billing_and_costs')}
|
||||
</Typography.Text>
|
||||
</Flex>
|
||||
|
||||
@ -361,7 +387,7 @@ export default function BillingContainer(): JSX.Element {
|
||||
<Flex justify="space-between" align="center">
|
||||
<Flex vertical>
|
||||
<Typography.Title level={5} style={{ marginTop: 2, fontWeight: 500 }}>
|
||||
{isCloudUserVal ? 'Enterprise Cloud' : 'Enterprise'}{' '}
|
||||
{isCloudUserVal ? t('enterprise_cloud') : t('enterprise')}{' '}
|
||||
{isFreeTrial ? <Tag color="success"> Free Trial </Tag> : ''}
|
||||
</Typography.Title>
|
||||
{!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')}
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
@ -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')}
|
||||
</Typography.Text>
|
||||
)}
|
||||
|
||||
@ -404,6 +429,18 @@ export default function BillingContainer(): JSX.Element {
|
||||
) : (
|
||||
<Skeleton.Input active style={{ height: 20, marginTop: 20 }} />
|
||||
)}
|
||||
|
||||
{isSubscriptionPastDue &&
|
||||
(!isLoading && !isFetchingBillingData ? (
|
||||
<Alert
|
||||
message={subscriptionPastDueMessage()}
|
||||
type="error"
|
||||
showIcon
|
||||
style={{ marginTop: 12 }}
|
||||
/>
|
||||
) : (
|
||||
<Skeleton.Input active style={{ height: 20, marginTop: 20 }} />
|
||||
))}
|
||||
</Card>
|
||||
|
||||
<BillingUsageGraphCallback />
|
||||
@ -434,16 +471,16 @@ export default function BillingContainer(): JSX.Element {
|
||||
<Col span={20} className="plan-benefits">
|
||||
<Typography.Text className="plan-benefit">
|
||||
<CheckCircleOutlined />
|
||||
Upgrade now to have uninterrupted access
|
||||
{t('upgrade_now_text')}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="plan-benefit">
|
||||
<CheckCircleOutlined />
|
||||
Your billing will start only after the trial period
|
||||
{t('Your billing will start only after the trial period')}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="plan-benefit">
|
||||
<CheckCircleOutlined />
|
||||
<span>
|
||||
Check out features in paid plans
|
||||
{t('checkout_plans')}
|
||||
<a
|
||||
href="https://signoz.io/pricing/"
|
||||
style={{
|
||||
@ -452,7 +489,7 @@ export default function BillingContainer(): JSX.Element {
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
here
|
||||
{t('here')}
|
||||
</a>
|
||||
</span>
|
||||
</Typography.Text>
|
||||
@ -464,7 +501,7 @@ export default function BillingContainer(): JSX.Element {
|
||||
loading={isLoadingBilling || isLoadingManageBilling}
|
||||
onClick={handleBilling}
|
||||
>
|
||||
Upgrade Plan
|
||||
{t('upgrade_plan')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
|
Loading…
x
Reference in New Issue
Block a user