diff --git a/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx b/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx index a5fdc851a8..bc8e69da8f 100644 --- a/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx +++ b/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx @@ -1,24 +1,61 @@ -import { Box, Paper, styled, Typography } from '@mui/material'; +import { useMemo, VFC } from 'react'; +import { + Box, + styled, + Typography, + useMediaQuery, + useTheme, +} from '@mui/material'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; -import { VFC } from 'react'; import { UsersChart } from './UsersChart/UsersChart'; import { FlagsChart } from './FlagsChart/FlagsChart'; import { useExecutiveDashboard } from 'hooks/api/getters/useExecutiveSummary/useExecutiveSummary'; import { UserStats } from './UserStats/UserStats'; import { FlagStats } from './FlagStats/FlagStats'; +import { Widget } from './Widget/Widget'; const StyledGrid = styled(Box)(({ theme }) => ({ display: 'grid', gridTemplateColumns: `300px 1fr`, - // TODO: responsive grid size gridAutoRows: 'auto', gap: theme.spacing(2), })); +const useDashboardGrid = () => { + const theme = useTheme(); + const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg')); + const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm')); + + if (isSmallScreen) { + return { + gridTemplateColumns: `1fr`, + chartSpan: 1, + userTrendsOrder: 3, + flagStatsOrder: 2, + }; + } + + if (isMediumScreen) { + return { + gridTemplateColumns: `1fr 1fr`, + chartSpan: 2, + userTrendsOrder: 3, + flagStatsOrder: 2, + }; + } + + return { + gridTemplateColumns: `300px auto`, + chartSpan: 1, + userTrendsOrder: 2, + flagStatsOrder: 3, + }; +}; + export const ExecutiveDashboard: VFC = () => { const { executiveDashboardData, loading, error } = useExecutiveDashboard(); - const calculateFlagPerUsers = () => { + const flagPerUsers = useMemo(() => { if ( executiveDashboardData.users.total === 0 || executiveDashboardData.flags.total === 0 @@ -29,7 +66,10 @@ export const ExecutiveDashboard: VFC = () => { executiveDashboardData.flags.total / executiveDashboardData.users.total ).toFixed(1); - }; + }, [executiveDashboardData]); + + const { gridTemplateColumns, chartSpan, userTrendsOrder, flagStatsOrder } = + useDashboardGrid(); return ( <> @@ -42,14 +82,30 @@ export const ExecutiveDashboard: VFC = () => { } /> - - - - - + + + + + + + + + + + + + ); diff --git a/frontend/src/component/executiveDashboard/FlagStats/FlagStats.tsx b/frontend/src/component/executiveDashboard/FlagStats/FlagStats.tsx index 846933254a..de21263c78 100644 --- a/frontend/src/component/executiveDashboard/FlagStats/FlagStats.tsx +++ b/frontend/src/component/executiveDashboard/FlagStats/FlagStats.tsx @@ -1,6 +1,5 @@ import { Settings } from '@mui/icons-material'; import { Box, Typography, styled } from '@mui/material'; -import { HelpIcon } from 'component/common/HelpIcon/HelpIcon'; const StyledContent = styled(Box)(({ theme }) => ({ borderRadius: `${theme.shape.borderRadiusLarge}px`, @@ -88,22 +87,7 @@ export const FlagStats: React.FC = ({ flagsPerUser, }) => { return ( - - - Total flags{' '} - - - Total flags represent the total active flags - (not archived) that currently exist across all - projects of your application. - - - } - /> - + <> {count} @@ -126,6 +110,6 @@ export const FlagStats: React.FC = ({ {flagsPerUser} - + ); }; diff --git a/frontend/src/component/executiveDashboard/FlagsChart/FlagsChartComponent.tsx b/frontend/src/component/executiveDashboard/FlagsChart/FlagsChartComponent.tsx index 0f81ad499d..e818ea5f0b 100644 --- a/frontend/src/component/executiveDashboard/FlagsChart/FlagsChartComponent.tsx +++ b/frontend/src/component/executiveDashboard/FlagsChart/FlagsChartComponent.tsx @@ -12,7 +12,7 @@ import { } from 'chart.js'; import { Line } from 'react-chartjs-2'; import 'chartjs-adapter-date-fns'; -import { Paper, Theme, Typography, useTheme } from '@mui/material'; +import { Theme, useTheme } from '@mui/material'; import { useLocationSettings, type ILocationSettings, @@ -116,17 +116,7 @@ const FlagsChartComponent: VFC = ({ ); const options = createOptions(theme, locationSettings); - return ( - ({ padding: theme.spacing(4) })}> - ({ marginBottom: theme.spacing(3) })} - > - Number of flags - - - - ); + return ; }; ChartJS.register( diff --git a/frontend/src/component/executiveDashboard/UserStats/UserStats.tsx b/frontend/src/component/executiveDashboard/UserStats/UserStats.tsx index 4291e75d34..91c19f908a 100644 --- a/frontend/src/component/executiveDashboard/UserStats/UserStats.tsx +++ b/frontend/src/component/executiveDashboard/UserStats/UserStats.tsx @@ -5,13 +5,6 @@ import { useUiFlag } from 'hooks/useUiFlag'; import React from 'react'; import { Link } from 'react-router-dom'; -const StyledContent = styled(Box)(({ theme }) => ({ - borderRadius: `${theme.shape.borderRadiusLarge}px`, - backgroundColor: theme.palette.background.paper, - maxWidth: 300, - padding: theme.spacing(3), -})); - const StyledUserContainer = styled(Box)(({ theme }) => ({ position: 'relative', })); @@ -85,49 +78,42 @@ export const UserStats: React.FC = ({ count }) => { return ( <> - - - Total users - - - - {count} - - - - + + + {count} + + + - - - - + + + + - - - - - - } - /> + + + + + + } + /> - - - View users - - - - + + + View users + + ); }; diff --git a/frontend/src/component/executiveDashboard/UsersChart/UsersChartComponent.tsx b/frontend/src/component/executiveDashboard/UsersChart/UsersChartComponent.tsx index f5a7ae1e27..e1b4de2daf 100644 --- a/frontend/src/component/executiveDashboard/UsersChart/UsersChartComponent.tsx +++ b/frontend/src/component/executiveDashboard/UsersChart/UsersChartComponent.tsx @@ -13,7 +13,7 @@ import { } from 'chart.js'; import { Line } from 'react-chartjs-2'; import 'chartjs-adapter-date-fns'; -import { Paper, Theme, Typography, useTheme } from '@mui/material'; +import { Theme, useTheme } from '@mui/material'; import { useLocationSettings, type ILocationSettings, @@ -181,23 +181,10 @@ const UsersChartComponent: VFC = ({ ); return ( - ({ - padding: theme.spacing(3), - position: 'relative', - })} - > - ({ marginBottom: theme.spacing(3) })} - > - Users - + <> - - + ); }; diff --git a/frontend/src/component/executiveDashboard/Widget/Widget.tsx b/frontend/src/component/executiveDashboard/Widget/Widget.tsx new file mode 100644 index 0000000000..1036759051 --- /dev/null +++ b/frontend/src/component/executiveDashboard/Widget/Widget.tsx @@ -0,0 +1,39 @@ +import { FC, ReactNode } from 'react'; +import { Paper, Typography } from '@mui/material'; +import { HelpIcon } from 'component/common/HelpIcon/HelpIcon'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; + +export const Widget: FC<{ + title: ReactNode; + order?: number; + span?: number; + tooltip?: ReactNode; +}> = ({ title, order, children, span = 1, tooltip }) => ( + ({ + padding: 3, + borderRadius: `${theme.shape.borderRadiusLarge}px`, + order, + gridColumn: `span ${span}`, + minWidth: 0, // bugfix, see: https://github.com/chartjs/Chart.js/issues/4156#issuecomment-295180128 + })} + > + ({ + marginBottom: theme.spacing(3), + display: 'flex', + alignItems: 'center', + gap: theme.spacing(0.5), + })} + > + {title} + } + /> + + {children} + +);