1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-31 01:16:01 +02:00

refactor: FlagsChart and FlagsProjectChart components (#6087)

Co-authored-by: Fredrik Strand Oseberg <fredrik.no@gmail.com>
This commit is contained in:
Tymoteusz Czech 2024-01-31 10:50:50 +01:00 committed by GitHub
parent bc95ed654f
commit d77e5391ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 114 additions and 311 deletions

View File

@ -33,6 +33,7 @@ const useDashboardGrid = () => {
chartSpan: 1,
userTrendsOrder: 3,
flagStatsOrder: 2,
largeChartSpan: 1,
};
}
@ -42,6 +43,7 @@ const useDashboardGrid = () => {
chartSpan: 2,
userTrendsOrder: 3,
flagStatsOrder: 2,
largeChartSpan: 2,
};
}
@ -50,6 +52,7 @@ const useDashboardGrid = () => {
chartSpan: 1,
userTrendsOrder: 2,
flagStatsOrder: 3,
largeChartSpan: 2,
};
};
@ -69,8 +72,13 @@ export const ExecutiveDashboard: VFC = () => {
).toFixed(1);
}, [executiveDashboardData]);
const { gridTemplateColumns, chartSpan, userTrendsOrder, flagStatsOrder } =
useDashboardGrid();
const {
gridTemplateColumns,
chartSpan,
userTrendsOrder,
flagStatsOrder,
largeChartSpan,
} = useDashboardGrid();
return (
<>
@ -107,11 +115,18 @@ export const ExecutiveDashboard: VFC = () => {
flagTrends={executiveDashboardData.flagTrends}
/>
</Widget>
<Widget
title='Number of flags per project'
order={5}
span={largeChartSpan}
>
<FlagsProjectChart
projectFlagTrends={
executiveDashboardData.projectFlagTrends
}
/>
</Widget>
</StyledGrid>
<FlagsProjectChart
projectFlagTrends={executiveDashboardData.projectFlagTrends}
/>
</>
);
};

View File

@ -1,3 +1,37 @@
import { lazy } from 'react';
import { useMemo, type VFC } from 'react';
import 'chartjs-adapter-date-fns';
import { useTheme } from '@mui/material';
import { ExecutiveSummarySchema } from 'openapi';
import { LineChart } from '../LineChart/LineChart';
export const FlagsChart = lazy(() => import('./FlagsChartComponent'));
interface IFlagsChartProps {
flagTrends: ExecutiveSummarySchema['flagTrends'];
}
export const FlagsChart: VFC<IFlagsChartProps> = ({ flagTrends }) => {
const theme = useTheme();
const data = useMemo(
() => ({
labels: flagTrends.map((item) => item.date),
datasets: [
{
label: 'Total flags',
data: flagTrends.map((item) => item.total),
borderColor: theme.palette.primary.light,
backgroundColor: theme.palette.primary.light,
fill: true,
},
{
label: 'Stale',
data: flagTrends.map((item) => item.stale),
borderColor: theme.palette.warning.border,
backgroundColor: theme.palette.warning.border,
fill: true,
},
],
}),
[theme, flagTrends],
);
return <LineChart data={data} />;
};

View File

@ -1,137 +0,0 @@
import { useMemo, type VFC } from 'react';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
TimeScale,
} from 'chart.js';
import { Line } from 'react-chartjs-2';
import 'chartjs-adapter-date-fns';
import { Theme, useTheme } from '@mui/material';
import {
useLocationSettings,
type ILocationSettings,
} from 'hooks/useLocationSettings';
import { formatDateYMD } from 'utils/formatDate';
import { ExecutiveSummarySchema } from 'openapi';
const createData = (
theme: Theme,
flagTrends: ExecutiveSummarySchema['flagTrends'] = [],
) => ({
labels: flagTrends.map((item) => item.date),
datasets: [
{
label: 'Total flags',
data: flagTrends.map((item) => item.total),
borderColor: theme.palette.primary.main,
backgroundColor: theme.palette.primary.main,
fill: true,
},
{
label: 'Stale',
data: flagTrends.map((item) => item.stale),
borderColor: theme.palette.warning.main,
backgroundColor: theme.palette.warning.main,
fill: true,
},
{
label: 'Active flags',
data: flagTrends.map((item) => item.active),
borderColor: theme.palette.success.main,
backgroundColor: theme.palette.success.main,
fill: true,
},
],
});
const createOptions = (theme: Theme, locationSettings: ILocationSettings) =>
({
responsive: true,
plugins: {
legend: {
position: 'bottom',
},
tooltip: {
callbacks: {
title: (tooltipItems: any) => {
const item = tooltipItems?.[0];
const date =
item?.chart?.data?.labels?.[item.dataIndex];
return date
? formatDateYMD(
date,
locationSettings.locale,
'UTC',
)
: '';
},
},
},
},
locale: locationSettings.locale,
interaction: {
intersect: false,
axis: 'x',
},
color: theme.palette.text.secondary,
scales: {
y: {
type: 'linear',
grid: {
color: theme.palette.divider,
borderColor: theme.palette.divider,
},
ticks: { color: theme.palette.text.secondary },
},
x: {
type: 'time',
time: {
unit: 'month',
},
grid: {
color: theme.palette.divider,
borderColor: theme.palette.divider,
},
ticks: {
color: theme.palette.text.secondary,
},
},
},
}) as const;
interface IFlagsChartComponentProps {
flagTrends: ExecutiveSummarySchema['flagTrends'];
}
const FlagsChartComponent: VFC<IFlagsChartComponentProps> = ({
flagTrends,
}) => {
const theme = useTheme();
const { locationSettings } = useLocationSettings();
const data = useMemo(
() => createData(theme, flagTrends),
[theme, flagTrends],
);
const options = createOptions(theme, locationSettings);
return <Line options={options} data={data} />;
};
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
TimeScale,
Title,
Tooltip,
Legend,
);
export default FlagsChartComponent;

