Update risk score color

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2025-05-02 12:15:06 -07:00
parent 193a4855ec
commit 244b01c0fc
2 changed files with 73 additions and 181 deletions

View File

@@ -16,7 +16,6 @@ import {
useTransition,
} from "react";
import type { ListRiskViewQuery } from "./__generated__/ListRiskViewQuery.graphql";
import type { RiskTreatment } from "./__generated__/ListRiskView_risks.graphql";
import { useParams, useSearchParams } from "react-router";
import { PageTemplate } from "@/components/PageTemplate";
import { RiskViewSkeleton } from "./ListRiskPage";
@@ -136,12 +135,23 @@ const deleteRiskMutation = graphql`
}
`;
// Helper function to convert risk score to severity label
const riskScoreToSeverity = (score: number): string => {
if (score >= 20) return "Catastrophic";
if (score >= 12) return "Critical";
if (score >= 5) return "Marginal";
return "Negligible";
// Helper function to convert risk score to risk level
const calculateRiskLevel = (score: number): string => {
if (score >= 15) return "High";
if (score >= 8) return "Medium";
return "Low";
};
// Helper function to get color for risk level that matches the matrix colors
const getRiskLevelColor = (level: string): {backgroundColor: string, color: string} => {
switch(level) {
case "High":
return {backgroundColor: "#ef4444", color: "#ffffff"}; // red-500
case "Medium":
return {backgroundColor: "#fcd34d", color: "#000000"}; // yellow-300
default:
return {backgroundColor: "#22c55e", color: "#ffffff"}; // green-500
}
};
// Helper function to format treatment value
@@ -224,30 +234,6 @@ const emptyRiskMatrixColors = {
high: "bg-red-50 text-black",
};
type RiskNode = {
readonly id: string;
readonly name: string;
readonly inherentLikelihood: number;
readonly inherentImpact: number;
readonly residualLikelihood: number;
readonly residualImpact: number;
readonly treatment: RiskTreatment;
readonly description: string;
readonly category: string;
readonly createdAt: string;
readonly updatedAt: string;
readonly owner: {
readonly id: string;
readonly fullName: string;
} | null;
readonly measures: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly category: string;
};
}>;
};
};
// Risk Matrix Component
function RiskMatrix({
@@ -312,39 +298,15 @@ function RiskMatrix({
// This follows the 5x5 grid with rows representing impact (5 to 1) and columns representing likelihood (1 to 5)
const colorMatrix = [
// Impact 5 (Catastrophic) - first row
[
colorSet.medium,
colorSet.medium,
colorSet.high,
colorSet.high,
colorSet.high,
],
[colorSet.low, colorSet.medium, colorSet.high, colorSet.high, colorSet.high],
// Impact 4 (Significant) - second row
[
colorSet.low,
colorSet.medium,
colorSet.medium,
colorSet.high,
colorSet.high,
],
[colorSet.low, colorSet.medium, colorSet.medium, colorSet.high, colorSet.high],
// Impact 3 (Moderate) - third row
[
colorSet.low,
colorSet.low,
colorSet.medium,
colorSet.medium,
colorSet.high,
],
[colorSet.low, colorSet.low, colorSet.medium, colorSet.medium, colorSet.high],
// Impact 2 (Low) - fourth row
[
colorSet.low,
colorSet.low,
colorSet.low,
colorSet.medium,
colorSet.medium,
],
[colorSet.low, colorSet.low, colorSet.low, colorSet.medium, colorSet.medium],
// Impact 1 (Negligible) - fifth row
[colorSet.low, colorSet.low, colorSet.low, colorSet.low, colorSet.medium],
[colorSet.low, colorSet.low, colorSet.low, colorSet.low, colorSet.low],
];
return colorMatrix[row][col];
@@ -409,32 +371,15 @@ function RiskMatrix({
</div>
<div className="text-sm pt-1 border-t border-gray-200 mt-1">
<span className="text-muted-foreground">Risk Score:</span>
<span className="text-muted-foreground">Risk Level:</span>
<span className="font-bold ml-1">
{impactValue * likelihoodValue}
{calculateRiskLevel(impactValue * likelihoodValue)} ({impactValue * likelihoodValue})
</span>
<span
className="ml-2 px-2 py-0.5 text-xs rounded-full font-medium inline-block"
style={{
backgroundColor:
impactValue * likelihoodValue >= 20
? "#ef4444"
: impactValue * likelihoodValue >= 12
? "#f59e0b"
: impactValue * likelihoodValue >= 5
? "#10b981"
: "#94a3b8",
color:
impactValue * likelihoodValue >= 12 ? "white" : "inherit",
}}
style={getRiskLevelColor(calculateRiskLevel(impactValue * likelihoodValue))}
>
{impactValue * likelihoodValue >= 20
? "Catastrophic"
: impactValue * likelihoodValue >= 12
? "Critical"
: impactValue * likelihoodValue >= 5
? "Marginal"
: "Negligible"}
{calculateRiskLevel(impactValue * likelihoodValue)}
</span>
</div>
</div>
@@ -449,26 +394,14 @@ function RiskMatrix({
className="text-sm border-l-2 pl-2"
style={{
borderColor:
(isResidual
? (risk.residualImpact || risk.inherentImpact) *
(risk.residualLikelihood || risk.inherentLikelihood)
: risk.inherentImpact * risk.inherentLikelihood) >= 20
? "#ef4444"
: (isResidual
getRiskLevelColor(
calculateRiskLevel(
isResidual
? (risk.residualImpact || risk.inherentImpact) *
(risk.residualLikelihood ||
risk.inherentLikelihood)
: risk.inherentImpact *
risk.inherentLikelihood) >= 12
? "#f59e0b"
: (isResidual
? (risk.residualImpact || risk.inherentImpact) *
(risk.residualLikelihood ||
risk.inherentLikelihood)
: risk.inherentImpact *
risk.inherentLikelihood) >= 5
? "#10b981"
: "#94a3b8",
(risk.residualLikelihood || risk.inherentLikelihood)
: risk.inherentImpact * risk.inherentLikelihood
)
).backgroundColor
}}
>
<Link
@@ -773,13 +706,13 @@ function ListRiskViewContent({
Name
</th>
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
Inherent
Inherent Risk Score
</th>
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
Treatment
</th>
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
Residual
Residual Risk Score
</th>
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
Owner
@@ -828,25 +761,9 @@ function ListRiskViewContent({
>
<span
className="px-2 py-0.5 text-xs rounded-full font-medium inline-block"
style={{
backgroundColor:
risk.inherentLikelihood * risk.inherentImpact >= 20
? "#ef4444"
: risk.inherentLikelihood * risk.inherentImpact >= 12
? "#f59e0b"
: risk.inherentLikelihood * risk.inherentImpact >= 5
? "#10b981"
: "#94a3b8",
color:
risk.inherentLikelihood * risk.inherentImpact >= 12
? "white"
: "inherit",
}}
style={getRiskLevelColor(calculateRiskLevel(risk.inherentLikelihood * risk.inherentImpact))}
>
{riskScoreToSeverity(
risk.inherentLikelihood * risk.inherentImpact
)}{" "}
({risk.inherentLikelihood * risk.inherentImpact})
{calculateRiskLevel(risk.inherentLikelihood * risk.inherentImpact)} ({risk.inherentLikelihood * risk.inherentImpact})
</span>
</Link>
</td>
@@ -866,25 +783,9 @@ function ListRiskViewContent({
{risk.residualLikelihood && risk.residualImpact ? (
<span
className="px-2 py-0.5 text-xs rounded-full font-medium inline-block"
style={{
backgroundColor:
risk.residualLikelihood * risk.residualImpact >= 20
? "#ef4444"
: risk.residualLikelihood * risk.residualImpact >= 12
? "#f59e0b"
: risk.residualLikelihood * risk.residualImpact >= 5
? "#10b981"
: "#94a3b8",
color:
risk.residualLikelihood * risk.residualImpact >= 12
? "white"
: "inherit",
}}
style={getRiskLevelColor(calculateRiskLevel(risk.residualLikelihood * risk.residualImpact))}
>
{riskScoreToSeverity(
risk.residualLikelihood * risk.residualImpact
)}{" "}
({risk.residualLikelihood * risk.residualImpact})
{calculateRiskLevel(risk.residualLikelihood * risk.residualImpact)} ({risk.residualLikelihood * risk.residualImpact})
</span>
) : (
"Not set"

View File

@@ -207,46 +207,37 @@ const deleteRiskPolicyMappingMutation = graphql`
}
`;
// Replace the current getRiskSeverity function with the functions from ListRiskView.tsx
// Helper function to convert risk score to risk level
const calculateRiskLevel = (score: number): string => {
if (score >= 15) return "High";
if (score >= 8) return "Medium";
return "Low";
};
// Helper function to get color for risk level that matches the matrix colors
const getRiskLevelColor = (level: string): {backgroundColor: string, color: string} => {
switch(level) {
case "High":
return {backgroundColor: "#ef4444", color: "#ffffff"}; // red-500
case "Medium":
return {backgroundColor: "#fcd34d", color: "#000000"}; // yellow-300
default:
return {backgroundColor: "#22c55e", color: "#ffffff"}; // green-500
}
};
function getRiskSeverity(likelihood: number, impact: number) {
const score = likelihood * impact;
if (score >= 20)
return {
level: "Catastrophic",
score: score,
style: {
backgroundColor: "#ef4444",
color: "white",
},
};
if (score >= 12)
return {
level: "Critical",
score: score,
style: {
backgroundColor: "#f59e0b",
color: "white",
},
};
if (score >= 5)
return {
level: "Marginal",
score: score,
style: {
backgroundColor: "#10b981",
color: "inherit",
},
};
const level = calculateRiskLevel(score);
return {
level: "Negligible",
level: level,
score: score,
style: {
backgroundColor: "#94a3b8",
color: "inherit",
},
style: getRiskLevelColor(level),
};
}
// Add helper functions to convert numerical values to labels
// Keep the getLikelihoodLabel and getImpactLabel functions as they are
function getLikelihoodLabel(likelihood: number): {
label: string;
style: React.CSSProperties;
@@ -254,26 +245,26 @@ function getLikelihoodLabel(likelihood: number): {
if (likelihood === 5)
return {
label: "Frequent (5)",
style: { backgroundColor: "#ef4444", color: "white" },
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
};
if (likelihood === 4)
return {
label: "Probable (4)",
style: { backgroundColor: "#f59e0b", color: "white" },
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
};
if (likelihood === 3)
return {
label: "Occasional (3)",
style: { backgroundColor: "#eab308", color: "black" },
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
};
if (likelihood === 2)
return {
label: "Remote (2)",
style: { backgroundColor: "#10b981", color: "black" },
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
};
return {
label: "Improbable (1)",
style: { backgroundColor: "#94a3b8", color: "black" },
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
};
}
@@ -284,26 +275,26 @@ function getImpactLabel(impact: number): {
if (impact === 5)
return {
label: "Catastrophic (5)",
style: { backgroundColor: "#ef4444", color: "white" },
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
};
if (impact === 4)
return {
label: "Significant (4)",
style: { backgroundColor: "#f59e0b", color: "white" },
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
};
if (impact === 3)
return {
label: "Moderate (3)",
style: { backgroundColor: "#eab308", color: "black" },
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
};
if (impact === 2)
return {
label: "Low (2)",
style: { backgroundColor: "#10b981", color: "black" },
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
};
return {
label: "Negligible (1)",
style: { backgroundColor: "#94a3b8", color: "black" },
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
};
}