From d77e5391ed8efae27d48361c70cd0d8c272a6a67 Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Wed, 31 Jan 2024 10:50:50 +0100 Subject: [PATCH] refactor: FlagsChart and FlagsProjectChart components (#6087) Co-authored-by: Fredrik Strand Oseberg --- .../executiveDashboard/ExecutiveDashboard.tsx | 27 ++- .../FlagsChart/FlagsChart.tsx | 38 +++- .../FlagsChart/FlagsChartComponent.tsx | 137 --------------- .../FlagsProjectChart/FlagsProjectChart.tsx | 61 ++++++- .../FlagsProjectChartComponent.tsx | 162 ------------------ 5 files changed, 114 insertions(+), 311 deletions(-) delete mode 100644 frontend/src/component/executiveDashboard/FlagsChart/FlagsChartComponent.tsx delete mode 100644 frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChartComponent.tsx diff --git a/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx b/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx index 1baee2436f..1713033313 100644 --- a/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx +++ b/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx @@ -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} /> + + + - - ); }; diff --git a/frontend/src/component/executiveDashboard/FlagsChart/FlagsChart.tsx b/frontend/src/component/executiveDashboard/FlagsChart/FlagsChart.tsx index 694e0687e8..92d1e5ad13 100644 --- a/frontend/src/component/executiveDashboard/FlagsChart/FlagsChart.tsx +++ b/frontend/src/component/executiveDashboard/FlagsChart/FlagsChart.tsx @@ -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 = ({ 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 ; +}; diff --git a/frontend/src/component/executiveDashboard/FlagsChart/FlagsChartComponent.tsx b/frontend/src/component/executiveDashboard/FlagsChart/FlagsChartComponent.tsx deleted file mode 100644 index bcb52b0db9..0000000000 --- a/frontend/src/component/executiveDashboard/FlagsChart/FlagsChartComponent.tsx +++ /dev/null @@ -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 = ({ - flagTrends, -}) => { - const theme = useTheme(); - const { locationSettings } = useLocationSettings(); - const data = useMemo( - () => createData(theme, flagTrends), - [theme, flagTrends], - ); - const options = createOptions(theme, locationSettings); - - return ; -}; - -ChartJS.register( - CategoryScale, - LinearScale, - PointElement, - LineElement, - TimeScale, - Title, - Tooltip, - Legend, -); - -export default FlagsChartComponent; diff --git a/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx b/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx index 0200b68fd5..8bc0f2a79b 100644 --- a/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx +++ b/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx @@ -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 = ({ + projectFlagTrends, +}) => { + const theme = useTheme(); + const data = useMemo(() => { + const groupedFlagTrends = projectFlagTrends.reduce< + Record + >((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 ; +}; diff --git a/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChartComponent.tsx b/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChartComponent.tsx deleted file mode 100644 index 07b5424126..0000000000 --- a/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChartComponent.tsx +++ /dev/null @@ -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 - >((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 = ({ - projectFlagTrends, -}) => { - const theme = useTheme(); - const { locationSettings } = useLocationSettings(); - const data = useMemo( - () => createData(theme, projectFlagTrends), - [theme, projectFlagTrends], - ); - const options = createOptions(theme, locationSettings); - - return ( - ({ padding: theme.spacing(4) })}> - ({ marginBottom: theme.spacing(3) })} - > - Number of flags per project - - - - ); -}; - -ChartJS.register( - CategoryScale, - LinearScale, - PointElement, - LineElement, - TimeScale, - Title, - Tooltip, - Legend, -); - -export default FlagsProjectChart;