Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(1-3267): use new API for chart creation #9149

Merged
merged 19 commits into from
Jan 29, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -20,13 +20,17 @@ import {
} from 'chart.js';

import { Bar } from 'react-chartjs-2';
import { useInstanceTrafficMetrics } from 'hooks/api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
import {
useInstanceTrafficMetrics,
useInstanceTrafficMetrics2,
} from 'hooks/api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
import type { Theme } from '@mui/material/styles/createTheme';
import Grid from '@mui/material/Grid';
import { NetworkTrafficUsagePlanSummary } from './NetworkTrafficUsagePlanSummary';
import annotationPlugin from 'chartjs-plugin-annotation';
import {
type ChartDatasetType,
newToChartData,
useTrafficDataEstimation,
} from 'hooks/useTrafficData';
import { customHighlightPlugin } from 'component/common/Chart/customHighlightPlugin';
@@ -36,6 +40,8 @@ import { BILLING_TRAFFIC_BUNDLE_PRICE } from 'component/admin/billing/BillingDas
import { useLocationSettings } from 'hooks/useLocationSettings';
import { PeriodSelector } from './PeriodSelector';
import { useUiFlag } from 'hooks/useUiFlag';
import { format } from 'date-fns';
import { monthlyTrafficDataToCurrentUsage } from './monthly-traffic-data-to-current-usage';

