diff --git a/frontend/src/component/insights/componentsChart/CreationArchiveChart/CreationArchiveChart.tsx b/frontend/src/component/insights/componentsChart/CreationArchiveChart/CreationArchiveChart.tsx index c92ee0258d..553cc9bdbc 100644 --- a/frontend/src/component/insights/componentsChart/CreationArchiveChart/CreationArchiveChart.tsx +++ b/frontend/src/component/insights/componentsChart/CreationArchiveChart/CreationArchiveChart.tsx @@ -30,6 +30,8 @@ import { GraphCover } from 'component/insights/GraphCover.tsx'; import { batchCreationArchiveData } from './batchCreationArchiveData.ts'; import { useBatchedTooltipDate } from '../useBatchedTooltipDate.ts'; import { aggregateCreationArchiveData } from './aggregateCreationArchiveData.ts'; +import type { Theme } from '@mui/material/styles/createTheme'; +import type { ChartData } from '../chartData.ts'; ChartJS.register( CategoryScale, @@ -51,6 +53,42 @@ interface ICreationArchiveChartProps { labels: { week: string; date: string }[]; } +const batchSize = 4; + +const sharedDatasetOptions = (lineColor: string) => ({ + backgroundColor: lineColor, + borderColor: lineColor, + hoverBackgroundColor: lineColor, + hoverBorderColor: lineColor, +}); +const makeChartData = ( + data: FinalizedWeekData[] | BatchedWeekData[], + theme: Theme, +) => ({ + datasets: [ + { + label: 'Flags archived', + data: data, + order: 1, + parsing: { + yAxisKey: 'archivedFlags', + xAxisKey: 'date', + }, + ...sharedDatasetOptions(theme.palette.charts.A2), + }, + { + label: 'Flags created', + data: data, + order: 2, + parsing: { + yAxisKey: 'totalCreatedFlags', + xAxisKey: 'date', + }, + ...sharedDatasetOptions(theme.palette.charts.A1), + }, + ], +}); + export const CreationArchiveChart: FC = ({ creationArchiveTrends, isLoading, @@ -61,62 +99,42 @@ export const CreationArchiveChart: FC = ({ const { locationSettings } = useLocationSettings(); const [tooltip, setTooltip] = useState(null); - const { dataResult, aggregateOrProjectData } = useMemo(() => { - const weeklyData = aggregateCreationArchiveData( - labels, - creationVsArchivedChart.datasets, - ); + const chartData: ChartData = + useMemo(() => { + if (isLoading) { + return { state: 'Loading', value: placeholderData }; + } - let dataResult: ChartDataResult = 'Weekly'; - let displayData: FinalizedWeekData[] | (BatchedWeekData | null)[] = - weeklyData; + const weeklyData = aggregateCreationArchiveData( + labels, + creationVsArchivedChart.datasets, + ); - if (weeklyData.length < 2) { - dataResult = 'Not Enough Data'; - } else if (weeklyData.length >= 12) { - dataResult = 'Batched'; - displayData = batchCreationArchiveData(weeklyData); - } + if (weeklyData.length < 2) { + return { state: 'Not Enough Data', value: placeholderData }; + } - return { - dataResult, - aggregateOrProjectData: { - datasets: [ - { - label: 'Flags archived', - data: displayData, - backgroundColor: theme.palette.charts.A2, - borderColor: theme.palette.charts.A2, - hoverBackgroundColor: theme.palette.charts.A2, - hoverBorderColor: theme.palette.charts.A2, - parsing: { - yAxisKey: 'archivedFlags', - xAxisKey: 'date', - }, - order: 1, - }, - { - label: 'Flags created', - data: displayData, - backgroundColor: theme.palette.charts.A1, - borderColor: theme.palette.charts.A1, - hoverBackgroundColor: theme.palette.charts.A1, - hoverBorderColor: theme.palette.charts.A1, - parsing: { - yAxisKey: 'totalCreatedFlags', - xAxisKey: 'date', - }, - order: 2, - }, - ], - }, - }; - }, [creationVsArchivedChart, theme]); + if (weeklyData.length >= 12) { + return { + state: 'Batched', + batchSize, + value: makeChartData( + batchCreationArchiveData(weeklyData, batchSize), + theme, + ), + }; + } - const useGraphCover = dataResult === 'Not Enough Data' || isLoading; - const showNotEnoughDataText = - dataResult === 'Not Enough Data' && !isLoading; - const data = useGraphCover ? placeholderData : aggregateOrProjectData; + return { + state: 'Weekly', + value: makeChartData(weeklyData, theme), + }; + }, [creationVsArchivedChart, theme]); + + const useGraphCover = ['Loading', 'Not Enough Data'].includes( + chartData.state, + ); + const showNotEnoughDataText = chartData.state === 'Not Enough Data'; const locale = getDateFnsLocale(locationSettings.locale); const batchedTooltipTitle = useBatchedTooltipDate(); @@ -153,7 +171,7 @@ export const CreationArchiveChart: FC = ({ external: createTooltip(setTooltip), callbacks: { title: - dataResult === 'Batched' + chartData.state === 'Batched' ? batchedTooltipTitle : undefined, }, @@ -171,7 +189,7 @@ export const CreationArchiveChart: FC = ({ display: true, time: { unit: - dataResult === 'Batched' + chartData.state === 'Batched' ? ('month' as const) : ('week' as const), tooltipFormat: 'P', @@ -181,7 +199,7 @@ export const CreationArchiveChart: FC = ({ }, ticks: { source: - dataResult === 'Batched' + chartData.state === 'Batched' ? ('auto' as const) : ('data' as const), display: !useGraphCover, @@ -208,7 +226,7 @@ export const CreationArchiveChart: FC = ({ return ( <> ({ merge: (accumulated: BatchedWeekData, next: FinalizedWeekData) => { if (next.state === 'empty') { return { @@ -53,4 +53,10 @@ export const batchCreationArchiveData = batchData({ endDate: item.date, }; }, + batchSize, }); + +export const batchCreationArchiveData = ( + data: FinalizedWeekData[], + batchSize?: number, +) => batchData(batchArgs(batchSize))(data); diff --git a/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/NewProductionFlagsChart.tsx b/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/NewProductionFlagsChart.tsx index c590917918..ddefe5ff09 100644 --- a/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/NewProductionFlagsChart.tsx +++ b/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/NewProductionFlagsChart.tsx @@ -1,29 +1,18 @@ import 'chartjs-adapter-date-fns'; -import { type FC, useMemo } from 'react'; -import type { InstanceInsightsSchema } from 'openapi'; -import { useProjectChartData } from 'component/insights/hooks/useProjectChartData'; +import type { FC } from 'react'; import { - fillGradientPrimary, LineChart, NotEnoughData, } from 'component/insights/components/LineChart/LineChart'; -import { useTheme } from '@mui/material'; -import type { GroupedDataByProject } from 'component/insights/hooks/useGroupedProjectTrends'; -import { usePlaceholderData } from 'component/insights/hooks/usePlaceholderData'; -import type { BatchedWeekData, WeekData } from './types.ts'; -import { batchProductionFlagsData } from './batchProductionFlagsData.ts'; import { useBatchedTooltipDate } from '../useBatchedTooltipDate.ts'; +import type { WeekData } from './types.ts'; +import type { ChartData } from '../chartData.ts'; -interface IProjectHealthChartProps { - lifecycleTrends: GroupedDataByProject< - InstanceInsightsSchema['lifecycleTrends'] - >; - isAggregate?: boolean; - isLoading?: boolean; - labels: { week: string; date: string }[]; +interface INewProductionFlagsChartProps { + chartData: ChartData; } -const useOverrideOptions = (chartDataResult: ChartDataResult) => { +const useOverrideOptions = (chartData: ChartData) => { const batchedTooltipTitle = useBatchedTooltipDate(); const sharedOptions = { parsing: { @@ -31,7 +20,7 @@ const useOverrideOptions = (chartDataResult: ChartDataResult) => { xAxisKey: 'date', }, }; - switch (chartDataResult) { + switch (chartData.state) { case 'Batched': { return { ...sharedOptions, @@ -58,112 +47,27 @@ const useOverrideOptions = (chartDataResult: ChartDataResult) => { case 'Weekly': return sharedOptions; case 'Not Enough Data': + case 'Loading': return {}; } }; -export const NewProductionFlagsChart: FC = ({ - labels, - lifecycleTrends, - isAggregate, - isLoading, +export const NewProductionFlagsChart: FC = ({ + chartData, }) => { - const lifecycleData = useProjectChartData(lifecycleTrends); - const theme = useTheme(); - const placeholderData = usePlaceholderData(); - - const shouldBatchData = labels.length >= 12; - - const { aggregateProductionFlagsData, chartDataResult } = useMemo(() => { - const weeks: WeekData[] = labels.map(({ week, date }) => { - return lifecycleData.datasets - .map((d) => d.data.find((item) => item.week === week)) - .reduce( - (acc: WeekData, item: WeekData) => { - if (item) { - acc.newProductionFlags = - (acc.newProductionFlags ?? 0) + - (item.newProductionFlags ?? 0); - } - return acc; - }, - { date, week }, - ); - }); - - let chartDataResult: ChartDataResult = 'Weekly'; - let displayData: WeekData[] | (BatchedWeekData | null)[] = weeks; - - if (!isLoading && labels.length < 2) { - chartDataResult = 'Not Enough Data'; - } else if (shouldBatchData) { - chartDataResult = 'Batched'; - displayData = batchProductionFlagsData(weeks); - } - - return { - chartDataResult, - aggregateProductionFlagsData: { - datasets: [ - { - label: 'Number of new flags', - data: displayData, - borderColor: theme.palette.primary.light, - backgroundColor: fillGradientPrimary, - fill: true, - order: 3, - }, - ], - }, - }; - }, [lifecycleData, theme, isLoading]); - - const padProjectData = () => { - if (lifecycleData.datasets.length === 0) { - // fallback for when there's no data in the selected time period for the selected projects - return [ - { - label: 'No data', - data: labels, - }, - ]; - } - - const padData = (data: WeekData[]) => { - const padded = labels.map( - ({ date, week }) => - data.find((item) => item?.week === week) ?? { - date, - week, - }, - ); - return shouldBatchData ? batchProductionFlagsData(padded) : padded; - }; - - return lifecycleData.datasets.map(({ data, ...dataset }) => ({ - ...dataset, - data: padData(data), - })); - }; - - const aggregateOrProjectData = isAggregate - ? aggregateProductionFlagsData - : { - datasets: padProjectData(), - }; - - const notEnoughData = chartDataResult === 'Not Enough Data'; - const data = - notEnoughData || isLoading ? placeholderData : aggregateOrProjectData; - - const overrideOptions = useOverrideOptions(chartDataResult); + const overrideOptions = useOverrideOptions(chartData); return ( : isLoading} + cover={ + chartData.state === 'Not Enough Data' ? ( + + ) : ( + chartData.state === 'Loading' + ) + } /> ); }; diff --git a/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/batchProductionFlagsData.test.ts b/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/batchProductionFlagsData.test.ts index a2d72097d0..fd33c07484 100644 --- a/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/batchProductionFlagsData.test.ts +++ b/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/batchProductionFlagsData.test.ts @@ -11,6 +11,7 @@ it('handles a single data point', () => { newProductionFlags: 5, date: input.date, endDate: input.date, + week: '50', }, ]); }); @@ -33,6 +34,7 @@ it('adds data in the expected way', () => { newProductionFlags: 11, date: '2022-01-01', endDate: '2022-02-01', + week: '50', }, ]); }); diff --git a/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/batchProductionFlagsData.ts b/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/batchProductionFlagsData.ts index 049c8ffeb4..195b26c2bd 100644 --- a/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/batchProductionFlagsData.ts +++ b/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/batchProductionFlagsData.ts @@ -1,7 +1,7 @@ import { batchData } from '../batchData.ts'; import type { BatchedWeekData, WeekData } from './types.ts'; -export const batchProductionFlagsData = batchData({ +const batchArgs = (batchSize?: number) => ({ merge: (accumulated: BatchedWeekData, next: WeekData) => { if (next.newProductionFlags) accumulated.newProductionFlags = @@ -16,11 +16,15 @@ export const batchProductionFlagsData = batchData({ return accumulated; }, map: (item: WeekData) => { - const { week: _, ...shared } = item; - return { - ...shared, + ...item, endDate: item.date, }; }, + batchSize, }); + +export const batchProductionFlagsData = ( + data: WeekData[], + batchSize?: number, +): BatchedWeekData[] => batchData(batchArgs(batchSize))(data); diff --git a/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/types.ts b/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/types.ts index d36dfd11ba..821eabdd5b 100644 --- a/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/types.ts +++ b/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/types.ts @@ -4,6 +4,6 @@ export type WeekData = { date: string; }; -export type BatchedWeekData = Omit & { +export type BatchedWeekData = WeekData & { endDate: string; }; diff --git a/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/useNewProductionFlagsData.ts b/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/useNewProductionFlagsData.ts new file mode 100644 index 0000000000..978c02c664 --- /dev/null +++ b/frontend/src/component/insights/componentsChart/NewProductionFlagsChart/useNewProductionFlagsData.ts @@ -0,0 +1,169 @@ +import { useTheme } from '@mui/material'; +import type { GroupedDataByProject } from 'component/insights/hooks/useGroupedProjectTrends'; +import { usePlaceholderData } from 'component/insights/hooks/usePlaceholderData'; +import { useProjectChartData } from 'component/insights/hooks/useProjectChartData'; +import type { InstanceInsightsSchema } from 'openapi'; +import type { BatchedWeekData, WeekData } from './types.ts'; +import { useMemo } from 'react'; +import { batchProductionFlagsData } from './batchProductionFlagsData.ts'; +import { fillGradientPrimary } from 'component/insights/components/LineChart/LineChart'; +import { calculateMedian } from 'component/insights/componentsStat/NewProductionFlagsStats/calculateMedian.ts'; +import { hasRealData, type ChartData } from '../chartData.ts'; +import type { ChartData as ChartJsData } from 'chart.js'; + +const batchSize = 4; + +type UseProductionFlagsDataProps = { + groupedLifecycleData: GroupedDataByProject< + InstanceInsightsSchema['lifecycleTrends'] + >; + showAllProjects?: boolean; + loading?: boolean; + labels: { week: string; date: string }[]; +}; + +type UseProductionFlagsDataResult = { + chartData: ChartData; + median: string | number; +}; + +const padProjectData = ({ + labels, + lifecycleData, + shouldBatchData, +}: { + labels: UseProductionFlagsDataProps['labels']; + lifecycleData: ChartJsData<'line', WeekData[]>; + shouldBatchData: boolean; +}) => { + if (lifecycleData.datasets.length === 0) { + // fallback for when there's no data in the selected time period for the selected projects + return [ + { + label: 'No data', + data: labels, + }, + ]; + } + + const padData = (data: WeekData[]) => { + const padded = labels.map( + ({ date, week }) => + data.find((item) => item?.week === week) ?? { + date, + week, + }, + ); + return shouldBatchData + ? batchProductionFlagsData(padded, batchSize) + : padded; + }; + + return lifecycleData.datasets.map(({ data, ...dataset }) => ({ + ...dataset, + data: padData(data), + })); +}; + +export const useProductionFlagsData = ({ + groupedLifecycleData, + showAllProjects, + loading, + labels, +}: UseProductionFlagsDataProps): UseProductionFlagsDataResult => { + const lifecycleData = useProjectChartData(groupedLifecycleData); + const theme = useTheme(); + const placeholderData = usePlaceholderData(); + + const shouldBatchData = labels.length >= 12; + + const chartData = useMemo((): ChartData< + WeekData | BatchedWeekData, + number + > => { + if (loading) { + return { + state: 'Loading', + value: placeholderData, + }; + } + + if (labels.length < 2) { + return { + state: 'Not Enough Data', + value: placeholderData, + }; + } + + const weeks: WeekData[] = labels.map(({ week, date }) => { + return lifecycleData.datasets + .map((d) => d.data.find((item: WeekData) => item.week === week)) + .reduce( + (acc: WeekData, item: WeekData) => { + if (item && item.newProductionFlags !== undefined) { + acc.newProductionFlags = + (acc.newProductionFlags ?? 0) + + item.newProductionFlags; + } + return acc; + }, + { date, week }, + ); + }); + + const chartArgs = (data: WeekData[] | BatchedWeekData[]) => ({ + datasets: [ + { + label: 'Number of new flags', + data, + borderColor: theme.palette.primary.light, + backgroundColor: fillGradientPrimary, + fill: true, + order: 3, + }, + ], + }); + + if (shouldBatchData) { + return { + state: 'Batched', + value: chartArgs(batchProductionFlagsData(weeks, batchSize)), + batchSize: batchSize, + }; + } + + return { + state: 'Weekly', + value: chartArgs(weeks), + }; + }, [lifecycleData, theme, loading]); + + if (!hasRealData(chartData)) { + return { + chartData, + median: 'N/A', + }; + } + + const { value: allProjectsData, ...metadata } = chartData; + + const value = showAllProjects + ? allProjectsData + : { + datasets: padProjectData({ + labels, + lifecycleData, + shouldBatchData, + }), + }; + + const median = calculateMedian(value.datasets); + + return { + chartData: { + ...metadata, + value, + }, + median, + }; +}; diff --git a/frontend/src/component/insights/componentsChart/batchData.test.ts b/frontend/src/component/insights/componentsChart/batchData.test.ts index 1372216c6f..fe025bf2c8 100644 --- a/frontend/src/component/insights/componentsChart/batchData.test.ts +++ b/frontend/src/component/insights/componentsChart/batchData.test.ts @@ -13,38 +13,3 @@ it('batches by 4, starting from the first entry', () => { ), ).toStrictEqual([50, 23]); }); - -describe('null entry handling', () => { - it('creates a new entry from the next item, if the accumulated is null', () => { - const input = [null, 9]; - const result = batchData({ - merge: (x, y) => x * y, - map: Math.sqrt, - })(input); - expect(result).toStrictEqual([3]); - }); - it('merges the accumulated entry with the next item if they are both present', () => { - const input = [4, 9]; - const result = batchData({ - merge: (x, y) => x + y, - map: (x) => x, - })(input); - expect(result).toStrictEqual([13]); - }); - it('it returns null if both the accumulated and the next item are null', () => { - const input = [null, null]; - const result = batchData({ - merge: (x, y) => x + y, - map: (x) => x, - })(input); - expect(result).toStrictEqual([null]); - }); - it('it returns the accumulated entry if the next item is null', () => { - const input = [7, null]; - const result = batchData({ - merge: (x, y) => x * y, - map: (x) => x, - })(input); - expect(result).toStrictEqual([7]); - }); -}); diff --git a/frontend/src/component/insights/componentsChart/batchData.ts b/frontend/src/component/insights/componentsChart/batchData.ts index a1d47a27fb..b42d40d54d 100644 --- a/frontend/src/component/insights/componentsChart/batchData.ts +++ b/frontend/src/component/insights/componentsChart/batchData.ts @@ -6,33 +6,22 @@ export type BatchDataOptions = { batchSize?: number; }; -const nullOrUndefined = (value: any): value is null | undefined => - value === null || value === undefined; - export const batchData = ({ merge, map, batchSize = defaultBatchSize, }: BatchDataOptions) => - (xs: (T | null)[]): (TBatched | null)[] => - xs.reduce( - (acc, curr, index) => { - const currentAggregatedIndex = Math.floor(index / batchSize); - const data = acc[currentAggregatedIndex]; + (xs: T[]): TBatched[] => + xs.reduce((acc, curr, index) => { + const currentAggregatedIndex = Math.floor(index / batchSize); + const data = acc[currentAggregatedIndex]; - const hasData = !nullOrUndefined(data); - const hasCurr = curr !== null; + if (data) { + acc[currentAggregatedIndex] = merge(data, curr); + } else { + acc[currentAggregatedIndex] = map(curr); + } - if (!hasData && !hasCurr) { - acc[currentAggregatedIndex] = null; - } else if (hasData && hasCurr) { - acc[currentAggregatedIndex] = merge(data, curr); - } else if (!hasData && hasCurr) { - acc[currentAggregatedIndex] = map(curr); - } - - return acc; - }, - [] as (TBatched | null)[], - ); + return acc; + }, [] as TBatched[]); diff --git a/frontend/src/component/insights/componentsChart/chartData.ts b/frontend/src/component/insights/componentsChart/chartData.ts new file mode 100644 index 0000000000..f9fa514e23 --- /dev/null +++ b/frontend/src/component/insights/componentsChart/chartData.ts @@ -0,0 +1,33 @@ +import type { ChartTypeRegistry, ChartData as ChartJsData } from 'chart.js'; + +export type ChartData< + T, + TPlaceholder = T, + TChartType extends keyof ChartTypeRegistry = any, +> = + | { state: 'Loading'; value: ChartJsData } + | { + state: 'Not Enough Data'; + value: ChartJsData; + } + | { state: 'Weekly'; value: ChartJsData } + | { + state: 'Batched'; + batchSize: number; + value: ChartJsData; + }; + +export const hasRealData = < + T, + TPlaceholder = T, + TChartType extends keyof ChartTypeRegistry = any, +>( + chartDataStatus: ChartData, +): chartDataStatus is + | { state: 'Weekly'; value: ChartJsData } + | { + state: 'Batched'; + batchSize: number; + value: ChartJsData; + } => + chartDataStatus.state === 'Weekly' || chartDataStatus.state === 'Batched'; diff --git a/frontend/src/component/insights/componentsChart/chartDataResult.ts b/frontend/src/component/insights/componentsChart/chartDataResult.ts deleted file mode 100644 index 0f48f12474..0000000000 --- a/frontend/src/component/insights/componentsChart/chartDataResult.ts +++ /dev/null @@ -1 +0,0 @@ -type ChartDataResult = 'Not Enough Data' | 'Batched' | 'Weekly'; diff --git a/frontend/src/component/insights/componentsStat/NewProductionFlagsStats/NewProductionFlagsStats.tsx b/frontend/src/component/insights/componentsStat/NewProductionFlagsStats/NewProductionFlagsStats.tsx new file mode 100644 index 0000000000..8a79233751 --- /dev/null +++ b/frontend/src/component/insights/componentsStat/NewProductionFlagsStats/NewProductionFlagsStats.tsx @@ -0,0 +1,28 @@ +import type { FC } from 'react'; +import { StatCard } from '../shared/StatCard.tsx'; +import type { ChartData } from 'component/insights/componentsChart/chartData.ts'; + +interface NewProductionFlagsStatsProps { + median: number | string; + chartData: ChartData; +} + +export const NewProductionFlagsStats: FC = ({ + median, + chartData, +}) => { + const label = + chartData.state === 'Batched' + ? `Median per ${chartData.batchSize} weeks` + : 'Median per week'; + + return ( + + ); +}; diff --git a/frontend/src/component/insights/hooks/usePlaceholderData.ts b/frontend/src/component/insights/hooks/usePlaceholderData.ts index c9ac9c3d70..8e155d4a40 100644 --- a/frontend/src/component/insights/hooks/usePlaceholderData.ts +++ b/frontend/src/component/insights/hooks/usePlaceholderData.ts @@ -1,6 +1,7 @@ import { useTheme } from '@mui/material'; import { useMemo } from 'react'; import { fillGradientPrimary } from '../components/LineChart/LineChart.jsx'; +import type { ChartData } from 'chart.js'; type PlaceholderDataOptions = { fill?: boolean; @@ -9,7 +10,7 @@ type PlaceholderDataOptions = { export const usePlaceholderData = ( placeholderDataOptions?: PlaceholderDataOptions, -) => { +): ChartData<'line', number[]> => { const { fill = false, type = 'constant' } = placeholderDataOptions || {}; const theme = useTheme(); diff --git a/frontend/src/component/insights/sections/PerformanceInsights.tsx b/frontend/src/component/insights/sections/PerformanceInsights.tsx index 9bfd3942c6..11e4535c98 100644 --- a/frontend/src/component/insights/sections/PerformanceInsights.tsx +++ b/frontend/src/component/insights/sections/PerformanceInsights.tsx @@ -22,13 +22,42 @@ import { StyledWidget, StyledWidgetContent, StyledWidgetStats, - StatsExplanation, } from '../InsightsCharts.styles'; import { useUiFlag } from 'hooks/useUiFlag'; import { NewProductionFlagsChart } from '../componentsChart/NewProductionFlagsChart/NewProductionFlagsChart.tsx'; -import Lightbulb from '@mui/icons-material/LightbulbOutlined'; import { CreationArchiveChart } from '../componentsChart/CreationArchiveChart/CreationArchiveChart.tsx'; import { CreationArchiveStats } from '../componentsStat/CreationArchiveStats/CreationArchiveStats.tsx'; +import { NewProductionFlagsStats } from '../componentsStat/NewProductionFlagsStats/NewProductionFlagsStats.tsx'; +import { useProductionFlagsData } from '../componentsChart/NewProductionFlagsChart/useNewProductionFlagsData.ts'; + +const NewProductionFlagsWidget = ({ + groupedLifecycleData, + loading, + showAllProjects, + labels, +}) => { + const { median, chartData } = useProductionFlagsData({ + groupedLifecycleData, + labels, + loading, + showAllProjects, + }); + + return ( + + + + + + + + + + ); +}; export const PerformanceInsights: FC = () => { const statePrefix = 'performance-'; @@ -85,23 +114,12 @@ export const PerformanceInsights: FC = () => { } > {isLifecycleGraphsEnabled && isEnterprise() ? ( - - - - - - How often do flags go live in production? - - - - - - + ) : null} {isLifecycleGraphsEnabled && isEnterprise() ? (