diff --git a/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingDetailsPAYG.tsx b/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingDetailsPAYG.tsx index 69c854ffb2..15687e8bc5 100644 --- a/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingDetailsPAYG.tsx +++ b/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingDetailsPAYG.tsx @@ -8,8 +8,8 @@ import { useUsers } from 'hooks/api/getters/useUsers/useUsers'; import { BILLING_INCLUDED_REQUESTS, BILLING_PAYG_DEFAULT_MINIMUM_SEATS, - BILLING_PAYG_USER_PRICE, - BILLING_TRAFFIC_BUNDLE_PRICE, + BILLING_PAYG_SEAT_PRICE, + BILLING_TRAFFIC_PRICE, } from './BillingPlan'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { useOverageCost } from './useOverageCost'; @@ -34,11 +34,16 @@ export const BillingDetailsPAYG = ({ const eligibleUsers = users.filter((user) => user.email); + const seatPrice = + instanceStatus.prices?.payg?.seat ?? BILLING_PAYG_SEAT_PRICE; + const trafficPrice = + instanceStatus.prices?.payg?.traffic ?? BILLING_TRAFFIC_PRICE; + const minSeats = instanceStatus.minSeats ?? BILLING_PAYG_DEFAULT_MINIMUM_SEATS; const billableUsers = Math.max(eligibleUsers.length, minSeats); - const usersCost = BILLING_PAYG_USER_PRICE * billableUsers; + const usersCost = seatPrice * billableUsers; const includedTraffic = BILLING_INCLUDED_REQUESTS; const overageCost = useOverageCost(includedTraffic); @@ -66,7 +71,7 @@ export const BillingDetailsPAYG = ({ - ${BILLING_PAYG_USER_PRICE}/month per paid member + ${seatPrice}/month per paid member @@ -93,8 +98,8 @@ export const BillingDetailsPAYG = ({ - ${BILLING_TRAFFIC_BUNDLE_PRICE} per 1 - million started above included data + ${trafficPrice} per 1 million started above + included data diff --git a/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingDetailsPro.tsx b/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingDetailsPro.tsx index 84ac700dcd..9e5280d87f 100644 --- a/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingDetailsPro.tsx +++ b/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingDetailsPro.tsx @@ -9,10 +9,10 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit import { useUsers } from 'hooks/api/getters/useUsers/useUsers'; import { BILLING_INCLUDED_REQUESTS, - BILLING_PLAN_PRICES, BILLING_PRO_DEFAULT_INCLUDED_SEATS, - BILLING_PRO_USER_PRICE, - BILLING_TRAFFIC_BUNDLE_PRICE, + BILLING_PRO_BASE_PRICE, + BILLING_PRO_SEAT_PRICE, + BILLING_TRAFFIC_PRICE, } from './BillingPlan'; import { useOverageCost } from './useOverageCost'; @@ -41,12 +41,17 @@ export const BillingDetailsPro = ({ const eligibleUsers = users.filter((user) => user.email); - const planPrice = BILLING_PLAN_PRICES[instanceStatus.plan]; + const planPrice = + instanceStatus.prices?.pro?.base ?? BILLING_PRO_BASE_PRICE; + const seatPrice = + instanceStatus.prices?.pro?.seat ?? BILLING_PRO_SEAT_PRICE; + const trafficPrice = + instanceStatus.prices?.pro?.traffic ?? BILLING_TRAFFIC_PRICE; const seats = BILLING_PRO_DEFAULT_INCLUDED_SEATS; const freeAssigned = Math.min(eligibleUsers.length, seats); const paidAssigned = eligibleUsers.length - freeAssigned; - const paidAssignedPrice = BILLING_PRO_USER_PRICE * paidAssigned; + const paidAssignedPrice = seatPrice * paidAssigned; const includedTraffic = BILLING_INCLUDED_REQUESTS; const overageCost = useOverageCost(includedTraffic); @@ -96,7 +101,7 @@ export const BillingDetailsPro = ({ - ${BILLING_PRO_USER_PRICE}/month per paid member + ${seatPrice}/month per paid member @@ -123,8 +128,8 @@ export const BillingDetailsPro = ({ - ${BILLING_TRAFFIC_BUNDLE_PRICE} per 1 - million started above included data + ${trafficPrice} per 1 million started above + included data diff --git a/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingPlan.tsx b/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingPlan.tsx index 24e5796a3f..a5b18253ef 100644 --- a/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingPlan.tsx +++ b/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/BillingPlan.tsx @@ -1,6 +1,6 @@ import { Alert, Grid, styled } from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { InstanceState, InstancePlan } from 'interfaces/instance'; +import { InstanceState } from 'interfaces/instance'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { trialHasExpired, isTrialInstance } from 'utils/instanceTrial'; import { GridRow } from 'component/common/GridRow/GridRow'; @@ -9,16 +9,14 @@ import { Badge } from 'component/common/Badge/Badge'; import { BillingDetails } from './BillingDetails'; import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus'; -export const BILLING_PLAN_PRICES: Record = { - [InstancePlan.PRO]: 80, -}; +export const BILLING_PRO_BASE_PRICE = 80; +export const BILLING_PRO_SEAT_PRICE = 15; +export const BILLING_PAYG_SEAT_PRICE = 75; +export const BILLING_TRAFFIC_PRICE = 5; -export const BILLING_PAYG_USER_PRICE = 75; export const BILLING_PAYG_DEFAULT_MINIMUM_SEATS = 5; -export const BILLING_PRO_USER_PRICE = 15; export const BILLING_PRO_DEFAULT_INCLUDED_SEATS = 5; export const BILLING_INCLUDED_REQUESTS = 53_000_000; -export const BILLING_TRAFFIC_BUNDLE_PRICE = 5; const StyledPlanBox = styled('aside')(({ theme }) => ({ padding: theme.spacing(2.5), @@ -77,7 +75,8 @@ export const BillingPlan = () => { ); const expired = trialHasExpired(instanceStatus); - const planPrice = BILLING_PLAN_PRICES[instanceStatus.plan] ?? 0; + const baseProPrice = + instanceStatus.prices?.pro?.base ?? BILLING_PRO_BASE_PRICE; const plan = `${instanceStatus.plan}${isPAYG ? ' Pay-as-You-Go' : ''}`; const inactive = instanceStatus.state !== InstanceState.ACTIVE; @@ -131,10 +130,10 @@ export const BillingPlan = () => { 0} + condition={!isPAYG && baseProPrice > 0} show={ - ${planPrice.toFixed(2)} + ${baseProPrice.toFixed(2)} } /> diff --git a/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/useOverageCost.ts b/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/useOverageCost.ts index 80ae41455c..1f29f95158 100644 --- a/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/useOverageCost.ts +++ b/frontend/src/component/admin/billing/BillingDashboard/BillingPlan/useOverageCost.ts @@ -5,7 +5,8 @@ import { calculateOverageCost, calculateTotalUsage, } from 'utils/traffic-calculations'; -import { BILLING_TRAFFIC_BUNDLE_PRICE } from './BillingPlan'; +import { BILLING_TRAFFIC_PRICE } from './BillingPlan'; +import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus'; export const useOverageCost = (includedTraffic: number) => { if (!includedTraffic) { @@ -17,6 +18,12 @@ export const useOverageCost = (includedTraffic: number) => { const from = formatDate(startOfMonth(now)); const to = formatDate(endOfMonth(now)); + const { instanceStatus } = useInstanceStatus(); + const trafficPrice = + instanceStatus?.prices?.[ + instanceStatus?.billing === 'pay-as-you-go' ? 'payg' : 'pro' + ]?.traffic ?? BILLING_TRAFFIC_PRICE; + const { result } = useTrafficSearch('daily', { from, to }); const overageCost = useMemo(() => { if (result.state !== 'success') { @@ -24,12 +31,8 @@ export const useOverageCost = (includedTraffic: number) => { } const totalUsage = calculateTotalUsage(result.data); - return calculateOverageCost( - totalUsage, - includedTraffic, - BILLING_TRAFFIC_BUNDLE_PRICE, - ); - }, [includedTraffic, JSON.stringify(result)]); + return calculateOverageCost(totalUsage, includedTraffic, trafficPrice); + }, [includedTraffic, JSON.stringify(result), trafficPrice]); return overageCost; }; diff --git a/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useStats.ts b/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useStats.ts index c34f4ab910..eec6922988 100644 --- a/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useStats.ts +++ b/frontend/src/component/admin/network/NetworkTrafficUsage/hooks/useStats.ts @@ -12,10 +12,11 @@ import { calculateOverageCost, calculateTotalUsage, } from 'utils/traffic-calculations'; -import { BILLING_TRAFFIC_BUNDLE_PRICE } from '../../../billing/BillingDashboard/BillingPlan/BillingPlan'; +import { BILLING_TRAFFIC_PRICE } from '../../../billing/BillingDashboard/BillingPlan/BillingPlan'; import { averageTrafficPreviousMonths } from '../average-traffic-previous-months'; import { useConnectionsConsumption } from 'hooks/api/getters/useConnectionsConsumption/useConnectionsConsumption'; import { useRequestsConsumption } from 'hooks/api/getters/useRequestsConsumption/useRequestsConsumption'; +import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus'; export const useTrafficStats = ( includedTraffic: number, @@ -26,6 +27,12 @@ export const useTrafficStats = ( chartDataSelection.grouping, toDateRange(chartDataSelection, currentDate), ); + const { instanceStatus } = useInstanceStatus(); + const trafficPrice = + instanceStatus?.prices?.[ + instanceStatus?.billing === 'pay-as-you-go' ? 'payg' : 'pro' + ]?.traffic ?? BILLING_TRAFFIC_PRICE; + const results = useMemo(() => { if (result.state !== 'success') { return { @@ -43,14 +50,14 @@ export const useTrafficStats = ( const overageCost = calculateOverageCost( usageTotal, includedTraffic, - BILLING_TRAFFIC_BUNDLE_PRICE, + trafficPrice, ); const estimatedMonthlyCost = calculateEstimatedMonthlyCost( traffic.apiData, includedTraffic, currentDate, - BILLING_TRAFFIC_BUNDLE_PRICE, + trafficPrice, ); const requestSummaryUsage = @@ -69,6 +76,7 @@ export const useTrafficStats = ( JSON.stringify(result), includedTraffic, JSON.stringify(chartDataSelection), + trafficPrice, ]); return results; diff --git a/frontend/src/component/admin/users/CreateUser/SeatCostWarning/SeatCostWarning.tsx b/frontend/src/component/admin/users/CreateUser/SeatCostWarning/SeatCostWarning.tsx index 0ef6df601a..62d20034c8 100644 --- a/frontend/src/component/admin/users/CreateUser/SeatCostWarning/SeatCostWarning.tsx +++ b/frontend/src/component/admin/users/CreateUser/SeatCostWarning/SeatCostWarning.tsx @@ -2,11 +2,15 @@ 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'; +import { BILLING_PRO_SEAT_PRICE } from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan'; +import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus'; export const SeatCostWarning: VFC = () => { const { users } = useUsers(); const { isBillingUsers, seats, planUsers } = useUsersPlan(users); + const { instanceStatus } = useInstanceStatus(); + const seatPrice = + instanceStatus?.prices?.pro?.seat ?? BILLING_PRO_SEAT_PRICE; if (!isBillingUsers || planUsers.length < seats) { return null; @@ -20,9 +24,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{' '} - ${BILLING_PRO_USER_PRICE}/month to your - invoice, starting with your next payment. + Creating this user will add ${seatPrice}/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 6728914ea5..9c517720e4 100644 --- a/frontend/src/component/demo/DemoDialog/DemoDialogPlans/DemoDialogPlans.tsx +++ b/frontend/src/component/demo/DemoDialog/DemoDialogPlans/DemoDialogPlans.tsx @@ -6,11 +6,10 @@ import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { useUiFlag } from 'hooks/useUiFlag'; import { BILLING_PAYG_DEFAULT_MINIMUM_SEATS, - BILLING_PAYG_USER_PRICE, - BILLING_PLAN_PRICES, + BILLING_PAYG_SEAT_PRICE, + BILLING_PRO_BASE_PRICE, BILLING_PRO_DEFAULT_INCLUDED_SEATS, } from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan'; -import { InstancePlan } from 'interfaces/instance'; const StyledDemoDialog = styled(DemoDialog)(({ theme }) => ({ '& .MuiDialog-paper': { @@ -139,7 +138,7 @@ export const DemoDialogPlans = ({ open, onClose }: IDemoDialogPlansProps) => {
- ${BILLING_PAYG_USER_PRICE} per user/month + ${BILLING_PAYG_SEAT_PRICE} per user/month {BILLING_PAYG_DEFAULT_MINIMUM_SEATS} users @@ -174,7 +173,7 @@ export const DemoDialogPlans = ({ open, onClose }: IDemoDialogPlansProps) => {
- ${BILLING_PLAN_PRICES[InstancePlan.PRO]}/month + ${BILLING_PRO_BASE_PRICE}/month includes {BILLING_PRO_DEFAULT_INCLUDED_SEATS}{' '} diff --git a/frontend/src/interfaces/instance.ts b/frontend/src/interfaces/instance.ts index 5a03e1e91b..c06eef0e63 100644 --- a/frontend/src/interfaces/instance.ts +++ b/frontend/src/interfaces/instance.ts @@ -1,3 +1,17 @@ +type InstancePrices = { + pro?: { + base?: number; + seat?: number; + traffic?: number; + }; + payg?: { + seat?: number; + traffic?: number; + }; +}; + +type InstanceBilling = 'pay-as-you-go' | 'subscription'; + export interface IInstanceStatus { plan: InstancePlan; trialExpiry?: string; @@ -8,6 +22,8 @@ export interface IInstanceStatus { seats?: number; minSeats?: number; isCustomBilling?: boolean; + prices?: InstancePrices; + billing?: InstanceBilling; } export enum InstanceState {