From 19121f234ef31d881dc5721cada98ec6e27543be Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Thu, 18 Jul 2024 12:43:52 +0200 Subject: [PATCH] Insights layout (#7610) Refactored insights page - stats and charts relevant to the same metric are now combined into a single widget. --- .../common/BreadcrumbNav/BreadcrumbNav.tsx | 2 +- frontend/src/component/insights/Insights.tsx | 2 +- .../src/component/insights/InsightsCharts.tsx | 324 ++++++++++-------- .../insights/LegacyInsightsCharts.tsx | 3 + frontend/src/component/insights/chart-info.ts | 3 + .../insights/components/Widget/Widget.tsx | 3 + .../components/WidgetTitle/WidgetTitle.tsx | 29 ++ .../HealthStats/HealthStats.tsx | 11 +- 8 files changed, 232 insertions(+), 145 deletions(-) create mode 100644 frontend/src/component/insights/components/WidgetTitle/WidgetTitle.tsx diff --git a/frontend/src/component/common/BreadcrumbNav/BreadcrumbNav.tsx b/frontend/src/component/common/BreadcrumbNav/BreadcrumbNav.tsx index c81a9733ca..d18a4e84a4 100644 --- a/frontend/src/component/common/BreadcrumbNav/BreadcrumbNav.tsx +++ b/frontend/src/component/common/BreadcrumbNav/BreadcrumbNav.tsx @@ -53,7 +53,7 @@ const BreadcrumbNav = () => { ) .map(decodeURI); - if (paths.length === 0) { + if (location.pathname === '/insights') { return null; } diff --git a/frontend/src/component/insights/Insights.tsx b/frontend/src/component/insights/Insights.tsx index 3f0cd6151e..0c7839fffb 100644 --- a/frontend/src/component/insights/Insights.tsx +++ b/frontend/src/component/insights/Insights.tsx @@ -17,7 +17,7 @@ import { InsightsFilters } from './InsightsFilters'; import { FilterItemParam } from '../../utils/serializeQueryParams'; const StyledWrapper = styled('div')(({ theme }) => ({ - paddingTop: theme.spacing(1), + paddingTop: theme.spacing(2), })); const StickyContainer = styled(Sticky)(({ theme }) => ({ diff --git a/frontend/src/component/insights/InsightsCharts.tsx b/frontend/src/component/insights/InsightsCharts.tsx index 32487b3fa2..ae8947e518 100644 --- a/frontend/src/component/insights/InsightsCharts.tsx +++ b/frontend/src/component/insights/InsightsCharts.tsx @@ -1,7 +1,5 @@ import type { FC } from 'react'; -import { Box, styled } from '@mui/material'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { Widget } from './components/Widget/Widget'; +import { Box, Paper, styled } from '@mui/material'; import { UserStats } from './componentsStat/UserStats/UserStats'; import { UsersChart } from './componentsChart/UsersChart/UsersChart'; import { UsersPerProjectChart } from './componentsChart/UsersPerProjectChart/UsersPerProjectChart'; @@ -21,8 +19,9 @@ import type { } from 'openapi'; import type { GroupedDataByProject } from './hooks/useGroupedProjectTrends'; import { allOption } from 'component/common/ProjectSelect/ProjectSelect'; -import { chartInfo } from './chart-info'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { WidgetTitle } from './components/WidgetTitle/WidgetTitle'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; export interface IChartsProps { flags: InstanceInsightsSchema['flags']; @@ -53,22 +52,48 @@ export interface IChartsProps { allMetricsDatapoints: string[]; } -const StyledGrid = styled(Box)(({ theme }) => ({ - display: 'grid', - gridTemplateColumns: `repeat(2, 1fr)`, - gridAutoRows: 'auto', +const StyledContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', gap: theme.spacing(2), - paddingBottom: theme.spacing(2), +})); + +const StyledWidget = styled(Paper)(({ theme }) => ({ + borderRadius: `${theme.shape.borderRadiusLarge}px`, + boxShadow: 'none', + display: 'flex', + flexWrap: 'wrap', [theme.breakpoints.up('md')]: { - gridTemplateColumns: `300px 1fr`, + flexDirection: 'row', + flexWrap: 'nowrap', }, })); -const ChartWidget = styled(Widget)(({ theme }) => ({ - [theme.breakpoints.down('md')]: { - gridColumnStart: 'span 2', - order: 2, - }, +const StyledWidgetContent = styled(Box)(({ theme }) => ({ + padding: theme.spacing(3), + width: '100%', +})); + +const StyledWidgetStats = styled(Box)<{ width?: number }>( + ({ theme, width = 300 }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + padding: theme.spacing(3), + minWidth: '100%', + [theme.breakpoints.up('md')]: { + minWidth: `${width}px`, + borderRight: `1px solid ${theme.palette.background.application}`, + }, + }), +); + +const StyledChartContainer = styled(Box)(({ theme }) => ({ + position: 'relative', + minWidth: 0, // bugfix, see: https://github.com/chartjs/Chart.js/issues/4156#issuecomment-295180128 + flexGrow: 1, + margin: 'auto 0', + padding: theme.spacing(3), })); export const InsightsCharts: FC = ({ @@ -84,9 +109,9 @@ export const InsightsCharts: FC = ({ allMetricsDatapoints, loading, }) => { - const { isEnterprise } = useUiConfig(); const showAllProjects = projects[0] === allOption.id; const isOneProjectSelected = projects.length === 1; + const { isEnterprise } = useUiConfig(); function getFlagsPerUser( flags: InstanceInsightsSchemaFlags, @@ -99,153 +124,172 @@ export const InsightsCharts: FC = ({ } return ( - <> - - - - - } - elseShow={ - - - - } - /> - - - - } - elseShow={ - - - - } - /> - - - - - - - } - elseShow={ - - - - } - /> - - + + + + + + + + + + + + + + + + + + + + + + } + elseShow={ + <> + + + + + + + + + + + + + + + + + + + + } + /> + + + + - - + + - - + + + + + - - + + - - - } - /> - - - - - - theme.spacing(2) }} - > - - + + + + + + + + + + + + + + + + } /> - + ); }; diff --git a/frontend/src/component/insights/LegacyInsightsCharts.tsx b/frontend/src/component/insights/LegacyInsightsCharts.tsx index 681860e9a6..ad3cb6a26c 100644 --- a/frontend/src/component/insights/LegacyInsightsCharts.tsx +++ b/frontend/src/component/insights/LegacyInsightsCharts.tsx @@ -71,6 +71,9 @@ const ChartWidget = styled(Widget)(({ theme }) => ({ }, })); +/** + * @deprecated remove with insightsV2 flag + */ export const LegacyInsightsCharts: VFC = ({ projects, flags, diff --git a/frontend/src/component/insights/chart-info.ts b/frontend/src/component/insights/chart-info.ts index 6411e55735..dcd517ab0e 100644 --- a/frontend/src/component/insights/chart-info.ts +++ b/frontend/src/component/insights/chart-info.ts @@ -1,3 +1,6 @@ +/** + * @deprecated remove with insightsV2 flag + */ export const chartInfo = { totalUsers: { title: 'Total users', diff --git a/frontend/src/component/insights/components/Widget/Widget.tsx b/frontend/src/component/insights/components/Widget/Widget.tsx index a165413677..202b3f70b0 100644 --- a/frontend/src/component/insights/components/Widget/Widget.tsx +++ b/frontend/src/component/insights/components/Widget/Widget.tsx @@ -13,6 +13,9 @@ const StyledPaper = styled(Paper)(({ theme }) => ({ position: 'relative', })); +/** + * @deprecated remove with insightsV2 flag + */ export const Widget: FC<{ title: ReactNode; tooltip?: ReactNode; diff --git a/frontend/src/component/insights/components/WidgetTitle/WidgetTitle.tsx b/frontend/src/component/insights/components/WidgetTitle/WidgetTitle.tsx new file mode 100644 index 0000000000..38cab0edf0 --- /dev/null +++ b/frontend/src/component/insights/components/WidgetTitle/WidgetTitle.tsx @@ -0,0 +1,29 @@ +import type { FC, ReactNode } from 'react'; +import { Typography } from '@mui/material'; +import { HelpIcon } from 'component/common/HelpIcon/HelpIcon'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import InfoOutlined from '@mui/icons-material/InfoOutlined'; + +export const WidgetTitle: FC<{ + title: ReactNode; + tooltip?: ReactNode; +}> = ({ title, tooltip }) => ( + ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(0.5), + })} + > + {title} + + + + } + /> + +); diff --git a/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx b/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx index 6be9e790b9..355765c5db 100644 --- a/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx +++ b/frontend/src/component/insights/componentsStat/HealthStats/HealthStats.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react'; import { useThemeMode } from 'hooks/useThemeMode'; -import { useTheme } from '@mui/material'; +import { styled, useTheme } from '@mui/material'; interface IHealthStatsProps { value?: string | number; @@ -9,6 +9,11 @@ interface IHealthStatsProps { potentiallyStale: number; } +const StyledSvg = styled('svg')(() => ({ + maxWidth: '250px', + margin: '0 auto', +})); + export const HealthStats: FC = ({ value, healthy, @@ -20,7 +25,7 @@ export const HealthStats: FC = ({ const theme = useTheme(); return ( - = ({ /> - + ); };