diff --git a/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx b/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx index 3645731f9bf7..e6dd57714403 100644 --- a/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx +++ b/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx @@ -18,6 +18,11 @@ import { ProjectHealthChart } from './ProjectHealthChart/ProjectHealthChart'; import { TimeToProductionChart } from './TimeToProductionChart/TimeToProductionChart'; import { TimeToProduction } from './TimeToProduction/TimeToProduction'; import { ProjectSelect, allOption } from './ProjectSelect/ProjectSelect'; +import { MetricsSummaryChart } from './MetricsSummaryChart/MetricsSummaryChart'; +import { + ExecutiveSummarySchemaMetricsSummaryTrendsItem, + ExecutiveSummarySchemaProjectFlagTrendsItem, +} from '../../openapi'; import { HealthStats } from './HealthStats/HealthStats'; const StyledGrid = styled(Box)(({ theme }) => ({ @@ -61,6 +66,11 @@ const useDashboardGrid = () => { }; }; +interface FilteredProjectData { + filteredProjectFlagTrends: ExecutiveSummarySchemaProjectFlagTrendsItem[]; + filteredMetricsSummaryTrends: ExecutiveSummarySchemaMetricsSummaryTrendsItem[]; +} + export const ExecutiveDashboard: VFC = () => { const { executiveDashboardData, loading, error } = useExecutiveDashboard(); const [projects, setProjects] = useState([allOption.id]); @@ -78,15 +88,32 @@ export const ExecutiveDashboard: VFC = () => { ).toFixed(1); }, [executiveDashboardData]); - const filteredProjectFlagTrends = useMemo(() => { - if (projects[0] === allOption.id) { - return executiveDashboardData.projectFlagTrends; - } + const { filteredProjectFlagTrends, filteredMetricsSummaryTrends } = + useMemo(() => { + if (projects[0] === allOption.id) { + return { + filteredProjectFlagTrends: + executiveDashboardData.projectFlagTrends, + filteredMetricsSummaryTrends: + executiveDashboardData.metricsSummaryTrends, + }; + } + + const filteredProjectFlagTrends = + executiveDashboardData.projectFlagTrends.filter((trend) => + projects.includes(trend.project), + ) as ExecutiveSummarySchemaProjectFlagTrendsItem[]; - return executiveDashboardData.projectFlagTrends.filter((trend) => - projects.includes(trend.project), - ); - }, [executiveDashboardData, projects]); + const filteredImpressionsSummary = + executiveDashboardData.metricsSummaryTrends.filter((summary) => + projects.includes(summary.project), + ) as ExecutiveSummarySchemaMetricsSummaryTrendsItem[]; + + return { + filteredProjectFlagTrends, + filteredMetricsSummaryTrends: filteredImpressionsSummary, + }; + }, [executiveDashboardData, projects]); const { gridTemplateColumns, @@ -159,13 +186,23 @@ export const ExecutiveDashboard: VFC = () => { projectFlagTrends={filteredProjectFlagTrends} /> - + + + + + - + diff --git a/frontend/src/component/executiveDashboard/MetricsSummaryChart/MetricsSummaryChart.tsx b/frontend/src/component/executiveDashboard/MetricsSummaryChart/MetricsSummaryChart.tsx new file mode 100644 index 000000000000..7ce6d1a2f896 --- /dev/null +++ b/frontend/src/component/executiveDashboard/MetricsSummaryChart/MetricsSummaryChart.tsx @@ -0,0 +1,17 @@ +import { type VFC } from 'react'; +import 'chartjs-adapter-date-fns'; +import { ExecutiveSummarySchema } from 'openapi'; +import { LineChart } from '../LineChart/LineChart'; +import { useMetricsSummary } from '../useMetricsSummary'; + +interface IMetricsSummaryChartProps { + metricsSummaryTrends: ExecutiveSummarySchema['metricsSummaryTrends']; +} + +export const MetricsSummaryChart: VFC = ({ + metricsSummaryTrends, +}) => { + const data = useMetricsSummary(metricsSummaryTrends, 'total'); + + return ; +}; diff --git a/frontend/src/component/executiveDashboard/useMetricsSummary.ts b/frontend/src/component/executiveDashboard/useMetricsSummary.ts new file mode 100644 index 000000000000..312f22d7969a --- /dev/null +++ b/frontend/src/component/executiveDashboard/useMetricsSummary.ts @@ -0,0 +1,58 @@ +import { useMemo } from 'react'; +import { getProjectColor } from './executive-dashboard-utils'; +import { useTheme } from '@mui/material'; +import { + ExecutiveSummarySchema, + ExecutiveSummarySchemaMetricsSummaryTrendsItem, +} from '../../openapi'; + +type MetricsSummaryTrends = ExecutiveSummarySchema['metricsSummaryTrends']; + +export const useMetricsSummary = ( + metricsSummaryTrends: MetricsSummaryTrends, + field: 'total' | 'totalYes' | 'totalNo' | 'totalApps', +) => { + const theme = useTheme(); + + const data = useMemo(() => { + const groupedFlagTrends = metricsSummaryTrends.reduce< + Record + >((groups, item) => { + if (!groups[item.project]) { + groups[item.project] = []; + } + groups[item.project].push(item); + return groups; + }, {}); + + const datasets = Object.entries(groupedFlagTrends).map( + ([project, metricsSummaryTrends]) => { + const color = getProjectColor(project); + return { + label: project, + data: metricsSummaryTrends.map((item) => { + if (field !== 'total') { + return item[field] || 0; + } + return item.totalYes + item.totalNo || 0; + }), + borderColor: color, + backgroundColor: color, + fill: false, + }; + }, + ); + + const objectKeys = Object.keys(groupedFlagTrends); + + const firstElementSummary = groupedFlagTrends[objectKeys[0]] || []; + const firstElementsDates = firstElementSummary.map((item) => item.date); + + return { + labels: firstElementsDates, + datasets, + }; + }, [theme, metricsSummaryTrends]); + + return data; +}; diff --git a/frontend/src/hooks/api/getters/useExecutiveSummary/useExecutiveSummary.ts b/frontend/src/hooks/api/getters/useExecutiveSummary/useExecutiveSummary.ts index e62f8c5af097..4ad7312fcd03 100644 --- a/frontend/src/hooks/api/getters/useExecutiveSummary/useExecutiveSummary.ts +++ b/frontend/src/hooks/api/getters/useExecutiveSummary/useExecutiveSummary.ts @@ -33,7 +33,7 @@ export const useExecutiveDashboard = ( userTrends: [], flagTrends: [], projectFlagTrends: [], - impressionsSummary: [], + metricsSummaryTrends: [], }, refetchExecutiveDashboard, loading: !error && !data, diff --git a/frontend/src/openapi/models/executiveSummarySchema.ts b/frontend/src/openapi/models/executiveSummarySchema.ts index 73b32728a403..5c150fd147b1 100644 --- a/frontend/src/openapi/models/executiveSummarySchema.ts +++ b/frontend/src/openapi/models/executiveSummarySchema.ts @@ -5,7 +5,7 @@ */ import type { ExecutiveSummarySchemaFlags } from './executiveSummarySchemaFlags'; import type { ExecutiveSummarySchemaFlagTrendsItem } from './executiveSummarySchemaFlagTrendsItem'; -import type { ExecutiveSummarySchemaImpressionsSummaryItem } from './executiveSummarySchemaImpressionsSummaryItem'; +import type { ExecutiveSummarySchemaMetricsSummaryTrendsItem } from './executiveSummarySchemaMetricsSummaryTrendsItem'; import type { ExecutiveSummarySchemaProjectFlagTrendsItem } from './executiveSummarySchemaProjectFlagTrendsItem'; import type { ExecutiveSummarySchemaUsers } from './executiveSummarySchemaUsers'; import type { ExecutiveSummarySchemaUserTrendsItem } from './executiveSummarySchemaUserTrendsItem'; @@ -18,8 +18,8 @@ export interface ExecutiveSummarySchema { flags: ExecutiveSummarySchemaFlags; /** How number of flags changed over time */ flagTrends: ExecutiveSummarySchemaFlagTrendsItem[]; - /** How impression data per project changed over time */ - impressionsSummary: ExecutiveSummarySchemaImpressionsSummaryItem[]; + /** How metrics data per project changed over time */ + metricsSummaryTrends: ExecutiveSummarySchemaMetricsSummaryTrendsItem[]; /** How number of flags per project changed over time */ projectFlagTrends: ExecutiveSummarySchemaProjectFlagTrendsItem[]; /** High level user count statistics */ diff --git a/frontend/src/openapi/models/executiveSummarySchemaImpressionsSummaryItem.ts b/frontend/src/openapi/models/executiveSummarySchemaMetricsSummaryTrendsItem.ts similarity index 92% rename from frontend/src/openapi/models/executiveSummarySchemaImpressionsSummaryItem.ts rename to frontend/src/openapi/models/executiveSummarySchemaMetricsSummaryTrendsItem.ts index 23c2aeb03a53..c9b2b679d70c 100644 --- a/frontend/src/openapi/models/executiveSummarySchemaImpressionsSummaryItem.ts +++ b/frontend/src/openapi/models/executiveSummarySchemaMetricsSummaryTrendsItem.ts @@ -4,7 +4,7 @@ * See `gen:api` script in package.json */ -export type ExecutiveSummarySchemaImpressionsSummaryItem = { +export type ExecutiveSummarySchemaMetricsSummaryTrendsItem = { /** Date the impressions summary were calculated */ date: string; /** Project id of the project the impressions summary belong to */ diff --git a/frontend/src/openapi/models/index.ts b/frontend/src/openapi/models/index.ts index b207cece073f..c85c4bcd61ac 100644 --- a/frontend/src/openapi/models/index.ts +++ b/frontend/src/openapi/models/index.ts @@ -512,7 +512,7 @@ export * from './eventsSchemaVersion'; export * from './executiveSummarySchema'; export * from './executiveSummarySchemaFlagTrendsItem'; export * from './executiveSummarySchemaFlags'; -export * from './executiveSummarySchemaImpressionsSummaryItem'; +export * from './executiveSummarySchemaMetricsSummaryTrendsItem'; export * from './executiveSummarySchemaProjectFlagTrendsItem'; export * from './executiveSummarySchemaUserTrendsItem'; export * from './executiveSummarySchemaUsers';