diff --git a/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx b/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx index 0a8356f124..db578052ed 100644 --- a/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx +++ b/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx @@ -12,7 +12,6 @@ export const FlagsProjectChart: VFC = ({ projectFlagTrends, }) => { const data = useProjectChartData(projectFlagTrends); - return ( ({ + padding: theme.spacing(2), +})); + +const StyledItemHeader = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + gap: theme.spacing(2), + alignItems: 'center', +})); + +const InfoLine = ({ + iconChar, + title, + color, +}: { + iconChar: string; + title: string; + color: 'info' | 'success' | 'error'; +}) => ( + theme.palette[color].main, + }} + > + {iconChar} + {title} + +); + +const InfoSummary = ({ data }: { data: { key: string; value: number }[] }) => ( + + + {data.map(({ key, value }) => ( +
+
+ {key} +
+
{value}
+
+ ))} +
+
+); + +export const MetricsSummaryTooltip: VFC<{ tooltip: TooltipState | null }> = ({ + tooltip, +}) => { + const data = tooltip?.dataPoints.map((point) => { + return { + label: point.label, + title: point.dataset.label, + color: point.dataset.borderColor, + value: point.raw as ExecutiveSummarySchemaMetricsSummaryTrendsItem & { + total: number; + }, + }; + }); + + const limitedData = data?.slice(0, 5); + + return ( + ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + width: '300px', + })} + > + {limitedData?.map((point, index) => ( + + + + + {'● '} + + {point.title} + + + {point.label} + + + ({ margin: theme.spacing(1.5, 0) })} + /> + + + + ({ margin: theme.spacing(1.5, 0) })} + /> + + + )) || null} + + ); +}; diff --git a/frontend/src/component/executiveDashboard/MetricsSummaryChart/MetricsSummaryChart.tsx b/frontend/src/component/executiveDashboard/MetricsSummaryChart/MetricsSummaryChart.tsx index 7ce6d1a2f8..027f13e1e8 100644 --- a/frontend/src/component/executiveDashboard/MetricsSummaryChart/MetricsSummaryChart.tsx +++ b/frontend/src/component/executiveDashboard/MetricsSummaryChart/MetricsSummaryChart.tsx @@ -3,6 +3,7 @@ import 'chartjs-adapter-date-fns'; import { ExecutiveSummarySchema } from 'openapi'; import { LineChart } from '../LineChart/LineChart'; import { useMetricsSummary } from '../useMetricsSummary'; +import { MetricsSummaryTooltip } from './MetricsChartTooltip/MetricsChartTooltip'; interface IMetricsSummaryChartProps { metricsSummaryTrends: ExecutiveSummarySchema['metricsSummaryTrends']; @@ -11,7 +12,15 @@ interface IMetricsSummaryChartProps { export const MetricsSummaryChart: VFC = ({ metricsSummaryTrends, }) => { - const data = useMetricsSummary(metricsSummaryTrends, 'total'); - - return ; + const data = useMetricsSummary(metricsSummaryTrends); + return ( + + ); }; diff --git a/frontend/src/component/executiveDashboard/useMetricsSummary.ts b/frontend/src/component/executiveDashboard/useMetricsSummary.ts index 312f22d796..aa91ab77b5 100644 --- a/frontend/src/component/executiveDashboard/useMetricsSummary.ts +++ b/frontend/src/component/executiveDashboard/useMetricsSummary.ts @@ -1,57 +1,81 @@ import { useMemo } from 'react'; -import { getProjectColor } from './executive-dashboard-utils'; import { useTheme } from '@mui/material'; -import { - ExecutiveSummarySchema, - ExecutiveSummarySchemaMetricsSummaryTrendsItem, -} from '../../openapi'; +import { ExecutiveSummarySchema } from '../../openapi'; +import { parseISO, getISOWeek, format } from 'date-fns'; +import { getProjectColor } from './executive-dashboard-utils'; type MetricsSummaryTrends = ExecutiveSummarySchema['metricsSummaryTrends']; +interface GroupedData { + [key: string]: { + [week: string]: { + total: number; + totalYes: number; + totalNo: number; + totalApps: number; + totalEnvironments: number; + totalFlags: number; + }; + }; +} + +function groupAndSumData(data: MetricsSummaryTrends): any { + const groupedData: GroupedData = {}; + + data.forEach((item) => { + const weekNumber = getISOWeek(parseISO(item.date)); + const year = format(parseISO(item.date), 'yyyy'); + const weekId = `${year}-${weekNumber.toString().padStart(2, '0')}`; + const project = item.project; + + if (!groupedData[project]) { + groupedData[project] = {}; + } + + if (!groupedData[project][weekId]) { + groupedData[project][weekId] = { + total: 0, + totalYes: 0, + totalNo: 0, + totalApps: 0, + totalEnvironments: 0, + totalFlags: 0, + }; + } + + groupedData[project][weekId].total += item.totalYes + item.totalNo; + groupedData[project][weekId].totalYes += item.totalYes; + groupedData[project][weekId].totalNo += item.totalNo; + groupedData[project][weekId].totalApps += item.totalApps; + groupedData[project][weekId].totalEnvironments += + item.totalEnvironments; + groupedData[project][weekId].totalFlags += item.totalFlags; + }); + + return Object.entries(groupedData).map(([project, weeks]) => { + const color = getProjectColor(project); + return { + label: project, + borderColor: color, + backgroundColor: color, + fill: false, + data: Object.entries(weeks) + .sort(([weekA], [weekB]) => weekA.localeCompare(weekB)) + .map(([weekId, values]) => ({ + weekId, + ...values, + })), + }; + }); +} + 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, - }; + return { datasets: groupAndSumData(metricsSummaryTrends) }; }, [theme, metricsSummaryTrends]); return data;