const StyledBox = styled(Box)(({ theme }) => ({
display: 'grid',
@@ -148,10 +154,173 @@ const NewHeader = styled('div')(() => ({
alignItems: 'flex-start',
}));

const NewNetworkTrafficUsage: FC = () => {
usePageTitle('Network - Data Usage');
const theme = useTheme();

const { isOss } = useUiConfig();

const { locationSettings } = useLocationSettings();
const {
record,
newPeriod,
setNewPeriod,
toTrafficUsageSum,
calculateOverageCost,
calculateEstimatedMonthlyCost,
} = useTrafficDataEstimation();

const includedTraffic = useTrafficLimit();

const options = useMemo(() => {
return createBarChartOptions(
theme,
(tooltipItems: any) => {
if (newPeriod.grouping === 'daily') {
const periodItem = record[newPeriod.month];
const tooltipDate = new Date(
periodItem.year,
periodItem.month,
Number.parseInt(tooltipItems[0].label),
);
return tooltipDate.toLocaleDateString(
locationSettings?.locale ?? 'en-US',
{
month: 'long',
day: 'numeric',
year: 'numeric',
},
);
} else {
return new Date(tooltipItems[0].label).toLocaleDateString(
locationSettings?.locale ?? 'en-US',
{
month: 'long',
year: 'numeric',
},
);
}
},
includedTraffic,
);
}, [theme, newPeriod]);

const traffic = useInstanceTrafficMetrics2(newPeriod);

const data = newToChartData(traffic.usage);

const [usageTotal, setUsageTotal] = useState<number>(0);

const [overageCost, setOverageCost] = useState<number>(0);

const [estimatedMonthlyCost, setEstimatedMonthlyCost] = useState<number>(0);

useEffect(() => {
if (data) {
let usage: number;
if (newPeriod.grouping === 'monthly') {
usage = monthlyTrafficDataToCurrentUsage(traffic.usage);
} else {
usage = toTrafficUsageSum(data.datasets);
}

setUsageTotal(usage);
if (includedTraffic > 0) {
const calculatedOverageCost = calculateOverageCost(
usage,
includedTraffic,
BILLING_TRAFFIC_BUNDLE_PRICE,
);
setOverageCost(calculatedOverageCost);

setEstimatedMonthlyCost(
calculateEstimatedMonthlyCost(
newPeriod.grouping === 'daily'
? newPeriod.month
: format(new Date(), 'yyyy-MM'),
data.datasets,
includedTraffic,
new Date(),
BILLING_TRAFFIC_BUNDLE_PRICE,
),
);
}
}
}, [data]);

return (
<ConditionallyRender
condition={isOss()}
show={<Alert severity='warning'>Not enabled.</Alert>}
elseShow={
<>
<ConditionallyRender
condition={includedTraffic > 0 && overageCost > 0}
show={
<Alert severity='warning' sx={{ mb: 4 }}>
<b>Heads up!</b> You are currently consuming
more requests than your plan includes and will
be billed according to our terms. Please see{' '}
<Link
component={RouterLink}
to='https://www.getunleash.io/pricing'
>
this page
</Link>{' '}
for more information. In order to reduce your
traffic consumption, you may configure an{' '}
<Link
component={RouterLink}
to='https://docs.getunleash.io/reference/unleash-edge'
>
Unleash Edge instance
</Link>{' '}
in your own datacenter.
</Alert>
}
/>
<StyledBox>
<NewHeader>
{
// todo: add new usage plan summary that works for monthly data as well as daily
}

<NetworkTrafficUsagePlanSummary
usageTotal={usageTotal}
includedTraffic={includedTraffic}
overageCost={overageCost}
estimatedMonthlyCost={estimatedMonthlyCost}
/>
<PeriodSelector
selectedPeriod={newPeriod}
setPeriod={setNewPeriod}
/>
</NewHeader>
<Bar
data={data}
plugins={[customHighlightPlugin()]} // todo: accomodate wide bars when grouping by month
options={options}
aria-label='An instance metrics line chart with two lines: requests per second for admin API and requests per second for client API' // todo: this isn't correct at all!
/>
</StyledBox>
</>
}
/>
);
};

export const NetworkTrafficUsage: FC = () => {
const useNewNetworkTraffic = useUiFlag('dataUsageMultiMonthView');
return useNewNetworkTraffic ? (
<NewNetworkTrafficUsage />
) : (
<OldNetworkTrafficUsage />
);
};

const OldNetworkTrafficUsage: FC = () => {
usePageTitle('Network - Data Usage');
const theme = useTheme();
const showMultiMonthSelector = useUiFlag('dataUsageMultiMonthView');

const { isOss } = useUiConfig();

@@ -279,49 +448,31 @@ export const NetworkTrafficUsage: FC = () => {
}
/>
<StyledBox>
{showMultiMonthSelector ? (
<NewHeader>
<Grid container component='header' spacing={2}>
<Grid item xs={12} md={10}>
<NetworkTrafficUsagePlanSummary
usageTotal={usageTotal}
includedTraffic={includedTraffic}
overageCost={overageCost}
estimatedMonthlyCost={estimatedMonthlyCost}
/>
<PeriodSelector
selectedPeriod={period}
setPeriod={setPeriod}
</Grid>
<Grid item xs={12} md={2}>
<Select
id='dataperiod-select'
name='dataperiod'
options={selectablePeriods}
value={period}
onChange={(e) => setPeriod(e.target.value)}
style={{
minWidth: '100%',
marginBottom: theme.spacing(2),
}}
formControlStyles={{ width: '100%' }}
Comment on lines +451 to +471
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert to the old version. The flag has moved up a level, so we don't need to check it here anymore

/>
</NewHeader>
) : (
<Grid container component='header' spacing={2}>
<Grid item xs={12} md={10}>
<NetworkTrafficUsagePlanSummary
usageTotal={usageTotal}
includedTraffic={includedTraffic}
overageCost={overageCost}
estimatedMonthlyCost={
estimatedMonthlyCost
}
/>
</Grid>
<Grid item xs={12} md={2}>
<Select
id='dataperiod-select'
name='dataperiod'
options={selectablePeriods}
value={period}
onChange={(e) =>
setPeriod(e.target.value)
}
style={{
minWidth: '100%',
marginBottom: theme.spacing(2),
}}
formControlStyles={{ width: '100%' }}
/>
</Grid>
</Grid>
)}
</Grid>

<Grid item xs={12} md={2}>
<Bar
data={data}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { styled } from '@mui/material';
import { type FC, useState } from 'react';
import type { ChartDataSelection } from 'hooks/api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
import type { FC } from 'react';

export type Period = {
key: string;
@@ -147,25 +148,13 @@ type Selection =
};

type Props = {
selectedPeriod: string;
setPeriod: (period: string) => void;
selectedPeriod: ChartDataSelection;
setPeriod: (period: ChartDataSelection) => void;
};

export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
const selectablePeriods = getSelectablePeriods();

// this is for dev purposes; only to show how the design will work when you select a range.
const [tempOverride, setTempOverride] = useState<Selection | null>();

const select = (value: Selection) => {
if (value.type === 'month') {
setTempOverride(null);
setPeriod(value.value);
} else {
setTempOverride(value);
}
};

const rangeOptions = [3, 6, 12].map((monthsBack) => ({
value: monthsBack,
label: `Last ${monthsBack} months`,
@@ -183,17 +172,17 @@ export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
<li key={period.label}>
<button
className={
!tempOverride &&
period.key === selectedPeriod
selectedPeriod.grouping === 'daily' &&
period.key === selectedPeriod.month
? 'selected'
: ''
}
type='button'
disabled={!period.selectable}
onClick={() => {
select({
type: 'month',
value: period.key,
setPeriod({
grouping: 'daily',
month: period.key,
});
}}
>
@@ -211,16 +200,15 @@ export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
<li key={option.label}>
<button
className={
tempOverride &&
tempOverride.type === 'range' &&
option.value === tempOverride.monthsBack
selectedPeriod.grouping === 'monthly' &&
option.value === selectedPeriod.monthsBack
? 'selected'
: ''
}
type='button'
onClick={() => {
select({
type: 'range',
setPeriod({
grouping: 'monthly',
monthsBack: option.value,
});
}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { format } from 'date-fns';
import type { TrafficUsageDataSegmentedCombinedSchema } from 'openapi';

export const monthlyTrafficDataToCurrentUsage = (
usage?: TrafficUsageDataSegmentedCombinedSchema,
) => {
if (!usage) {
return 0;
}
const currentMonth = format(new Date(), 'yyyy-MM');
return usage.apiData.reduce((acc, current) => {
const currentPoint = current.dataPoints.find(
({ period }) => period === currentMonth,
);
const pointUsage =
currentPoint?.trafficTypes.reduce(
(acc, next) => acc + next.count,
0,
) ?? 0;
return acc + pointUsage;
}, 0);
};
Loading