= ({ instanceStatus }) => {
- const { users, loading } = useUsers();
- const expired = trialHasExpired(instanceStatus);
- const { isPro } = useUiConfig();
-
+export const BillingPlan = () => {
const {
- currentPeriod,
- toChartData,
- toTrafficUsageSum,
- endpointsInfo,
- getDayLabels,
- calculateOverageCost,
- } = useTrafficDataEstimation();
+ uiConfig: { billing },
+ } = useUiConfig();
+ const { instanceStatus } = useInstanceStatus();
- const eligibleUsers = users.filter((user: any) => user.email);
+ const isPAYG = billing === 'pay-as-you-go';
- const price = {
- [InstancePlan.PRO]: 80,
- [InstancePlan.COMPANY]: 0,
- [InstancePlan.TEAM]: 0,
- [InstancePlan.ENTERPRISE]: 0,
- [InstancePlan.UNKNOWN]: 0,
- user: 15,
- };
-
- const planPrice = price[instanceStatus.plan];
- const seats = instanceStatus.seats ?? 5;
-
- const freeAssigned = Math.min(eligibleUsers.length, seats);
- const paidAssigned = eligibleUsers.length - freeAssigned;
- const paidAssignedPrice = price.user * paidAssigned;
- const includedTraffic = isPro() ? proPlanIncludedRequests : 0;
- const traffic = useInstanceTrafficMetrics(currentPeriod.key);
-
- const overageCost = useMemo(() => {
- if (!includedTraffic) {
- return 0;
- }
- const trafficData = toChartData(
- getDayLabels(currentPeriod.dayCount),
- traffic,
- endpointsInfo,
+ if (!instanceStatus)
+ return (
+
+
+
);
- const totalTraffic = toTrafficUsageSum(trafficData);
- return calculateOverageCost(totalTraffic, includedTraffic);
- }, [includedTraffic, traffic, currentPeriod, endpointsInfo]);
-
- const totalCost = planPrice + paidAssignedPrice + overageCost;
+ const expired = trialHasExpired(instanceStatus);
+ const planPrice = BILLING_PLAN_PRICES[instanceStatus.plan] ?? 0;
+ const plan = `${instanceStatus.plan}${isPAYG ? ' Pay-as-You-Go' : ''}`;
const inactive = instanceStatus.state !== InstanceState.ACTIVE;
- if (loading) return null;
-
return (
@@ -139,7 +90,9 @@ export const BillingPlan: FC = ({ instanceStatus }) => {
After you have sent your billing information, your
instance will be upgraded - you don't have to do
anything.{' '}
-
+
Get in touch with us
{' '}
for any clarification
@@ -147,10 +100,11 @@ export const BillingPlan: FC = ({ instanceStatus }) => {
}
/>
Current plan
-
- ({ marginBottom: theme.spacing(3) })}
- >
+ ({ marginBottom: theme.spacing(3) })}
+ >
+
{instanceStatus.plan}
@@ -185,134 +139,18 @@ export const BillingPlan: FC = ({ instanceStatus }) => {
/>
+
+ Pay-as-You-Go
+ }
+ />
+
-
-
- ({
- marginBottom: theme.spacing(1.5),
- })}
- >
-
-
- Included members
-
-
- {freeAssigned} of 5 assigned
-
-
-
-
- You have 5 team members included in
- your PRO plan
-
-
-
-
-
- included
-
-
-
- ({
- marginBottom: theme.spacing(1.5),
- })}
- >
-
-
- Paid members
-
-
- {paidAssigned} assigned
-
-
-
-
- $15/month per paid member
-
-
-
- ({
- fontSize:
- theme.fontSizes.mainHeader,
- })}
- >
- ${paidAssignedPrice.toFixed(2)}
-
-
-
- 0}
- show={
-
-
-
-
- Accrued traffic charges
-
-
-
- view details
-
-
-
-
- $5 dollar per 1 million
- started above included data
-
-
-
- ({
- fontSize:
- theme.fontSizes
- .mainHeader,
- })}
- >
- ${overageCost.toFixed(2)}
-
-
-
- }
- />
-
-
-
-
-
- ({
- fontWeight:
- theme.fontWeight.bold,
- fontSize:
- theme.fontSizes.mainHeader,
- })}
- >
- Total
-
-
-
- ({
- fontWeight:
- theme.fontWeight.bold,
- fontSize: '2rem',
- })}
- >
- ${totalCost.toFixed(2)}
-
-
-
-
- >
- }
+
diff --git a/frontend/src/component/admin/users/CreateUser/SeatCostWarning/SeatCostWarning.tsx b/frontend/src/component/admin/users/CreateUser/SeatCostWarning/SeatCostWarning.tsx
index 83a2a49f2a..0ef6df601a 100644
--- a/frontend/src/component/admin/users/CreateUser/SeatCostWarning/SeatCostWarning.tsx
+++ b/frontend/src/component/admin/users/CreateUser/SeatCostWarning/SeatCostWarning.tsx
@@ -2,6 +2,7 @@ import type { VFC } from 'react';
import { Alert } from '@mui/material';
import { useUsersPlan } from 'hooks/useUsersPlan';
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
+import { BILLING_PRO_USER_PRICE } from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan';
export const SeatCostWarning: VFC = () => {
const { users } = useUsers();
@@ -19,7 +20,8 @@ export const SeatCostWarning: VFC = () => {
Heads up! You are exceeding your allocated free
members included in your plan ({planUsers.length} of {seats}).
- Creating this user will add $15/month to your
+ Creating this user will add{' '}
+ ${BILLING_PRO_USER_PRICE}/month to your
invoice, starting with your next payment.
diff --git a/frontend/src/component/demo/DemoDialog/DemoDialogPlans/DemoDialogPlans.tsx b/frontend/src/component/demo/DemoDialog/DemoDialogPlans/DemoDialogPlans.tsx
index dd5d4fa9e7..6728914ea5 100644
--- a/frontend/src/component/demo/DemoDialog/DemoDialogPlans/DemoDialogPlans.tsx
+++ b/frontend/src/component/demo/DemoDialog/DemoDialogPlans/DemoDialogPlans.tsx
@@ -4,6 +4,13 @@ import GitHub from '@mui/icons-material/GitHub';
import Launch from '@mui/icons-material/Launch';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { useUiFlag } from 'hooks/useUiFlag';
+import {
+ BILLING_PAYG_DEFAULT_MINIMUM_SEATS,
+ BILLING_PAYG_USER_PRICE,
+ BILLING_PLAN_PRICES,
+ BILLING_PRO_DEFAULT_INCLUDED_SEATS,
+} from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan';
+import { InstancePlan } from 'interfaces/instance';
const StyledDemoDialog = styled(DemoDialog)(({ theme }) => ({
'& .MuiDialog-paper': {
@@ -132,10 +139,11 @@ export const DemoDialogPlans = ({ open, onClose }: IDemoDialogPlansProps) => {
- $75 per user/month
+ ${BILLING_PAYG_USER_PRICE} per user/month
- 5 users minimum
+ {BILLING_PAYG_DEFAULT_MINIMUM_SEATS} users
+ minimum
{
- $80/month
+ ${BILLING_PLAN_PRICES[InstancePlan.PRO]}/month
- includes 5 seats
+ includes {BILLING_PRO_DEFAULT_INCLUDED_SEATS}{' '}
+ seats
;
@@ -61,8 +62,9 @@ export const OrderEnvironmentsDialogPricing: FC<
))}
- With Pro, there is a minimum of 5 users, meaning an additional
- environment will cost at least $50 per month.
+ With Pro, there is a minimum of{' '}
+ {BILLING_PRO_DEFAULT_INCLUDED_SEATS} users, meaning an
+ additional environment will cost at least $50 per month.
diff --git a/frontend/src/component/insights/componentsStat/UserSeats/UserSeats.test.tsx b/frontend/src/component/insights/componentsStat/UserSeats/UserSeats.test.tsx
index 8cfc96cabc..eb28359636 100644
--- a/frontend/src/component/insights/componentsStat/UserSeats/UserSeats.test.tsx
+++ b/frontend/src/component/insights/componentsStat/UserSeats/UserSeats.test.tsx
@@ -2,19 +2,16 @@ import { testServerRoute, testServerSetup } from '../../../../utils/testServer';
import { screen } from '@testing-library/react';
import { render } from 'utils/testRenderer';
import { UserSeats } from './UserSeats';
+import { BILLING_PRO_DEFAULT_INCLUDED_SEATS } from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan';
const server = testServerSetup();
const user1 = {};
const user2 = {};
-const setupApiWithSeats = (seats: number | undefined) => {
+const setupApi = () => {
testServerRoute(server, '/api/admin/user-admin', {
users: [user1, user2],
});
- testServerRoute(server, '/api/instance/status', {
- plan: 'Enterprise',
- seats,
- });
testServerRoute(server, '/api/admin/ui-config', {
flags: {
UNLEASH_CLOUD: true,
@@ -23,18 +20,12 @@ const setupApiWithSeats = (seats: number | undefined) => {
};
test('User seats display when seats are available', async () => {
- setupApiWithSeats(20);
+ setupApi();
render( );
await screen.findByText('User seats');
- await screen.findByText('2/20 seats used');
-});
-
-test('User seats does not display when seats are not available', async () => {
- setupApiWithSeats(undefined);
-
- render( );
-
- expect(screen.queryByText('User seats')).not.toBeInTheDocument();
+ await screen.findByText(
+ `2/${BILLING_PRO_DEFAULT_INCLUDED_SEATS} seats used`,
+ );
});
diff --git a/frontend/src/component/insights/componentsStat/UserSeats/UserSeats.tsx b/frontend/src/component/insights/componentsStat/UserSeats/UserSeats.tsx
index bf79caec9e..5817eec4ee 100644
--- a/frontend/src/component/insights/componentsStat/UserSeats/UserSeats.tsx
+++ b/frontend/src/component/insights/componentsStat/UserSeats/UserSeats.tsx
@@ -2,7 +2,7 @@ import LicenseIcon from '@mui/icons-material/ReceiptLongOutlined';
import { Box, styled, Typography } from '@mui/material';
import LinearProgress from '@mui/material/LinearProgress';
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
-import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
+import { BILLING_PRO_DEFAULT_INCLUDED_SEATS } from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan';
const SeatsUsageBar = styled(LinearProgress)(({ theme }) => ({
marginTop: theme.spacing(0.5),
@@ -32,8 +32,7 @@ const SeatsUsageText = styled(Box)(({ theme }) => ({
export const UserSeats = () => {
const { users } = useUsers();
- const { instanceStatus } = useInstanceStatus();
- const seats = instanceStatus?.seats;
+ const seats = BILLING_PRO_DEFAULT_INCLUDED_SEATS;
if (typeof seats === 'number') {
const percentageSeats = Math.floor((users.length / seats) * 100);
diff --git a/frontend/src/hooks/api/getters/useInstanceStatus/useInstanceStatus.ts b/frontend/src/hooks/api/getters/useInstanceStatus/useInstanceStatus.ts
index c7bd17e09e..9b8b262fc4 100644
--- a/frontend/src/hooks/api/getters/useInstanceStatus/useInstanceStatus.ts
+++ b/frontend/src/hooks/api/getters/useInstanceStatus/useInstanceStatus.ts
@@ -37,7 +37,9 @@ export const useInstanceStatus = (): IUseInstanceStatusOutput => {
instanceStatus: data,
refetchInstanceStatus: refetch,
refresh,
- isBilling: billingPlans.includes(data?.plan ?? InstancePlan.UNKNOWN),
+ isBilling:
+ uiConfig.billing === 'pay-as-you-go' ||
+ billingPlans.includes(data?.plan ?? InstancePlan.UNKNOWN),
loading,
error,
};
diff --git a/frontend/src/hooks/useUsersPlan.ts b/frontend/src/hooks/useUsersPlan.ts
index c19d5e4195..e426ed2b11 100644
--- a/frontend/src/hooks/useUsersPlan.ts
+++ b/frontend/src/hooks/useUsersPlan.ts
@@ -2,7 +2,7 @@ import type { IUser } from 'interfaces/user';
import { useMemo } from 'react';
import { useInstanceStatus } from './api/getters/useInstanceStatus/useInstanceStatus';
import { InstancePlan } from 'interfaces/instance';
-import useUiConfig from './api/getters/useUiConfig/useUiConfig';
+import { BILLING_PRO_DEFAULT_INCLUDED_SEATS } from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan';
export interface IUsersPlanOutput {
planUsers: IUser[];
@@ -13,10 +13,9 @@ export interface IUsersPlanOutput {
export const useUsersPlan = (users: IUser[]): IUsersPlanOutput => {
const { instanceStatus } = useInstanceStatus();
- const { uiConfig } = useUiConfig();
const isBillingUsers = Boolean(instanceStatus?.plan === InstancePlan.PRO);
- const seats = instanceStatus?.seats ?? 5;
+ const seats = BILLING_PRO_DEFAULT_INCLUDED_SEATS;
const planUsers = useMemo(
() => calculatePaidUsers(users, isBillingUsers, seats),
diff --git a/frontend/src/interfaces/instance.ts b/frontend/src/interfaces/instance.ts
index cdd3397e6c..5580f2fd2c 100644
--- a/frontend/src/interfaces/instance.ts
+++ b/frontend/src/interfaces/instance.ts
@@ -6,6 +6,7 @@ export interface IInstanceStatus {
billingCenter?: string;
state?: InstanceState;
seats?: number;
+ minSeats?: number;
}
export enum InstanceState {