diff --git a/frontend/src/component/insights/Insights.tsx b/frontend/src/component/insights/Insights.tsx index 860305373b..9c8b06a517 100644 --- a/frontend/src/component/insights/Insights.tsx +++ b/frontend/src/component/insights/Insights.tsx @@ -59,7 +59,11 @@ export const Insights: VFC = () => { selectedProjects={projects} onChange={setProjects} dataTestId={'DASHBOARD_PROJECT_SELECT'} - sx={{ flex: 1, maxWidth: '360px', width: '100%' }} + sx={{ + flex: 1, + maxWidth: '360px', + width: '100%', + }} /> } /> diff --git a/frontend/src/component/insights/componentsChart/TimeToProductionChart/TimeToProductionTooltip/TimeToProductionTooltip.tsx b/frontend/src/component/insights/componentsChart/TimeToProductionChart/TimeToProductionTooltip/TimeToProductionTooltip.tsx index ded06ffba2..db21b40a24 100644 --- a/frontend/src/component/insights/componentsChart/TimeToProductionChart/TimeToProductionTooltip/TimeToProductionTooltip.tsx +++ b/frontend/src/component/insights/componentsChart/TimeToProductionChart/TimeToProductionTooltip/TimeToProductionTooltip.tsx @@ -17,7 +17,7 @@ const StyledItemHeader = styled(Box)(({ theme }) => ({ const getInterval = (days?: number) => { if (!days) { - return 'N/A'; + return 0; } if (days > 11) { diff --git a/frontend/src/component/insights/componentsChart/UpdatesPerEnvironmentTypeChart/UpdatesPerEnvironmentTypeChart.tsx b/frontend/src/component/insights/componentsChart/UpdatesPerEnvironmentTypeChart/UpdatesPerEnvironmentTypeChart.tsx index 04cf5bbf49..849f30e9c3 100644 --- a/frontend/src/component/insights/componentsChart/UpdatesPerEnvironmentTypeChart/UpdatesPerEnvironmentTypeChart.tsx +++ b/frontend/src/component/insights/componentsChart/UpdatesPerEnvironmentTypeChart/UpdatesPerEnvironmentTypeChart.tsx @@ -17,29 +17,55 @@ interface IUpdatesPerEnvironmnetTypeChart { isLoading?: boolean; } -const groupByDate = ( +export const groupByDateAndFillMissingDatapoints = ( items: InstanceInsightsSchemaEnvironmentTypeTrendsItem[], ): Record => { - if (!items) { + if (!items.length) { return {}; } - const grouped = items.reduce( - (acc, item) => { - const key = item.environmentType; + const initialGrouping: Record< + string, + InstanceInsightsSchemaEnvironmentTypeTrendsItem[] + > = {}; + for (const item of items) { + if (!initialGrouping[item.date]) { + initialGrouping[item.date] = []; + } + initialGrouping[item.date].push(item); + } - if (!acc[key]) { - acc[key] = []; - } - - acc[key].push(item); - - return acc; - }, - {} as Record, + const allEnvironmentTypes = Array.from( + new Set(items.map((item) => item.environmentType)), ); - return grouped; + const finalGrouping: Record< + string, + InstanceInsightsSchemaEnvironmentTypeTrendsItem[] + > = {}; + Object.entries(initialGrouping).forEach(([date, environmentItems]) => { + const fullSetForDate = allEnvironmentTypes.map((envType) => { + const existingItem = environmentItems.find( + (item) => item.environmentType === envType, + ); + if (existingItem) { + return existingItem; + } else { + const week = + items.find((item) => item.date === date)?.week || ''; + return { + date, + environmentType: envType, + totalUpdates: 0, + week, + }; + } + }); + + finalGrouping[date] = fullSetForDate; + }); + + return finalGrouping; }; const useEnvironmentTypeColor = () => { @@ -70,19 +96,38 @@ export const UpdatesPerEnvironmentTypeChart: VFC< const placeholderData = usePlaceholderData({ fill: true, type: 'double' }); const data = useMemo(() => { - const grouped = groupByDate(environmentTypeTrends); - const datasets = Object.entries(grouped).map( - ([environmentType, trends]) => { + const groupedByDate = groupByDateAndFillMissingDatapoints( + environmentTypeTrends, + ); + + const aggregatedByType: Record< + string, + InstanceInsightsSchemaEnvironmentTypeTrendsItem[] + > = {}; + + Object.entries(groupedByDate).forEach(([date, trends]) => { + trends.forEach((trend) => { + if (!aggregatedByType[trend.environmentType]) { + aggregatedByType[trend.environmentType] = []; + } + // Add an object that includes the date and totalUpdates for that environmentType + aggregatedByType[trend.environmentType].push(trend); + }); + }); + + const datasets = Object.entries(aggregatedByType).map( + ([environmentType, dataPoints]) => { const color = getEnvironmentTypeColor(environmentType); return { label: environmentType, - data: trends, + data: dataPoints, borderColor: color, backgroundColor: color, fill: false, }; }, ); + return { datasets }; }, [theme, environmentTypeTrends]); diff --git a/frontend/src/component/insights/componentsChart/UpdatesPerEnvironmentTypeChart/group-by-date.test.ts b/frontend/src/component/insights/componentsChart/UpdatesPerEnvironmentTypeChart/group-by-date.test.ts new file mode 100644 index 0000000000..8d0f464ba3 --- /dev/null +++ b/frontend/src/component/insights/componentsChart/UpdatesPerEnvironmentTypeChart/group-by-date.test.ts @@ -0,0 +1,87 @@ +import { groupByDateAndFillMissingDatapoints } from './UpdatesPerEnvironmentTypeChart'; + +describe('groupByDate', () => { + it('correctly groups items by date and includes all environment types', () => { + const items = [ + { + date: '2023-01-01', + environmentType: 'development', + totalUpdates: 5, + week: '2023-01', + }, + { + date: '2023-01-01', + environmentType: 'production', + totalUpdates: 3, + week: '2023-01', + }, + { + date: '2023-01-09', + environmentType: 'development', + totalUpdates: 2, + week: '2023-02', + }, + ]; + const grouped = groupByDateAndFillMissingDatapoints(items); + expect(Object.keys(grouped)).toEqual( + expect.arrayContaining(['2023-01-01', '2023-01-09']), + ); + expect(grouped['2023-01-01']).toMatchObject([ + { + date: '2023-01-01', + environmentType: 'development', + totalUpdates: 5, + week: '2023-01', + }, + { + date: '2023-01-01', + environmentType: 'production', + totalUpdates: 3, + week: '2023-01', + }, + ]); + + expect(grouped['2023-01-09']).toMatchObject([ + { + date: '2023-01-09', + environmentType: 'development', + totalUpdates: 2, + week: '2023-02', + }, + { + date: '2023-01-09', + environmentType: 'production', + totalUpdates: 0, + week: '2023-02', + }, + ]); + }); + + it('inserts placeholder items for missing environmentType data on some dates', () => { + const items = [ + { + date: '2023-01-01', + environmentType: 'development', + totalUpdates: 4, + week: '2023-01', + }, + { + date: '2023-01-09', + environmentType: 'production', + totalUpdates: 6, + week: '2023-02', + }, + ]; + const grouped = groupByDateAndFillMissingDatapoints(items); + const productionOnJan01 = grouped['2023-01-01']?.find( + (item) => item.environmentType === 'production', + ) ?? { totalUpdates: -1 }; + const developmentOnJan09 = grouped['2023-01-09']?.find( + (item) => item.environmentType === 'development', + ) ?? { totalUpdates: -1 }; + + expect(productionOnJan01.totalUpdates).toBe(0); + expect(developmentOnJan09.totalUpdates).toBe(0); + expect(grouped['2023-01-01']?.[1]?.week).toBe('2023-01'); + }); +});