diff --git a/frontend/src/component/insights/InsightsCharts.tsx b/frontend/src/component/insights/InsightsCharts.tsx index 26ce5c245c..a491a2aec3 100644 --- a/frontend/src/component/insights/InsightsCharts.tsx +++ b/frontend/src/component/insights/InsightsCharts.tsx @@ -1,4 +1,4 @@ -import type { FC } from 'react'; +import type { FC, PropsWithChildren } from 'react'; import { Box, Paper, styled } from '@mui/material'; import { UserStats } from './componentsStat/UserStats/UserStats.tsx'; import { UsersChart } from './componentsChart/UsersChart/UsersChart.tsx'; @@ -8,8 +8,6 @@ import { FlagsChart } from './componentsChart/FlagsChart/FlagsChart.tsx'; import { FlagsProjectChart } from './componentsChart/FlagsProjectChart/FlagsProjectChart.tsx'; import { HealthStats } from './componentsStat/HealthStats/HealthStats.tsx'; import { ProjectHealthChart } from './componentsChart/ProjectHealthChart/ProjectHealthChart.tsx'; -import { TimeToProduction } from './componentsStat/TimeToProduction/TimeToProduction.tsx'; -import { TimeToProductionChart } from './componentsChart/TimeToProductionChart/TimeToProductionChart.tsx'; import { MetricsSummaryChart } from './componentsChart/MetricsSummaryChart/MetricsSummaryChart.tsx'; import { UpdatesPerEnvironmentTypeChart } from './componentsChart/UpdatesPerEnvironmentTypeChart/UpdatesPerEnvironmentTypeChart.tsx'; import type { InstanceInsightsSchema } from 'openapi'; @@ -17,8 +15,8 @@ import type { GroupedDataByProject } from './hooks/useGroupedProjectTrends.ts'; import { allOption } from 'component/common/ProjectSelect/ProjectSelect'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { WidgetTitle } from './components/WidgetTitle/WidgetTitle.tsx'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { useUiFlag } from 'hooks/useUiFlag.ts'; +import { LegacyInsightsCharts } from './LegacyInsightsCharts.tsx'; export interface IChartsProps { flagTrends: InstanceInsightsSchema['flagTrends']; @@ -50,7 +48,7 @@ export interface IChartsProps { const StyledContainer = styled(Box)(({ theme }) => ({ display: 'flex', flexDirection: 'column', - gap: theme.spacing(2), + gap: theme.spacing(4), })); const StyledWidget = styled(Paper)(({ theme }) => ({ @@ -91,7 +89,23 @@ const StyledChartContainer = styled(Box)(({ theme }) => ({ padding: theme.spacing(3), })); -export const InsightsCharts: FC = ({ +const StyledSection = styled('section')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), +})); + +const Section: FC> = ({ + title, + children, +}) => ( + +

{title}

+ {children} +
+); + +const NewInsightsCharts: FC = ({ projects, summary, userTrends, @@ -105,7 +119,6 @@ export const InsightsCharts: FC = ({ const showAllProjects = projects[0] === allOption.id; const isOneProjectSelected = projects.length === 1; const { isEnterprise } = useUiConfig(); - const showMedianTimeToProduction = !useUiFlag('lifecycleMetrics'); const lastUserTrend = userTrends[userTrends.length - 1]; const lastFlagTrend = flagTrends[flagTrends.length - 1]; @@ -124,163 +137,74 @@ export const InsightsCharts: FC = ({ return ( - - - - - - - - - - - - } - elseShow={ - <> - - - - - - - - - - - } - /> - - - - - } - /> - - - - - - {showMedianTimeToProduction ? ( - - +
+
+ {showAllProjects ? ( + + + + + + + + + + ) : ( + + + + + + + + + + )} + {isEnterprise() ? ( + + + - - - - - - - ) : null} - - } - /> - - - - - - - - - - - - } - elseShow={ - <> - - - - - - - - - - - } - /> - + + + + + + ) : null} + {isEnterprise() ? ( <> @@ -317,8 +241,67 @@ export const InsightsCharts: FC = ({ - } - /> + ) : null} +
+ +
+ {showAllProjects ? ( + + + + + + + + + + ) : ( + + + + + + + + + + )} +
); }; + +export const InsightsCharts: FC = (props) => { + const useNewInsightsCharts = useUiFlag('lifecycleMetrics'); + + return useNewInsightsCharts ? ( + + ) : ( + + ); +}; diff --git a/frontend/src/component/insights/LegacyInsightsCharts.tsx b/frontend/src/component/insights/LegacyInsightsCharts.tsx new file mode 100644 index 0000000000..512b4fdd73 --- /dev/null +++ b/frontend/src/component/insights/LegacyInsightsCharts.tsx @@ -0,0 +1,320 @@ +import type { FC } from 'react'; +import { Box, Paper, styled } from '@mui/material'; +import { UserStats } from './componentsStat/UserStats/UserStats.tsx'; +import { UsersChart } from './componentsChart/UsersChart/UsersChart.tsx'; +import { UsersPerProjectChart } from './componentsChart/UsersPerProjectChart/UsersPerProjectChart.tsx'; +import { FlagStats } from './componentsStat/FlagStats/FlagStats.tsx'; +import { FlagsChart } from './componentsChart/FlagsChart/FlagsChart.tsx'; +import { FlagsProjectChart } from './componentsChart/FlagsProjectChart/FlagsProjectChart.tsx'; +import { HealthStats } from './componentsStat/HealthStats/HealthStats.tsx'; +import { ProjectHealthChart } from './componentsChart/ProjectHealthChart/ProjectHealthChart.tsx'; +import { TimeToProduction } from './componentsStat/TimeToProduction/TimeToProduction.tsx'; +import { TimeToProductionChart } from './componentsChart/TimeToProductionChart/TimeToProductionChart.tsx'; +import { MetricsSummaryChart } from './componentsChart/MetricsSummaryChart/MetricsSummaryChart.tsx'; +import { UpdatesPerEnvironmentTypeChart } from './componentsChart/UpdatesPerEnvironmentTypeChart/UpdatesPerEnvironmentTypeChart.tsx'; +import type { InstanceInsightsSchema } from 'openapi'; +import type { GroupedDataByProject } from './hooks/useGroupedProjectTrends.ts'; +import { allOption } from 'component/common/ProjectSelect/ProjectSelect'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { WidgetTitle } from './components/WidgetTitle/WidgetTitle.tsx'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; + +export interface IChartsProps { + flagTrends: InstanceInsightsSchema['flagTrends']; + projectsData: InstanceInsightsSchema['projectFlagTrends']; + groupedProjectsData: GroupedDataByProject< + InstanceInsightsSchema['projectFlagTrends'] + >; + metricsData: InstanceInsightsSchema['metricsSummaryTrends']; + groupedMetricsData: GroupedDataByProject< + InstanceInsightsSchema['metricsSummaryTrends'] + >; + userTrends: InstanceInsightsSchema['userTrends']; + environmentTypeTrends: InstanceInsightsSchema['environmentTypeTrends']; + summary: { + total: number; + active: number; + stale: number; + potentiallyStale: number; + averageUsers: number; + averageHealth?: string; + flagsPerUser?: string; + medianTimeToProduction?: number; + }; + loading: boolean; + projects: string[]; + allMetricsDatapoints: string[]; +} + +const StyledContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), +})); + +const StyledWidget = styled(Paper)(({ theme }) => ({ + borderRadius: `${theme.shape.borderRadiusLarge}px`, + boxShadow: 'none', + display: 'flex', + flexWrap: 'wrap', + [theme.breakpoints.up('md')]: { + flexDirection: 'row', + flexWrap: 'nowrap', + }, +})); + +const StyledWidgetContent = styled(Box)(({ theme }) => ({ + padding: theme.spacing(3), + width: '100%', +})); + +const StyledWidgetStats = styled(Box)<{ width?: number; padding?: number }>( + ({ theme, width = 300, padding = 3 }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + padding: theme.spacing(padding), + 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 LegacyInsightsCharts: FC = ({ + projects, + summary, + userTrends, + groupedProjectsData, + flagTrends, + groupedMetricsData, + environmentTypeTrends, + allMetricsDatapoints, + loading, +}) => { + const showAllProjects = projects[0] === allOption.id; + const isOneProjectSelected = projects.length === 1; + const { isEnterprise } = useUiConfig(); + + const lastUserTrend = userTrends[userTrends.length - 1]; + const lastFlagTrend = flagTrends[flagTrends.length - 1]; + + const usersTotal = lastUserTrend?.total ?? 0; + const usersActive = lastUserTrend?.active ?? 0; + const usersInactive = lastUserTrend?.inactive ?? 0; + const flagsTotal = lastFlagTrend?.total ?? 0; + + function getFlagsPerUser(flagsTotal: number, usersTotal: number) { + const flagsPerUserCalculation = flagsTotal / usersTotal; + return Number.isNaN(flagsPerUserCalculation) + ? 'N/A' + : flagsPerUserCalculation.toFixed(2); + } + + return ( + + + + + + + + + + + + + } + elseShow={ + <> + + + + + + + + + + + } + /> + + + + + } + /> + + + + + + + + + + + + + + + + } + /> + + + + + + + + + + + + } + elseShow={ + <> + + + + + + + + + + + } + /> + + + + + + + + + + + + + + + + + } + /> + + ); +}; diff --git a/frontend/src/component/insights/components/InsightsHeader/InsightsHeader.tsx b/frontend/src/component/insights/components/InsightsHeader/InsightsHeader.tsx index b9e5e07ac1..cc91c4b1a4 100644 --- a/frontend/src/component/insights/components/InsightsHeader/InsightsHeader.tsx +++ b/frontend/src/component/insights/components/InsightsHeader/InsightsHeader.tsx @@ -73,14 +73,14 @@ export const InsightsHeader: VFC = ({ actions }) => { titleElement={ ({ display: 'flex', alignItems: 'center', gap: theme.spacing(1), })} > - Insights{' '} + Insights } actions={