diff --git a/src/views/ReportsCenter/ReportsCenterCMDashboard.tsx b/src/views/ReportsCenter/ReportsCenterCMDashboard.tsx index 62dcab1a59..1895aee7e2 100644 --- a/src/views/ReportsCenter/ReportsCenterCMDashboard.tsx +++ b/src/views/ReportsCenter/ReportsCenterCMDashboard.tsx @@ -39,10 +39,21 @@ interface ReportCount { non_consensus: number; } +interface ReportAlignmentCount { + date: string; + escalated: number; + unanimous: number; + non_unanimous: number; +} + interface CMVotingOutcomeData { [reportType: string]: ReportCount[]; } +interface CMGroupVotingAlignmentData { + [reportType: string]: ReportAlignmentCount[]; +} + interface IndividualCMVotingOutcomeData { user_id: number; vote_data: CMVotingOutcomeData; @@ -62,13 +73,14 @@ function startOfWeek(the_date: Date): Date { // TBD: it might be nice if this number was dynamically provided by the server, but // we are already possibly hitting it hard for these rollups -const EXPECTED_MAX_WEEKLY_CM_REPORTS = 160; -const Y_STEP_SIZE = 40; // must divide evenly into EXPECTED_MAX_WEEKLY_CM_REPORTS +const EXPECTED_MAX_WEEKLY_CM_REPORTS = 200; +const Y_STEP_SIZE = 40; // must divide nicely into EXPECTED_MAX_WEEKLY_CM_REPORTS interface CMVoteCountGraphProps { vote_data: ReportCount[]; period: number; } + const CMVoteCountGraph = ({ vote_data, period }: CMVoteCountGraphProps): JSX.Element => { if (!vote_data) { vote_data = []; @@ -283,10 +295,220 @@ const CMVoteCountGraph = ({ vote_data, period }: CMVoteCountGraphProps): JSX.Ele ); }; +interface CMVotingGroupGraphProps { + vote_data: ReportAlignmentCount[]; + period: number; +} + +const CMVotingGroupGraph = ({ vote_data, period }: CMVotingGroupGraphProps): JSX.Element => { + if (!vote_data) { + vote_data = []; + } + + const aggregateDataByWeek = React.useMemo(() => { + const aggregated: { + [key: string]: { + escalated: number; + unanimous: number; + non_unanimous: number; + total: number; + }; + } = {}; + + vote_data.forEach(({ date, escalated, unanimous, non_unanimous }) => { + const weekStart = startOfWeek(new Date(date)).toISOString().slice(0, 10); // Get week start and convert to ISO string for key + + if (!aggregated[weekStart]) { + aggregated[weekStart] = { escalated: 0, unanimous: 0, non_unanimous: 0, total: 0 }; + } + aggregated[weekStart].escalated += escalated; + aggregated[weekStart].unanimous += unanimous; + aggregated[weekStart].non_unanimous += non_unanimous; + aggregated[weekStart].total += unanimous + non_unanimous; + }); + + return Object.entries(aggregated).map(([date, counts]) => ({ + date, + ...counts, + })); + }, [vote_data]); + + const totals_data = React.useMemo(() => { + return [ + { + id: "unanimous", + data: dropCurrentPeriod( + aggregateDataByWeek.map((week) => ({ + x: week.date, + y: week.unanimous, + })), + ), + }, + { + id: "escalated", + data: dropCurrentPeriod( + aggregateDataByWeek.map((week) => ({ + x: week.date, + y: week.escalated, + })), + ), + }, + { + id: "non-unanimous", + data: dropCurrentPeriod( + aggregateDataByWeek.map((week) => ({ + x: week.date, + y: week.non_unanimous, + })), + ), + }, + ]; + }, [aggregateDataByWeek]); + + const percent_data = React.useMemo( + () => [ + { + id: "unanimous", + data: aggregateDataByWeek.map((week) => ({ + x: week.date, + y: week.unanimous / week.total, + })), + }, + { + id: "escalated", + data: aggregateDataByWeek.map((week) => ({ + x: week.date, + y: week.escalated / week.total, + })), + }, + { + id: "non-unanimous", + data: aggregateDataByWeek.map((week) => ({ + x: week.date, + y: week.non_unanimous / week.total, + })), + }, + ], + [aggregateDataByWeek], + ); + + const chart_theme = + data.get("theme") === "light" // (Accessible theme TBD - this assumes accessible is dark for now) + ? { + /* nivo defaults work well with our light theme */ + } + : { + text: { fill: "#FFFFFF" }, + tooltip: { container: { color: "#111111" } }, + grid: { line: { stroke: "#444444" } }, + }; + + const line_colors = { + unanimous: "rgba(0, 128, 0, 1)", // green + escalated: "rgba(255, 165, 0, 1)", // orange + "non-unanimous": "rgba(255, 0, 0, 1)", // red + }; + + if (!totals_data[0].data.length) { + return