mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: chart info naming bug fix (#6660)
Rename param Signed-off-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
		
							parent
							
								
									2f7580e6b1
								
							
						
					
					
						commit
						9233d4ca33
					
				@ -41,7 +41,7 @@ interface IChartsProps {
 | 
				
			|||||||
        averageHealth?: string;
 | 
					        averageHealth?: string;
 | 
				
			||||||
        flagsPerUser?: string;
 | 
					        flagsPerUser?: string;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    avgDaysToProduction: number;
 | 
					    medianTimeToProduction: number;
 | 
				
			||||||
    loading: boolean;
 | 
					    loading: boolean;
 | 
				
			||||||
    projects: string[];
 | 
					    projects: string[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -71,7 +71,7 @@ export const Charts: VFC<IChartsProps> = ({
 | 
				
			|||||||
    userTrends,
 | 
					    userTrends,
 | 
				
			||||||
    groupedProjectsData,
 | 
					    groupedProjectsData,
 | 
				
			||||||
    flagTrends,
 | 
					    flagTrends,
 | 
				
			||||||
    avgDaysToProduction,
 | 
					    medianTimeToProduction,
 | 
				
			||||||
    groupedMetricsData,
 | 
					    groupedMetricsData,
 | 
				
			||||||
    environmentTypeTrends,
 | 
					    environmentTypeTrends,
 | 
				
			||||||
    loading,
 | 
					    loading,
 | 
				
			||||||
@ -165,8 +165,10 @@ export const Charts: VFC<IChartsProps> = ({
 | 
				
			|||||||
                        isAggregate={showAllProjects}
 | 
					                        isAggregate={showAllProjects}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
                </ChartWidget>
 | 
					                </ChartWidget>
 | 
				
			||||||
                <Widget {...chartInfo.averageTimeToProduction}>
 | 
					                <Widget {...chartInfo.medianTimeToProduction}>
 | 
				
			||||||
                    <TimeToProduction daysToProduction={avgDaysToProduction} />
 | 
					                    <TimeToProduction
 | 
				
			||||||
 | 
					                        daysToProduction={medianTimeToProduction}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
                </Widget>
 | 
					                </Widget>
 | 
				
			||||||
                <ChartWidget
 | 
					                <ChartWidget
 | 
				
			||||||
                    {...(showAllProjects
 | 
					                    {...(showAllProjects
 | 
				
			||||||
 | 
				
			|||||||
@ -50,7 +50,7 @@ export const chartInfo = {
 | 
				
			|||||||
        tooltip:
 | 
					        tooltip:
 | 
				
			||||||
            'How the overall health changes over time for the selected projects.',
 | 
					            'How the overall health changes over time for the selected projects.',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    averageTimeToProduction: {
 | 
					    medianTimeToProduction: {
 | 
				
			||||||
        title: 'Median time to production',
 | 
					        title: 'Median time to production',
 | 
				
			||||||
        tooltip:
 | 
					        tooltip:
 | 
				
			||||||
            'How long does it currently take on average from when a feature flag was created until it was enabled in a "production" type environment. This is calculated only from feature flags of the type "release" and is the median across the selected projects.',
 | 
					            'How long does it currently take on average from when a feature flag was created until it was enabled in a "production" type environment. This is calculated only from feature flags of the type "release" and is the median across the selected projects.',
 | 
				
			||||||
@ -63,7 +63,7 @@ export const chartInfo = {
 | 
				
			|||||||
    timeToProductionPerProject: {
 | 
					    timeToProductionPerProject: {
 | 
				
			||||||
        title: 'Time to production per project',
 | 
					        title: 'Time to production per project',
 | 
				
			||||||
        tooltip:
 | 
					        tooltip:
 | 
				
			||||||
            'How the median time to production changes over time for the selected projects.',
 | 
					            'How the average time to production changes over time for the selected projects.',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    metrics: {
 | 
					    metrics: {
 | 
				
			||||||
        title: 'Flag evaluation metrics',
 | 
					        title: 'Flag evaluation metrics',
 | 
				
			||||||
 | 
				
			|||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
import { useAvgTimeToProduction } from './useAvgTimeToProduction';
 | 
					 | 
				
			||||||
import { renderHook } from '@testing-library/react-hooks';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('useAvgTimeToProduction', () => {
 | 
					 | 
				
			||||||
    test('returns 0 when projectsData is empty', () => {
 | 
					 | 
				
			||||||
        const projectsData = {};
 | 
					 | 
				
			||||||
        const { result } = renderHook(() =>
 | 
					 | 
				
			||||||
            useAvgTimeToProduction(projectsData),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        expect(result.current).toBe(0);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('calculates average time to production based on the latest date correctly', () => {
 | 
					 | 
				
			||||||
        const projectsData = {
 | 
					 | 
				
			||||||
            project1: [
 | 
					 | 
				
			||||||
                { timeToProduction: 10, date: '2023-01-01' },
 | 
					 | 
				
			||||||
                { timeToProduction: 20, date: '2023-02-01' },
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            project2: [
 | 
					 | 
				
			||||||
                { timeToProduction: 15, date: '2023-01-15' },
 | 
					 | 
				
			||||||
                { timeToProduction: 25, date: '2023-02-15' },
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
        } as any;
 | 
					 | 
				
			||||||
        const { result } = renderHook(() =>
 | 
					 | 
				
			||||||
            useAvgTimeToProduction(projectsData),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        // Expect average of the latest timeToProductions (20 from project1 and 25 from project2)
 | 
					 | 
				
			||||||
        expect(result.current).toBe(22.5);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('ignores projects without time to production data in their latest entries', () => {
 | 
					 | 
				
			||||||
        const projectsData = {
 | 
					 | 
				
			||||||
            project1: [
 | 
					 | 
				
			||||||
                { timeToProduction: 10, date: '2023-01-01' },
 | 
					 | 
				
			||||||
                { timeToProduction: 20, date: '2023-02-01' },
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            project2: [
 | 
					 | 
				
			||||||
                { date: '2023-01-15' },
 | 
					 | 
				
			||||||
                { timeToProduction: 25, date: '2023-01-10' },
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
        } as any;
 | 
					 | 
				
			||||||
        const { result } = renderHook(() =>
 | 
					 | 
				
			||||||
            useAvgTimeToProduction(projectsData),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        // Since project2's latest entry doesn't have timeToProduction, only project1's latest is considered
 | 
					 | 
				
			||||||
        expect(result.current).toBe(20);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
import { useMemo } from 'react';
 | 
					 | 
				
			||||||
import type {
 | 
					 | 
				
			||||||
    ExecutiveSummarySchema,
 | 
					 | 
				
			||||||
    ExecutiveSummarySchemaProjectFlagTrendsItem,
 | 
					 | 
				
			||||||
} from 'openapi';
 | 
					 | 
				
			||||||
import type { GroupedDataByProject } from './useGroupedProjectTrends';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const validTrend = (trend: ExecutiveSummarySchemaProjectFlagTrendsItem) =>
 | 
					 | 
				
			||||||
    Boolean(trend) && Boolean(trend.timeToProduction);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const useAvgTimeToProduction = (
 | 
					 | 
				
			||||||
    projectsData: GroupedDataByProject<
 | 
					 | 
				
			||||||
        ExecutiveSummarySchema['projectFlagTrends']
 | 
					 | 
				
			||||||
    >,
 | 
					 | 
				
			||||||
) =>
 | 
					 | 
				
			||||||
    useMemo(() => {
 | 
					 | 
				
			||||||
        let totalProjects = Object.keys(projectsData).length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (totalProjects === 0) {
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const totalAvgTimeToProduction = Object.entries(projectsData).reduce(
 | 
					 | 
				
			||||||
            (acc, [_, trends]) => {
 | 
					 | 
				
			||||||
                const latestTrend = trends.reduce(
 | 
					 | 
				
			||||||
                    (latest, current) =>
 | 
					 | 
				
			||||||
                        new Date(latest.date) < new Date(current.date)
 | 
					 | 
				
			||||||
                            ? current
 | 
					 | 
				
			||||||
                            : latest,
 | 
					 | 
				
			||||||
                    trends[0],
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // If there's no valid latest trend, this project won't contribute to the average
 | 
					 | 
				
			||||||
                if (!validTrend(latestTrend)) {
 | 
					 | 
				
			||||||
                    totalProjects--;
 | 
					 | 
				
			||||||
                    return acc;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                const timeToProduction = latestTrend.timeToProduction || 0;
 | 
					 | 
				
			||||||
                return acc + timeToProduction;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            0,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const overallAverage = totalAvgTimeToProduction / totalProjects;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return overallAverage;
 | 
					 | 
				
			||||||
    }, [projectsData]);
 | 
					 | 
				
			||||||
@ -3,7 +3,7 @@ import type { ExecutiveSummarySchema } from 'openapi';
 | 
				
			|||||||
import { useFilteredTrends } from './useFilteredTrends';
 | 
					import { useFilteredTrends } from './useFilteredTrends';
 | 
				
			||||||
import { useGroupedProjectTrends } from './useGroupedProjectTrends';
 | 
					import { useGroupedProjectTrends } from './useGroupedProjectTrends';
 | 
				
			||||||
import { useFilteredFlagsSummary } from './useFilteredFlagsSummary';
 | 
					import { useFilteredFlagsSummary } from './useFilteredFlagsSummary';
 | 
				
			||||||
import { useAvgTimeToProduction } from './useAvgTimeToProduction';
 | 
					import { useMedianTimeToProduction } from './useMedianTimeToProduction';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const useDashboardData = (
 | 
					export const useDashboardData = (
 | 
				
			||||||
    executiveDashboardData: ExecutiveSummarySchema,
 | 
					    executiveDashboardData: ExecutiveSummarySchema,
 | 
				
			||||||
@ -27,7 +27,8 @@ export const useDashboardData = (
 | 
				
			|||||||
        executiveDashboardData.users,
 | 
					        executiveDashboardData.users,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const avgDaysToProduction = useAvgTimeToProduction(groupedProjectsData);
 | 
					    const medianTimeToProduction =
 | 
				
			||||||
 | 
					        useMedianTimeToProduction(groupedProjectsData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return useMemo(
 | 
					    return useMemo(
 | 
				
			||||||
        () => ({
 | 
					        () => ({
 | 
				
			||||||
@ -39,7 +40,7 @@ export const useDashboardData = (
 | 
				
			|||||||
            users: executiveDashboardData.users,
 | 
					            users: executiveDashboardData.users,
 | 
				
			||||||
            environmentTypeTrends: executiveDashboardData.environmentTypeTrends,
 | 
					            environmentTypeTrends: executiveDashboardData.environmentTypeTrends,
 | 
				
			||||||
            summary,
 | 
					            summary,
 | 
				
			||||||
            avgDaysToProduction,
 | 
					            medianTimeToProduction,
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
        [
 | 
					        [
 | 
				
			||||||
            executiveDashboardData,
 | 
					            executiveDashboardData,
 | 
				
			||||||
@ -49,7 +50,7 @@ export const useDashboardData = (
 | 
				
			|||||||
            metricsData,
 | 
					            metricsData,
 | 
				
			||||||
            groupedMetricsData,
 | 
					            groupedMetricsData,
 | 
				
			||||||
            summary,
 | 
					            summary,
 | 
				
			||||||
            avgDaysToProduction,
 | 
					            medianTimeToProduction,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					import { useMedianTimeToProduction } from './useMedianTimeToProduction';
 | 
				
			||||||
 | 
					import { renderHook } from '@testing-library/react-hooks';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('useMedianTimeToProduction', () => {
 | 
				
			||||||
 | 
					    test('returns 0 when projectsData is empty', () => {
 | 
				
			||||||
 | 
					        const projectsData = {};
 | 
				
			||||||
 | 
					        const { result } = renderHook(() =>
 | 
				
			||||||
 | 
					            useMedianTimeToProduction(projectsData),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(result.current).toBe(0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('calculates median time to production correctly with even number of projects', () => {
 | 
				
			||||||
 | 
					        const projectsData = {
 | 
				
			||||||
 | 
					            project1: [
 | 
				
			||||||
 | 
					                { timeToProduction: 10, date: '2023-01-01' },
 | 
				
			||||||
 | 
					                { timeToProduction: 20, date: '2023-02-01' },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            project2: [
 | 
				
			||||||
 | 
					                { timeToProduction: 15, date: '2023-01-15' },
 | 
				
			||||||
 | 
					                { timeToProduction: 25, date: '2023-02-15' },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            project3: [{ timeToProduction: 30, date: '2023-01-20' }],
 | 
				
			||||||
 | 
					        } as any;
 | 
				
			||||||
 | 
					        const { result } = renderHook(() =>
 | 
				
			||||||
 | 
					            useMedianTimeToProduction(projectsData),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        // With sorted timeToProductions [10, 15, 20, 25, 30], median is the middle value, 20.
 | 
				
			||||||
 | 
					        expect(result.current).toBe(20);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('calculates median time to production correctly with odd number of projects', () => {
 | 
				
			||||||
 | 
					        const projectsData = {
 | 
				
			||||||
 | 
					            project1: [
 | 
				
			||||||
 | 
					                { timeToProduction: 10, date: '2023-01-01' },
 | 
				
			||||||
 | 
					                { timeToProduction: 20, date: '2023-02-01' },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            project2: [{ timeToProduction: 15, date: '2023-01-15' }],
 | 
				
			||||||
 | 
					        } as any;
 | 
				
			||||||
 | 
					        const { result } = renderHook(() =>
 | 
				
			||||||
 | 
					            useMedianTimeToProduction(projectsData),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        // With sorted timeToProductions [10, 15, 20], median is the middle value, 15.
 | 
				
			||||||
 | 
					        expect(result.current).toBe(15);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('correctly handles all valid time to production values', () => {
 | 
				
			||||||
 | 
					        const projectsData = {
 | 
				
			||||||
 | 
					            project1: [{ timeToProduction: 10, date: '2023-01-01' }],
 | 
				
			||||||
 | 
					            project2: [{ timeToProduction: 25, date: '2023-01-10' }],
 | 
				
			||||||
 | 
					            project3: [{ date: '2023-01-15' }],
 | 
				
			||||||
 | 
					            project4: [
 | 
				
			||||||
 | 
					                { timeToProduction: 30, date: '2023-02-01' },
 | 
				
			||||||
 | 
					                { timeToProduction: 20, date: '2023-02-02' },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        } as any;
 | 
				
			||||||
 | 
					        const { result } = renderHook(() =>
 | 
				
			||||||
 | 
					            useMedianTimeToProduction(projectsData),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        // With sorted timeToProductions [10, 20, 25, 30], the median is the average of 20 and 25, i.e., 22.5.
 | 
				
			||||||
 | 
					        expect(result.current).toBe(22.5);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import { useMemo } from 'react';
 | 
				
			||||||
 | 
					import type {
 | 
				
			||||||
 | 
					    ExecutiveSummarySchema,
 | 
				
			||||||
 | 
					    ExecutiveSummarySchemaProjectFlagTrendsItem,
 | 
				
			||||||
 | 
					} from 'openapi';
 | 
				
			||||||
 | 
					import type { GroupedDataByProject } from './useGroupedProjectTrends';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const validTrend = (trend: ExecutiveSummarySchemaProjectFlagTrendsItem) =>
 | 
				
			||||||
 | 
					    Boolean(trend) && Boolean(trend.timeToProduction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useMedianTimeToProduction = (
 | 
				
			||||||
 | 
					    projectsData: GroupedDataByProject<
 | 
				
			||||||
 | 
					        ExecutiveSummarySchema['projectFlagTrends']
 | 
				
			||||||
 | 
					    >,
 | 
				
			||||||
 | 
					) =>
 | 
				
			||||||
 | 
					    useMemo(() => {
 | 
				
			||||||
 | 
					        const timesToProduction: number[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Object.values(projectsData).forEach((trends) => {
 | 
				
			||||||
 | 
					            trends.forEach((trend) => {
 | 
				
			||||||
 | 
					                if (validTrend(trend)) {
 | 
				
			||||||
 | 
					                    timesToProduction.push(trend.timeToProduction!);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (timesToProduction.length === 0) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        timesToProduction.sort((a, b) => a - b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const midIndex = Math.floor(timesToProduction.length / 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const median =
 | 
				
			||||||
 | 
					            timesToProduction.length % 2 === 0
 | 
				
			||||||
 | 
					                ? (timesToProduction[midIndex - 1] +
 | 
				
			||||||
 | 
					                      timesToProduction[midIndex]) /
 | 
				
			||||||
 | 
					                  2
 | 
				
			||||||
 | 
					                : timesToProduction[midIndex];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return median;
 | 
				
			||||||
 | 
					    }, [projectsData]);
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user