mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-27 11:02:16 +01:00
feat: flag traffic billing display feature (#10718)
Feature flag and initial changes in Billing UI
This commit is contained in:
parent
df67c041fc
commit
9b5324ac92
@ -4,15 +4,18 @@ import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
|
import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
|
||||||
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
|
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
|
||||||
import { Alert } from '@mui/material';
|
import { Alert, Box } from '@mui/material';
|
||||||
import { BillingDashboard } from './BillingDashboard/BillingDashboard.tsx';
|
import { BillingDashboard } from './BillingDashboard/BillingDashboard.tsx';
|
||||||
import { BillingHistory } from './BillingHistory/BillingHistory.tsx';
|
import { BillingHistory } from './BillingHistory/BillingHistory.tsx';
|
||||||
import useInvoices from 'hooks/api/getters/useInvoices/useInvoices';
|
import useInvoices from 'hooks/api/getters/useInvoices/useInvoices';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
import { BillingInvoices } from './BillingInvoices/BillingInvoices.tsx';
|
||||||
|
|
||||||
export const Billing = () => {
|
export const Billing = () => {
|
||||||
const { isBilling, refetchInstanceStatus, refresh, loading } =
|
const { isBilling, refetchInstanceStatus, refresh, loading } =
|
||||||
useInstanceStatus();
|
useInstanceStatus();
|
||||||
const { invoices } = useInvoices();
|
const { invoices } = useInvoices();
|
||||||
|
const trafficBillingDisplay = useUiFlag('trafficBillingDisplay');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const hardRefresh = async () => {
|
const hardRefresh = async () => {
|
||||||
@ -22,6 +25,21 @@ export const Billing = () => {
|
|||||||
hardRefresh();
|
hardRefresh();
|
||||||
}, [refetchInstanceStatus, refresh]);
|
}, [refetchInstanceStatus, refresh]);
|
||||||
|
|
||||||
|
if (trafficBillingDisplay) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={(theme) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(4),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<BillingDashboard />
|
||||||
|
<BillingInvoices />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageContent header='Billing' isLoading={loading}>
|
<PageContent header='Billing' isLoading={loading}>
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
import { Grid } from '@mui/material';
|
import { Grid } from '@mui/material';
|
||||||
import { BillingInformation } from './BillingInformation/BillingInformation.tsx';
|
import { BillingInformation } from './BillingInformation/BillingInformation.tsx';
|
||||||
import { BillingPlan } from './BillingPlan/BillingPlan.tsx';
|
import { BillingPlan } from './BillingPlan/BillingPlan.tsx';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
||||||
|
|
||||||
export const BillingDashboard = () => {
|
export const BillingDashboard = () => {
|
||||||
|
const trafficBillingDisplay = useUiFlag('trafficBillingDisplay');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={4}>
|
<Grid container spacing={trafficBillingDisplay ? 2 : 4}>
|
||||||
<BillingInformation />
|
<BillingInformation />
|
||||||
<BillingPlan />
|
<BillingPlan />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -1,17 +1,28 @@
|
|||||||
import { Alert, Divider, Grid, styled, Typography } from '@mui/material';
|
import { Alert, Divider, Grid, Paper, styled, Typography } from '@mui/material';
|
||||||
import { BillingInformationButton } from './BillingInformationButton/BillingInformationButton.tsx';
|
import { BillingInformationButton } from './BillingInformationButton/BillingInformationButton.tsx';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { InstanceState } from 'interfaces/instance';
|
import { InstanceState } from 'interfaces/instance';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
|
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
||||||
|
|
||||||
const StyledInfoBox = styled('aside')(({ theme }) => ({
|
/**
|
||||||
|
* @deprecated remove with `trafficBillingDisplay` flag
|
||||||
|
*/
|
||||||
|
const LegacyStyledInfoBox = styled('aside')(({ theme }) => ({
|
||||||
padding: theme.spacing(4),
|
padding: theme.spacing(4),
|
||||||
height: '100%',
|
height: '100%',
|
||||||
borderRadius: theme.shape.borderRadiusLarge,
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
backgroundColor: theme.palette.background.elevation2,
|
backgroundColor: theme.palette.background.elevation2,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const StyledInfoBox = styled(Paper)(({ theme }) => ({
|
||||||
|
padding: theme.spacing(4),
|
||||||
|
height: '100%',
|
||||||
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
}));
|
||||||
|
|
||||||
const StyledTitle = styled(Typography)(({ theme }) => ({
|
const StyledTitle = styled(Typography)(({ theme }) => ({
|
||||||
marginBottom: theme.spacing(4),
|
marginBottom: theme.spacing(4),
|
||||||
}));
|
}));
|
||||||
@ -36,11 +47,15 @@ export const BillingInformation = () => {
|
|||||||
uiConfig: { billing },
|
uiConfig: { billing },
|
||||||
} = useUiConfig();
|
} = useUiConfig();
|
||||||
const isPAYG = billing === 'pay-as-you-go';
|
const isPAYG = billing === 'pay-as-you-go';
|
||||||
|
const trafficBillingDisplay = useUiFlag('trafficBillingDisplay');
|
||||||
|
const StyledWrapper = trafficBillingDisplay
|
||||||
|
? StyledInfoBox
|
||||||
|
: LegacyStyledInfoBox;
|
||||||
|
|
||||||
if (!instanceStatus)
|
if (!instanceStatus)
|
||||||
return (
|
return (
|
||||||
<Grid item xs={12} md={5}>
|
<Grid item xs={12} md={5}>
|
||||||
<StyledInfoBox data-loading sx={{ flex: 1, height: '400px' }} />
|
<StyledWrapper data-loading sx={{ flex: 1, height: '400px' }} />
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -50,7 +65,7 @@ export const BillingInformation = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid item xs={12} md={5}>
|
<Grid item xs={12} md={5}>
|
||||||
<StyledInfoBox>
|
<StyledWrapper>
|
||||||
<StyledTitle variant='body1'>Billing information</StyledTitle>
|
<StyledTitle variant='body1'>Billing information</StyledTitle>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(isCustomBilling)}
|
condition={Boolean(isCustomBilling)}
|
||||||
@ -89,7 +104,7 @@ export const BillingInformation = () => {
|
|||||||
</a>{' '}
|
</a>{' '}
|
||||||
for any clarification
|
for any clarification
|
||||||
</StyledInfoLabel>
|
</StyledInfoLabel>
|
||||||
</StyledInfoBox>
|
</StyledWrapper>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Alert, Grid, styled } from '@mui/material';
|
import { Alert, Grid, Paper, styled } from '@mui/material';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { InstancePlan, InstanceState } from 'interfaces/instance';
|
import { InstancePlan, InstanceState } from 'interfaces/instance';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
@ -8,6 +8,7 @@ import { GridCol } from 'component/common/GridCol/GridCol';
|
|||||||
import { Badge } from 'component/common/Badge/Badge';
|
import { Badge } from 'component/common/Badge/Badge';
|
||||||
import { BillingDetails } from './BillingDetails.tsx';
|
import { BillingDetails } from './BillingDetails.tsx';
|
||||||
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
|
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
||||||
|
|
||||||
export const BILLING_PRO_BASE_PRICE = 80;
|
export const BILLING_PRO_BASE_PRICE = 80;
|
||||||
export const BILLING_PRO_SEAT_PRICE = 15;
|
export const BILLING_PRO_SEAT_PRICE = 15;
|
||||||
@ -18,7 +19,10 @@ export const BILLING_PAYG_DEFAULT_MINIMUM_SEATS = 5;
|
|||||||
export const BILLING_PRO_DEFAULT_INCLUDED_SEATS = 5;
|
export const BILLING_PRO_DEFAULT_INCLUDED_SEATS = 5;
|
||||||
export const BILLING_INCLUDED_REQUESTS = 53_000_000;
|
export const BILLING_INCLUDED_REQUESTS = 53_000_000;
|
||||||
|
|
||||||
const StyledPlanBox = styled('aside')(({ theme }) => ({
|
/**
|
||||||
|
* @deprecated remove with `trafficBillingDisplay` flag
|
||||||
|
*/
|
||||||
|
const LegacyStyledPlanBox = styled('aside')(({ theme }) => ({
|
||||||
padding: theme.spacing(2.5),
|
padding: theme.spacing(2.5),
|
||||||
height: '100%',
|
height: '100%',
|
||||||
borderRadius: theme.shape.borderRadiusLarge,
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
@ -28,6 +32,13 @@ const StyledPlanBox = styled('aside')(({ theme }) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const StyledPlanBox = styled(Paper)(({ theme }) => ({
|
||||||
|
padding: theme.spacing(4),
|
||||||
|
height: '100%',
|
||||||
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
}));
|
||||||
|
|
||||||
const StyledPlanSpan = styled('span')(({ theme }) => ({
|
const StyledPlanSpan = styled('span')(({ theme }) => ({
|
||||||
fontSize: '3.25rem',
|
fontSize: '3.25rem',
|
||||||
lineHeight: 1,
|
lineHeight: 1,
|
||||||
@ -63,6 +74,7 @@ export const BillingPlan = () => {
|
|||||||
const {
|
const {
|
||||||
uiConfig: { billing },
|
uiConfig: { billing },
|
||||||
} = useUiConfig();
|
} = useUiConfig();
|
||||||
|
const trafficBillingDisplay = useUiFlag('trafficBillingDisplay');
|
||||||
const { instanceStatus } = useInstanceStatus();
|
const { instanceStatus } = useInstanceStatus();
|
||||||
|
|
||||||
const isPro =
|
const isPro =
|
||||||
@ -70,10 +82,14 @@ export const BillingPlan = () => {
|
|||||||
const isPAYG = billing === 'pay-as-you-go';
|
const isPAYG = billing === 'pay-as-you-go';
|
||||||
const isEnterpriseConsumption = billing === 'enterprise-consumption';
|
const isEnterpriseConsumption = billing === 'enterprise-consumption';
|
||||||
|
|
||||||
|
const StyledWrapper = trafficBillingDisplay
|
||||||
|
? StyledPlanBox
|
||||||
|
: LegacyStyledPlanBox;
|
||||||
|
|
||||||
if (!instanceStatus)
|
if (!instanceStatus)
|
||||||
return (
|
return (
|
||||||
<Grid item xs={12} md={7}>
|
<Grid item xs={12} md={7}>
|
||||||
<StyledPlanBox data-loading sx={{ flex: 1, height: '400px' }} />
|
<StyledWrapper data-loading sx={{ flex: 1, height: '400px' }} />
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -85,7 +101,7 @@ export const BillingPlan = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid item xs={12} md={7}>
|
<Grid item xs={12} md={7}>
|
||||||
<StyledPlanBox>
|
<StyledWrapper>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={inactive}
|
condition={inactive}
|
||||||
show={
|
show={
|
||||||
@ -161,7 +177,7 @@ export const BillingPlan = () => {
|
|||||||
isPAYG={isPAYG}
|
isPAYG={isPAYG}
|
||||||
isEnterpriseConsumption={isEnterpriseConsumption}
|
isEnterpriseConsumption={isEnterpriseConsumption}
|
||||||
/>
|
/>
|
||||||
</StyledPlanBox>
|
</StyledWrapper>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { Box, styled, Typography } from '@mui/material';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
const StyledContainer = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(3),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledHeader = styled(Typography)(({ theme }) => ({
|
||||||
|
fontSize: theme.fontSizes.mainHeader,
|
||||||
|
fontWeight: theme.fontWeight.semi,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
type BillingInvoicesProps = {};
|
||||||
|
|
||||||
|
export const BillingInvoices: FC<BillingInvoicesProps> = () => {
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
<StyledHeader>Usage and invoices</StyledHeader>
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -89,6 +89,7 @@ export type UiFlags = {
|
|||||||
newStrategyModal?: boolean;
|
newStrategyModal?: boolean;
|
||||||
globalChangeRequestList?: boolean;
|
globalChangeRequestList?: boolean;
|
||||||
flagsUiFilterRefactor?: boolean;
|
flagsUiFilterRefactor?: boolean;
|
||||||
|
trafficBillingDisplay?: boolean;
|
||||||
milestoneProgression?: boolean;
|
milestoneProgression?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -61,6 +61,7 @@ export type IFlagKey =
|
|||||||
| 'globalChangeRequestList'
|
| 'globalChangeRequestList'
|
||||||
| 'newUiConfigService'
|
| 'newUiConfigService'
|
||||||
| 'flagsUiFilterRefactor'
|
| 'flagsUiFilterRefactor'
|
||||||
|
| 'trafficBillingDisplay'
|
||||||
| 'milestoneProgression';
|
| 'milestoneProgression';
|
||||||
|
|
||||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
||||||
@ -278,6 +279,10 @@ const flags: IFlags = {
|
|||||||
process.env.UNLEASH_EXPERIMENTAL_FLAGS_UI_FILTER_REFACTOR,
|
process.env.UNLEASH_EXPERIMENTAL_FLAGS_UI_FILTER_REFACTOR,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
trafficBillingDisplay: parseEnvVarBoolean(
|
||||||
|
process.env.UNLEASH_EXPERIMENTAL_TRAFFIC_BILLING_DISPLAY,
|
||||||
|
false,
|
||||||
|
),
|
||||||
milestoneProgression: parseEnvVarBoolean(
|
milestoneProgression: parseEnvVarBoolean(
|
||||||
process.env.UNLEASH_EXPERIMENTAL_MILESTONE_PROGRESSION,
|
process.env.UNLEASH_EXPERIMENTAL_MILESTONE_PROGRESSION,
|
||||||
false,
|
false,
|
||||||
|
|||||||
@ -57,6 +57,7 @@ process.nextTick(async () => {
|
|||||||
globalChangeRequestList: true,
|
globalChangeRequestList: true,
|
||||||
newUiConfigService: true,
|
newUiConfigService: true,
|
||||||
flagsUiFilterRefactor: true,
|
flagsUiFilterRefactor: true,
|
||||||
|
trafficBillingDisplay: true,
|
||||||
milestoneProgression: true,
|
milestoneProgression: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user