View File

@ -1,5 +1,58 @@
import { lazy } from 'react';
import { useMemo, type VFC } from 'react';
import 'chartjs-adapter-date-fns';
import { useTheme } from '@mui/material';
import {
ExecutiveSummarySchema,
ExecutiveSummarySchemaProjectFlagTrendsItem,
} from 'openapi';
import { LineChart } from '../LineChart/LineChart';
export const FlagsProjectChart = lazy(
() => import('./FlagsProjectChartComponent'),
);
interface IFlagsProjectChartProps {
projectFlagTrends: ExecutiveSummarySchema['projectFlagTrends'];
}
const getRandomColor = () => {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
export const FlagsProjectChart: VFC<IFlagsProjectChartProps> = ({
projectFlagTrends,
}) => {
const theme = useTheme();
const data = useMemo(() => {
const groupedFlagTrends = projectFlagTrends.reduce<
Record<string, ExecutiveSummarySchemaProjectFlagTrendsItem[]>
>((groups, item) => {
if (!groups[item.project]) {
groups[item.project] = [];
}
groups[item.project].push(item);
return groups;
}, {});
const datasets = Object.entries(groupedFlagTrends).map(
([project, trends]) => {
const color = getRandomColor();
return {
label: project,
data: trends.map((item) => item.total),
borderColor: color,
backgroundColor: color,
fill: true,
};
},
);
return {
labels: projectFlagTrends.map((item) => item.date),
datasets,
};
}, [theme, projectFlagTrends]);
return <LineChart data={data} />;
};

View File

@ -1,162 +0,0 @@
import { useMemo, type VFC } from 'react';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
TimeScale,
} from 'chart.js';
import { Line } from 'react-chartjs-2';
import 'chartjs-adapter-date-fns';
import { Paper, Theme, Typography, useTheme } from '@mui/material';
import {
useLocationSettings,
type ILocationSettings,
} from 'hooks/useLocationSettings';
import { formatDateYMD } from 'utils/formatDate';
import {
ExecutiveSummarySchema,
ExecutiveSummarySchemaProjectFlagTrendsItem,
} from 'openapi';
const getRandomColor = () => {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
const createData = (
theme: Theme,
flagTrends: ExecutiveSummarySchema['projectFlagTrends'] = [],
) => {
const groupedFlagTrends = flagTrends.reduce<
Record<string, ExecutiveSummarySchemaProjectFlagTrendsItem[]>
>((groups, item) => {
if (!groups[item.project]) {
groups[item.project] = [];
}
groups[item.project].push(item);
return groups;
}, {});
const datasets = Object.entries(groupedFlagTrends).map(
([project, trends]) => {
const color = getRandomColor();
return {
label: project,
data: trends.map((item) => item.total),
borderColor: color,
backgroundColor: color,
fill: true,
};
},
);
return {
labels: flagTrends.map((item) => item.date),
datasets,
};
};
const createOptions = (theme: Theme, locationSettings: ILocationSettings) =>
({
responsive: true,
plugins: {
legend: {
position: 'bottom',
},
tooltip: {
callbacks: {
title: (tooltipItems: any) => {
const item = tooltipItems?.[0];
const date =
item?.chart?.data?.labels?.[item.dataIndex];
return date
? formatDateYMD(
date,
locationSettings.locale,
'UTC',
)
: '';
},
},
},
},
locale: locationSettings.locale,
interaction: {
intersect: false,
axis: 'x',
},
color: theme.palette.text.secondary,
scales: {
y: {
type: 'linear',
grid: {
color: theme.palette.divider,
borderColor: theme.palette.divider,
},
ticks: { color: theme.palette.text.secondary },
},
x: {
type: 'time',
time: {
unit: 'month',
},
grid: {
color: theme.palette.divider,
borderColor: theme.palette.divider,
},
ticks: {
color: theme.palette.text.secondary,
},
},
},
}) as const;
interface IFlagsChartComponentProps {
projectFlagTrends: ExecutiveSummarySchema['projectFlagTrends'];
}
const FlagsProjectChart: VFC<IFlagsChartComponentProps> = ({
projectFlagTrends,
}) => {
const theme = useTheme();
const { locationSettings } = useLocationSettings();
const data = useMemo(
() => createData(theme, projectFlagTrends),
[theme, projectFlagTrends],
);
const options = createOptions(theme, locationSettings);
return (
<Paper sx={(theme) => ({ padding: theme.spacing(4) })}>
<Typography
variant='h3'
sx={(theme) => ({ marginBottom: theme.spacing(3) })}
>
Number of flags per project
</Typography>
<Line options={options} data={data} />
</Paper>
);
};
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
TimeScale,
Title,
Tooltip,
Legend,
);
export default FlagsProjectChart;