Skip to content

Commit

Permalink
✨ Add missing risk chart for assessment review
Browse files Browse the repository at this point in the history
Signed-off-by: ibolton336 <[email protected]>
  • Loading branch information
ibolton336 committed Dec 6, 2023
1 parent 4a90493 commit 3a69a0b
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { ChartDonut, ChartLegend } from "@patternfly/react-charts";
import { RISK_LIST } from "@app/Constants";
import {
Assessment,
Section,
IdRef,
AssessmentWithArchetypeApplications,
} from "@app/api/models";

import { global_palette_blue_300 as defaultColor } from "@patternfly/react-tokens";
import { useFetchAssessmentsWithArchetypeApplications } from "@app/queries/assessments";

export interface ChartData {
red: number;
amber: number;
green: number;
unknown: number;
}

export const getChartDataFromCategories = (
categories: Section[]
): ChartData => {
let green = 0;
let amber = 0;
let red = 0;
let unknown = 0;

categories
.flatMap((f) => f.questions)
.flatMap((f) => f.answers)
.filter((f) => f.selected === true)
.forEach((f) => {
switch (f.risk) {
case "GREEN":
green++;
break;
case "yellow":
amber++;
break;
case "RED":
red++;
break;
default:
unknown++;
}
});

return {
red,
amber,
green,
unknown,
} as ChartData;
};

export const getChartDataFromMultipleAssessments = (
assessments: Assessment[]
): ChartData => {
let green = 0,
amber = 0,
red = 0,
unknown = 0;

assessments.forEach((assessment) => {
assessment.sections
.flatMap((section) => section.questions)
.flatMap((question) => question.answers)
.filter((answer) => answer.selected)
.forEach((answer) => {
switch (answer.risk) {
case "green":
green++;
break;
case "yellow":
amber++;
break;
case "red":
red++;
break;
default:
unknown++;
}
});
});

return { red, amber, green, unknown };
};

export interface IApplicationAssessmentDonutChartProps {
assessmentRefs?: IdRef[];
}

export const ApplicationAssessmentDonutChart: React.FC<
IApplicationAssessmentDonutChartProps
> = ({ assessmentRefs }) => {
const { t } = useTranslation();
const { assessmentsWithArchetypeApplications } =
useFetchAssessmentsWithArchetypeApplications();

const filterAssessmentsByRefs = (
assessments: AssessmentWithArchetypeApplications[],
refs: IdRef[]
) => {
if (refs && refs.length > 0) {
return assessments.filter((assessment) =>
refs.some((ref) => ref.id === assessment.id)
);
}
return assessments;
};

const filteredAssessments = filterAssessmentsByRefs(
assessmentsWithArchetypeApplications,
assessmentRefs || []
);

const charData: ChartData = useMemo(() => {
return getChartDataFromMultipleAssessments(filteredAssessments);
}, [filteredAssessments]);

const chartDefinition = [
{
x: t(RISK_LIST["green"].i18Key),
y: charData.green,
color: RISK_LIST["green"].hexColor,
},
{
x: t(RISK_LIST["yellow"].i18Key),
y: charData.amber,
color: RISK_LIST["yellow"].hexColor,
},
{
x: t(RISK_LIST["red"].i18Key),
y: charData.red,
color: RISK_LIST["red"].hexColor,
},
{
x: t(RISK_LIST["unknown"].i18Key),
y: charData.unknown,
color: RISK_LIST["unknown"].hexColor,
},
].filter((f) => f.y > 0);

return (
<div style={{ height: "250px", width: "380px" }}>
<ChartDonut
ariaDesc="risk-donut-chart"
constrainToVisibleArea={true}
data={chartDefinition.map((elem) => ({ x: elem.x, y: elem.y }))}
labels={({ datum }) => `${datum.x}: ${datum.y}`}
colorScale={chartDefinition.map(
(elem) => elem.color || defaultColor.value
)}
legendComponent={
<ChartLegend
data={chartDefinition.map((elem) => ({
name: `${elem.x}: ${elem.y}`,
}))}
colorScale={chartDefinition.map(
(elem) => elem.color || defaultColor.value
)}
/>
}
legendOrientation="vertical"
legendPosition="right"
padding={{
bottom: 20,
left: 20,
right: 140,
top: 20,
}}
innerRadius={50}
width={380}
/>
</div>
);
};
15 changes: 13 additions & 2 deletions client/src/app/pages/review/review-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { useFetchApplicationById } from "@app/queries/applications";
import { useFetchArchetypeById } from "@app/queries/archetypes";
import { IdentifiedRisksTable } from "../reports/components/identified-risks-table";
import { AssessmentLandscape } from "../reports/components/assessment-landscape";
import { ApplicationAssessmentDonutChart } from "../../components/application-assessment-donut-chart/application-assessment-donut-chart";

const ReviewPage: React.FC = () => {
const { t } = useTranslation();
Expand Down Expand Up @@ -112,12 +113,22 @@ const ReviewPage: React.FC = () => {
</FormSection>
</div>
</GridItem>
{application?.assessments?.length ||
archetype?.assessments?.length ? (
<GridItem md={6}>
<ApplicationAssessmentDonutChart
assessmentRefs={
application?.assessments || archetype?.assessments
}
/>
</GridItem>
) : null}
</Grid>
</ConditionalRender>
</CardBody>
</Card>
</PageSection>
{(application?.assessments?.length || archetype?.assessments?.length) && (
{application?.assessments?.length || archetype?.assessments?.length ? (
<PageSection>
<Card>
<CardHeader>
Expand All @@ -140,7 +151,7 @@ const ReviewPage: React.FC = () => {
</CardBody>
</Card>
</PageSection>
)}
) : null}
</>
);
};
Expand Down

0 comments on commit 3a69a0b

Please sign in to comment.