diff --git a/frontend/src/component/insights/Insights.tsx b/frontend/src/component/insights/Insights.tsx index d7cfcd447bc3..cebc93fdcc1b 100644 --- a/frontend/src/component/insights/Insights.tsx +++ b/frontend/src/component/insights/Insights.tsx @@ -10,6 +10,9 @@ import { useInsights } from 'hooks/api/getters/useInsights/useInsights'; import { InsightsHeader } from './components/InsightsHeader/InsightsHeader'; import { useInsightsData } from './hooks/useInsightsData'; import { InsightsCharts } from './InsightsCharts'; +import { LegacyInsightsCharts } from './LegacyInsightsCharts'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { useUiFlag } from 'hooks/useUiFlag'; const StickyWrapper = styled(Box, { shouldForwardProp: (prop) => prop !== 'scrolled', @@ -50,6 +53,8 @@ export const Insights: VFC = () => { window.addEventListener('scroll', handleScroll); } + const isInsightsV2Enabled = useUiFlag('insightsV2'); + return ( <> @@ -69,10 +74,22 @@ export const Insights: VFC = () => { } /> - + } + elseShow={ + + } /> ); diff --git a/frontend/src/component/insights/LegacyInsightsCharts.tsx b/frontend/src/component/insights/LegacyInsightsCharts.tsx new file mode 100644 index 000000000000..681860e9a64e --- /dev/null +++ b/frontend/src/component/insights/LegacyInsightsCharts.tsx @@ -0,0 +1,251 @@ +import type { VFC } from 'react'; +import { Box, styled } from '@mui/material'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { Widget } from './components/Widget/Widget'; +import { UserStats } from './componentsStat/UserStats/UserStats'; +import { UsersChart } from './componentsChart/UsersChart/UsersChart'; +import { UsersPerProjectChart } from './componentsChart/UsersPerProjectChart/UsersPerProjectChart'; +import { FlagStats } from './componentsStat/FlagStats/FlagStats'; +import { FlagsChart } from './componentsChart/FlagsChart/FlagsChart'; +import { FlagsProjectChart } from './componentsChart/FlagsProjectChart/FlagsProjectChart'; +import { HealthStats } from './componentsStat/HealthStats/HealthStats'; +import { ProjectHealthChart } from './componentsChart/ProjectHealthChart/ProjectHealthChart'; +import { TimeToProduction } from './componentsStat/TimeToProduction/TimeToProduction'; +import { TimeToProductionChart } from './componentsChart/TimeToProductionChart/TimeToProductionChart'; +import { MetricsSummaryChart } from './componentsChart/MetricsSummaryChart/MetricsSummaryChart'; +import { UpdatesPerEnvironmentTypeChart } from './componentsChart/UpdatesPerEnvironmentTypeChart/UpdatesPerEnvironmentTypeChart'; +import type { + InstanceInsightsSchema, + InstanceInsightsSchemaFlags, + InstanceInsightsSchemaUsers, +} 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'; + +interface IChartsProps { + flags: InstanceInsightsSchema['flags']; + flagTrends: InstanceInsightsSchema['flagTrends']; + projectsData: InstanceInsightsSchema['projectFlagTrends']; + groupedProjectsData: GroupedDataByProject< + InstanceInsightsSchema['projectFlagTrends'] + >; + metricsData: InstanceInsightsSchema['metricsSummaryTrends']; + groupedMetricsData: GroupedDataByProject< + InstanceInsightsSchema['metricsSummaryTrends'] + >; + users: InstanceInsightsSchema['users']; + 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 StyledGrid = styled(Box)(({ theme }) => ({ + display: 'grid', + gridTemplateColumns: `repeat(2, 1fr)`, + gridAutoRows: 'auto', + gap: theme.spacing(2), + paddingBottom: theme.spacing(2), + [theme.breakpoints.up('md')]: { + gridTemplateColumns: `300px 1fr`, + }, +})); + +const ChartWidget = styled(Widget)(({ theme }) => ({ + [theme.breakpoints.down('md')]: { + gridColumnStart: 'span 2', + order: 2, + }, +})); + +export const LegacyInsightsCharts: VFC = ({ + projects, + flags, + users, + summary, + userTrends, + groupedProjectsData, + flagTrends, + groupedMetricsData, + environmentTypeTrends, + allMetricsDatapoints, + loading, +}) => { + const { isEnterprise } = useUiConfig(); + const showAllProjects = projects[0] === allOption.id; + const isOneProjectSelected = projects.length === 1; + + function getFlagsPerUser( + flags: InstanceInsightsSchemaFlags, + users: InstanceInsightsSchemaUsers, + ) { + const flagsPerUserCalculation = flags.total / users.total; + return Number.isNaN(flagsPerUserCalculation) + ? 'N/A' + : flagsPerUserCalculation.toFixed(2); + } + + return ( + <> + + + + + } + elseShow={ + + + + } + /> + + + + } + elseShow={ + + + + } + /> + + + + + + + } + elseShow={ + + + + } + /> + + + + + + + + + + + + + + + } + /> + + + + + + theme.spacing(2) }} + > + + + + } + /> + + ); +}; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 4c91256200c2..860e155cb3b4 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -90,6 +90,7 @@ export type UiFlags = { commandBarUI?: boolean; flagCreator?: boolean; resourceLimits?: boolean; + insightsV2?: boolean; }; export interface IVersionInfo { diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index f7c66b6045ad..ec18e916e79b 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -126,6 +126,7 @@ exports[`should create default config 1`] = ` "filterInvalidClientMetrics": false, "flagCreator": false, "googleAuthEnabled": false, + "insightsV2": false, "killInsightsUI": false, "killScheduledChangeRequestCache": false, "maintenanceMode": false, diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 906604a882b9..2cc86ea82a97 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -66,7 +66,8 @@ export type IFlagKey = | 'extendedMetrics' | 'cleanApiTokenWhenOrphaned' | 'allowOrphanedWildcardTokens' - | 'removeUnsafeInlineStyleSrc'; + | 'removeUnsafeInlineStyleSrc' + | 'insightsV2'; export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; @@ -319,6 +320,10 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_REMOVE_UNSAFE_INLINE_STYLE_SRC, false, ), + insightsV2: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_INSIGHTS_V2, + false, + ), }; export const defaultExperimentalOptions: IExperimentalOptions = { diff --git a/src/server-dev.ts b/src/server-dev.ts index 6dd12fcf6c99..877ed1065c8b 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -56,6 +56,7 @@ process.nextTick(async () => { flagCreator: true, resourceLimits: true, extendedMetrics: true, + insightsV2: true, }, }, authentication: {