mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
feat: placeholder flag metrics chart (#8197)
This commit is contained in:
parent
0587203ad6
commit
87b997698b
@ -17,8 +17,6 @@ import {
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
type Chart,
|
||||
type Tick,
|
||||
} from 'chart.js';
|
||||
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
@ -32,43 +30,14 @@ import {
|
||||
type ChartDatasetType,
|
||||
useTrafficDataEstimation,
|
||||
} from 'hooks/useTrafficData';
|
||||
import { customHighlightPlugin } from 'component/common/Chart/customHighlightPlugin';
|
||||
import { formatTickValue } from 'component/common/Chart/formatTickValue';
|
||||
|
||||
const StyledBox = styled(Box)(({ theme }) => ({
|
||||
display: 'grid',
|
||||
gap: theme.spacing(5),
|
||||
}));
|
||||
|
||||
const customHighlightPlugin = {
|
||||
id: 'customLine',
|
||||
beforeDraw: (chart: Chart) => {
|
||||
const width = 46;
|
||||
if (chart.tooltip?.opacity && chart.tooltip.x) {
|
||||
const x = chart.tooltip.caretX;
|
||||
const yAxis = chart.scales.y;
|
||||
const ctx = chart.ctx;
|
||||
ctx.save();
|
||||
const gradient = ctx.createLinearGradient(
|
||||
x,
|
||||
yAxis.top,
|
||||
x,
|
||||
yAxis.bottom + 34,
|
||||
);
|
||||
gradient.addColorStop(0, 'rgba(129, 122, 254, 0)');
|
||||
gradient.addColorStop(1, 'rgba(129, 122, 254, 0.12)');
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.roundRect(
|
||||
x - width / 2,
|
||||
yAxis.top,
|
||||
width,
|
||||
yAxis.bottom - yAxis.top + 34,
|
||||
5,
|
||||
);
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const createBarChartOptions = (
|
||||
theme: Theme,
|
||||
tooltipTitleCallback: (tooltipItems: any) => string,
|
||||
@ -150,20 +119,7 @@ const createBarChartOptions = (
|
||||
ticks: {
|
||||
color: theme.palette.text.secondary,
|
||||
maxTicksLimit: 5,
|
||||
callback: (
|
||||
tickValue: string | number,
|
||||
index: number,
|
||||
ticks: Tick[],
|
||||
) => {
|
||||
if (typeof tickValue === 'string') {
|
||||
return tickValue;
|
||||
}
|
||||
const value = Number.parseInt(tickValue.toString());
|
||||
if (value > 999999) {
|
||||
return `${value / 1000000}M`;
|
||||
}
|
||||
return value > 999 ? `${value / 1000}k` : value;
|
||||
},
|
||||
callback: formatTickValue,
|
||||
},
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
|
32
frontend/src/component/common/Chart/customHighlightPlugin.ts
Normal file
32
frontend/src/component/common/Chart/customHighlightPlugin.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import type { Chart } from 'chart.js';
|
||||
|
||||
export const customHighlightPlugin = {
|
||||
id: 'customLine',
|
||||
beforeDraw: (chart: Chart) => {
|
||||
const width = 46;
|
||||
if (chart.tooltip?.opacity && chart.tooltip.x) {
|
||||
const x = chart.tooltip.caretX;
|
||||
const yAxis = chart.scales.y;
|
||||
const ctx = chart.ctx;
|
||||
ctx.save();
|
||||
const gradient = ctx.createLinearGradient(
|
||||
x,
|
||||
yAxis.top,
|
||||
x,
|
||||
yAxis.bottom + 34,
|
||||
);
|
||||
gradient.addColorStop(0, 'rgba(129, 122, 254, 0)');
|
||||
gradient.addColorStop(1, 'rgba(129, 122, 254, 0.12)');
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.roundRect(
|
||||
x - width / 2,
|
||||
yAxis.top,
|
||||
width,
|
||||
yAxis.bottom - yAxis.top + 34,
|
||||
5,
|
||||
);
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
},
|
||||
};
|
16
frontend/src/component/common/Chart/formatTickValue.ts
Normal file
16
frontend/src/component/common/Chart/formatTickValue.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import type { Tick } from 'chart.js';
|
||||
|
||||
export const formatTickValue = (
|
||||
tickValue: string | number,
|
||||
index: number,
|
||||
ticks: Tick[],
|
||||
) => {
|
||||
if (typeof tickValue === 'string') {
|
||||
return tickValue;
|
||||
}
|
||||
const value = Number.parseInt(tickValue.toString());
|
||||
if (value > 999999) {
|
||||
return `${value / 1000000}M`;
|
||||
}
|
||||
return value > 999 ? `${value / 1000}k` : value;
|
||||
};
|
126
frontend/src/component/personalDashboard/FlagMetricsChart.tsx
Normal file
126
frontend/src/component/personalDashboard/FlagMetricsChart.tsx
Normal file
@ -0,0 +1,126 @@
|
||||
import {
|
||||
BarElement,
|
||||
CategoryScale,
|
||||
Chart as ChartJS,
|
||||
type ChartOptions,
|
||||
Legend,
|
||||
LinearScale,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from 'chart.js';
|
||||
import annotationPlugin from 'chartjs-plugin-annotation';
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
import type { Theme } from '@mui/material/styles/createTheme';
|
||||
import useTheme from '@mui/material/styles/useTheme';
|
||||
import { useMemo } from 'react';
|
||||
import { formatTickValue } from 'component/common/Chart/formatTickValue';
|
||||
|
||||
const defaultYes = [
|
||||
45_000_000, 28_000_000, 28_000_000, 25_000_000, 50_000_000, 27_000_000,
|
||||
26_000_000, 50_000_000, 32_000_000, 12_000_000, 13_000_000, 31_000_000,
|
||||
12_000_000, 47_000_000, 29_000_000, 46_000_000, 45_000_000, 28_000_000,
|
||||
28_000_000, 25_000_000, 50_000_000, 27_000_000, 26_000_000, 50_000_000,
|
||||
32_000_000, 12_000_000, 13_000_000, 31_000_000, 12_000_000, 47_000_000,
|
||||
];
|
||||
const defaultNo = [
|
||||
5_000_000, 8_000_000, 3_000_000, 2_000_000, 2_000_000, 5_000_000, 9_000_000,
|
||||
3_000_000, 7_000_000, 2_000_000, 5_000_000, 8_000_000, 3_000_000, 2_000_000,
|
||||
2_000_000, 5_000_000, 1_000_000, 3_000_000, 12_000_000, 2_000_000,
|
||||
1_000_000, 1_000_000, 3_000_000, 2_000_000, 2_000_000, 5_000_000, 1_000_000,
|
||||
3_000_000, 8_000_000, 2_000_000,
|
||||
];
|
||||
|
||||
const data = {
|
||||
labels: Array.from({ length: 30 }, (_, i) => i + 1),
|
||||
datasets: [
|
||||
{
|
||||
data: defaultYes,
|
||||
label: 'yes',
|
||||
backgroundColor: '#BEBEBE',
|
||||
hoverBackgroundColor: '#BEBEBE',
|
||||
},
|
||||
{
|
||||
data: defaultNo,
|
||||
label: 'no',
|
||||
backgroundColor: '#9A9A9A',
|
||||
hoverBackgroundColor: '#9A9A9A',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const createBarChartOptions = (theme: Theme): ChartOptions<'bar'> => ({
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
color: theme.palette.text.primary,
|
||||
pointStyle: 'circle',
|
||||
usePointStyle: true,
|
||||
boxHeight: 6,
|
||||
padding: 15,
|
||||
boxPadding: 5,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
responsive: true,
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
ticks: {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
ticks: {
|
||||
color: theme.palette.text.secondary,
|
||||
maxTicksLimit: 5,
|
||||
callback: formatTickValue,
|
||||
},
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
bar: {
|
||||
borderRadius: 5,
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
});
|
||||
|
||||
export const PlaceholderFlagMetricsChart = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
const options = useMemo(() => {
|
||||
return createBarChartOptions(theme);
|
||||
}, [theme]);
|
||||
|
||||
return (
|
||||
<Bar
|
||||
data={data}
|
||||
options={options}
|
||||
aria-label='A bar chart with a single feature flag exposure metrics'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
ChartJS.register(
|
||||
annotationPlugin,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
BarElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
);
|
@ -1,6 +1,7 @@
|
||||
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
||||
import {
|
||||
Box,
|
||||
Grid,
|
||||
IconButton,
|
||||
Link,
|
||||
List,
|
||||
@ -8,7 +9,6 @@ import {
|
||||
ListItemButton,
|
||||
styled,
|
||||
Typography,
|
||||
Grid,
|
||||
} from '@mui/material';
|
||||
import type { Theme } from '@mui/material/styles/createTheme';
|
||||
import { ProjectIcon } from 'component/common/ProjectIcon/ProjectIcon';
|
||||
@ -17,6 +17,7 @@ import { useProfile } from 'hooks/api/getters/useProfile/useProfile';
|
||||
import LinkIcon from '@mui/icons-material/Link';
|
||||
import { Badge } from '../common/Badge/Badge';
|
||||
import { ConnectSDK, CreateFlag } from './ConnectSDK';
|
||||
import { PlaceholderFlagMetricsChart } from './FlagMetricsChart';
|
||||
|
||||
const ScreenExplanation = styled(Typography)(({ theme }) => ({
|
||||
marginTop: theme.spacing(1),
|
||||
@ -30,7 +31,7 @@ const StyledHeaderTitle = styled(Typography)(({ theme }) => ({
|
||||
marginBottom: theme.spacing(2),
|
||||
}));
|
||||
|
||||
const ProjectsGrid = styled(Grid)(({ theme }) => ({
|
||||
const ContentGrid = styled(Grid)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
borderRadius: `${theme.shape.borderRadiusLarge}px`,
|
||||
}));
|
||||
@ -104,7 +105,7 @@ const ActiveProjectDetails: FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const SpacedGrid = styled(Grid)(({ theme }) => ({
|
||||
const SpacedGridItem = styled(Grid)(({ theme }) => ({
|
||||
padding: theme.spacing(4),
|
||||
border: `0.5px solid ${theme.palette.divider}`,
|
||||
}));
|
||||
@ -148,19 +149,19 @@ export const PersonalDashboard = () => {
|
||||
most of Unleash
|
||||
</ScreenExplanation>
|
||||
<StyledHeaderTitle>Your resources</StyledHeaderTitle>
|
||||
<ProjectsGrid container columns={{ lg: 12, md: 1 }}>
|
||||
<SpacedGrid item lg={4} md={1}>
|
||||
<ContentGrid container columns={{ lg: 12, md: 1 }}>
|
||||
<SpacedGridItem item lg={4} md={1}>
|
||||
<Typography variant='h3'>My projects</Typography>
|
||||
</SpacedGrid>
|
||||
<SpacedGrid
|
||||
</SpacedGridItem>
|
||||
<SpacedGridItem
|
||||
item
|
||||
lg={8}
|
||||
md={1}
|
||||
sx={{ display: 'flex', justifyContent: 'flex-end' }}
|
||||
>
|
||||
<Badge color='warning'>Setup incomplete</Badge>
|
||||
</SpacedGrid>
|
||||
<SpacedGrid item lg={4} md={1}>
|
||||
</SpacedGridItem>
|
||||
<SpacedGridItem item lg={4} md={1}>
|
||||
<List
|
||||
disablePadding={true}
|
||||
sx={{ maxHeight: '400px', overflow: 'auto' }}
|
||||
@ -207,19 +208,19 @@ export const PersonalDashboard = () => {
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</SpacedGrid>
|
||||
<SpacedGrid item lg={4} md={1}>
|
||||
</SpacedGridItem>
|
||||
<SpacedGridItem item lg={4} md={1}>
|
||||
{activeProject ? (
|
||||
<CreateFlag project={activeProject} />
|
||||
) : null}
|
||||
</SpacedGrid>
|
||||
<SpacedGrid item lg={4} md={1}>
|
||||
</SpacedGridItem>
|
||||
<SpacedGridItem item lg={4} md={1}>
|
||||
{activeProject ? (
|
||||
<ConnectSDK project={activeProject} />
|
||||
) : null}
|
||||
</SpacedGrid>
|
||||
<SpacedGrid item lg={4} md={1} />
|
||||
<SpacedGrid
|
||||
</SpacedGridItem>
|
||||
<SpacedGridItem item lg={4} md={1} />
|
||||
<SpacedGridItem
|
||||
item
|
||||
lg={8}
|
||||
md={1}
|
||||
@ -228,8 +229,24 @@ export const PersonalDashboard = () => {
|
||||
<span>Your roles in this project:</span>{' '}
|
||||
<Badge color='secondary'>Member</Badge>{' '}
|
||||
<Badge color='secondary'>Another</Badge>
|
||||
</SpacedGrid>
|
||||
</ProjectsGrid>
|
||||
</SpacedGridItem>
|
||||
</ContentGrid>
|
||||
<ContentGrid container columns={{ lg: 12, md: 1 }} sx={{ mt: 2 }}>
|
||||
<SpacedGridItem item lg={4} md={1}>
|
||||
<Typography variant='h3'>My feature flags</Typography>
|
||||
</SpacedGridItem>
|
||||
<SpacedGridItem item lg={8} md={1} />
|
||||
<SpacedGridItem item lg={4} md={1}>
|
||||
<Typography>
|
||||
You have not created or favorited any feature flags.
|
||||
Once you do, the will show up here.
|
||||
</Typography>
|
||||
</SpacedGridItem>
|
||||
<SpacedGridItem item lg={8} md={1}>
|
||||
<Typography sx={{ mb: 4 }}>Feature flag metrics</Typography>
|
||||
<PlaceholderFlagMetricsChart />
|
||||
</SpacedGridItem>
|
||||
</ContentGrid>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user