From 87b997698b4e8a00ea2450f2380c1d0cf2d913ff Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Fri, 20 Sep 2024 11:05:53 +0200 Subject: [PATCH] feat: placeholder flag metrics chart (#8197) --- .../NetworkTrafficUsage.tsx | 50 +------ .../common/Chart/customHighlightPlugin.ts | 32 +++++ .../component/common/Chart/formatTickValue.ts | 16 +++ .../personalDashboard/FlagMetricsChart.tsx | 126 ++++++++++++++++++ .../personalDashboard/PersonalDashboard.tsx | 53 +++++--- 5 files changed, 212 insertions(+), 65 deletions(-) create mode 100644 frontend/src/component/common/Chart/customHighlightPlugin.ts create mode 100644 frontend/src/component/common/Chart/formatTickValue.ts create mode 100644 frontend/src/component/personalDashboard/FlagMetricsChart.tsx diff --git a/frontend/src/component/admin/network/NetworkTrafficUsage/NetworkTrafficUsage.tsx b/frontend/src/component/admin/network/NetworkTrafficUsage/NetworkTrafficUsage.tsx index 48ca0081559b..fa483f18e80b 100644 --- a/frontend/src/component/admin/network/NetworkTrafficUsage/NetworkTrafficUsage.tsx +++ b/frontend/src/component/admin/network/NetworkTrafficUsage/NetworkTrafficUsage.tsx @@ -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, diff --git a/frontend/src/component/common/Chart/customHighlightPlugin.ts b/frontend/src/component/common/Chart/customHighlightPlugin.ts new file mode 100644 index 000000000000..d742b8fb6d76 --- /dev/null +++ b/frontend/src/component/common/Chart/customHighlightPlugin.ts @@ -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(); + } + }, +}; diff --git a/frontend/src/component/common/Chart/formatTickValue.ts b/frontend/src/component/common/Chart/formatTickValue.ts new file mode 100644 index 000000000000..702dbc9dff18 --- /dev/null +++ b/frontend/src/component/common/Chart/formatTickValue.ts @@ -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; +}; diff --git a/frontend/src/component/personalDashboard/FlagMetricsChart.tsx b/frontend/src/component/personalDashboard/FlagMetricsChart.tsx new file mode 100644 index 000000000000..6135a137e66a --- /dev/null +++ b/frontend/src/component/personalDashboard/FlagMetricsChart.tsx @@ -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 ( + + ); +}; + +ChartJS.register( + annotationPlugin, + CategoryScale, + LinearScale, + BarElement, + Title, + Tooltip, + Legend, +); diff --git a/frontend/src/component/personalDashboard/PersonalDashboard.tsx b/frontend/src/component/personalDashboard/PersonalDashboard.tsx index 5267d9afe1a9..e6a76c6078e6 100644 --- a/frontend/src/component/personalDashboard/PersonalDashboard.tsx +++ b/frontend/src/component/personalDashboard/PersonalDashboard.tsx @@ -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 Your resources - - + + My projects - - + Setup incomplete - - + + { ); })} - - + + {activeProject ? ( ) : null} - - + + {activeProject ? ( ) : null} - - - + + { Your roles in this project:{' '} Member{' '} Another - - + + + + + My feature flags + + + + + You have not created or favorited any feature flags. + Once you do, the will show up here. + + + + Feature flag metrics + + + ); };