Update risk score color
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -16,7 +16,6 @@ import {
|
|||||||
useTransition,
|
useTransition,
|
||||||
} from "react";
|
} from "react";
|
||||||
import type { ListRiskViewQuery } from "./__generated__/ListRiskViewQuery.graphql";
|
import type { ListRiskViewQuery } from "./__generated__/ListRiskViewQuery.graphql";
|
||||||
import type { RiskTreatment } from "./__generated__/ListRiskView_risks.graphql";
|
|
||||||
import { useParams, useSearchParams } from "react-router";
|
import { useParams, useSearchParams } from "react-router";
|
||||||
import { PageTemplate } from "@/components/PageTemplate";
|
import { PageTemplate } from "@/components/PageTemplate";
|
||||||
import { RiskViewSkeleton } from "./ListRiskPage";
|
import { RiskViewSkeleton } from "./ListRiskPage";
|
||||||
@@ -136,12 +135,23 @@ const deleteRiskMutation = graphql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Helper function to convert risk score to severity label
|
// Helper function to convert risk score to risk level
|
||||||
const riskScoreToSeverity = (score: number): string => {
|
const calculateRiskLevel = (score: number): string => {
|
||||||
if (score >= 20) return "Catastrophic";
|
if (score >= 15) return "High";
|
||||||
if (score >= 12) return "Critical";
|
if (score >= 8) return "Medium";
|
||||||
if (score >= 5) return "Marginal";
|
return "Low";
|
||||||
return "Negligible";
|
};
|
||||||
|
|
||||||
|
// 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
|
// Helper function to format treatment value
|
||||||
@@ -224,30 +234,6 @@ const emptyRiskMatrixColors = {
|
|||||||
high: "bg-red-50 text-black",
|
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
|
// Risk Matrix Component
|
||||||
function RiskMatrix({
|
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)
|
// This follows the 5x5 grid with rows representing impact (5 to 1) and columns representing likelihood (1 to 5)
|
||||||
const colorMatrix = [
|
const colorMatrix = [
|
||||||
// Impact 5 (Catastrophic) - first row
|
// Impact 5 (Catastrophic) - first row
|
||||||
[
|
[colorSet.low, colorSet.medium, colorSet.high, colorSet.high, colorSet.high],
|
||||||
colorSet.medium,
|
|
||||||
colorSet.medium,
|
|
||||||
colorSet.high,
|
|
||||||
colorSet.high,
|
|
||||||
colorSet.high,
|
|
||||||
],
|
|
||||||
// Impact 4 (Significant) - second row
|
// 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
|
// 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
|
// 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
|
// 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];
|
return colorMatrix[row][col];
|
||||||
@@ -409,32 +371,15 @@ function RiskMatrix({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-sm pt-1 border-t border-gray-200 mt-1">
|
<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">
|
<span className="font-bold ml-1">
|
||||||
{impactValue * likelihoodValue}
|
{calculateRiskLevel(impactValue * likelihoodValue)} ({impactValue * likelihoodValue})
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className="ml-2 px-2 py-0.5 text-xs rounded-full font-medium inline-block"
|
className="ml-2 px-2 py-0.5 text-xs rounded-full font-medium inline-block"
|
||||||
style={{
|
style={getRiskLevelColor(calculateRiskLevel(impactValue * likelihoodValue))}
|
||||||
backgroundColor:
|
|
||||||
impactValue * likelihoodValue >= 20
|
|
||||||
? "#ef4444"
|
|
||||||
: impactValue * likelihoodValue >= 12
|
|
||||||
? "#f59e0b"
|
|
||||||
: impactValue * likelihoodValue >= 5
|
|
||||||
? "#10b981"
|
|
||||||
: "#94a3b8",
|
|
||||||
color:
|
|
||||||
impactValue * likelihoodValue >= 12 ? "white" : "inherit",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{impactValue * likelihoodValue >= 20
|
{calculateRiskLevel(impactValue * likelihoodValue)}
|
||||||
? "Catastrophic"
|
|
||||||
: impactValue * likelihoodValue >= 12
|
|
||||||
? "Critical"
|
|
||||||
: impactValue * likelihoodValue >= 5
|
|
||||||
? "Marginal"
|
|
||||||
: "Negligible"}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -449,26 +394,14 @@ function RiskMatrix({
|
|||||||
className="text-sm border-l-2 pl-2"
|
className="text-sm border-l-2 pl-2"
|
||||||
style={{
|
style={{
|
||||||
borderColor:
|
borderColor:
|
||||||
(isResidual
|
getRiskLevelColor(
|
||||||
? (risk.residualImpact || risk.inherentImpact) *
|
calculateRiskLevel(
|
||||||
(risk.residualLikelihood || risk.inherentLikelihood)
|
isResidual
|
||||||
: risk.inherentImpact * risk.inherentLikelihood) >= 20
|
|
||||||
? "#ef4444"
|
|
||||||
: (isResidual
|
|
||||||
? (risk.residualImpact || risk.inherentImpact) *
|
? (risk.residualImpact || risk.inherentImpact) *
|
||||||
(risk.residualLikelihood ||
|
(risk.residualLikelihood || risk.inherentLikelihood)
|
||||||
risk.inherentLikelihood)
|
: risk.inherentImpact * risk.inherentLikelihood
|
||||||
: risk.inherentImpact *
|
)
|
||||||
risk.inherentLikelihood) >= 12
|
).backgroundColor
|
||||||
? "#f59e0b"
|
|
||||||
: (isResidual
|
|
||||||
? (risk.residualImpact || risk.inherentImpact) *
|
|
||||||
(risk.residualLikelihood ||
|
|
||||||
risk.inherentLikelihood)
|
|
||||||
: risk.inherentImpact *
|
|
||||||
risk.inherentLikelihood) >= 5
|
|
||||||
? "#10b981"
|
|
||||||
: "#94a3b8",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
@@ -773,13 +706,13 @@ function ListRiskViewContent({
|
|||||||
Name
|
Name
|
||||||
</th>
|
</th>
|
||||||
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
|
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
|
||||||
Inherent
|
Inherent Risk Score
|
||||||
</th>
|
</th>
|
||||||
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
|
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
|
||||||
Treatment
|
Treatment
|
||||||
</th>
|
</th>
|
||||||
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
|
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
|
||||||
Residual
|
Residual Risk Score
|
||||||
</th>
|
</th>
|
||||||
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
|
<th className="h-12 px-4 text-left align-middle font-medium text-tertiary w-1/6">
|
||||||
Owner
|
Owner
|
||||||
@@ -828,25 +761,9 @@ function ListRiskViewContent({
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="px-2 py-0.5 text-xs rounded-full font-medium inline-block"
|
className="px-2 py-0.5 text-xs rounded-full font-medium inline-block"
|
||||||
style={{
|
style={getRiskLevelColor(calculateRiskLevel(risk.inherentLikelihood * risk.inherentImpact))}
|
||||||
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",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{riskScoreToSeverity(
|
{calculateRiskLevel(risk.inherentLikelihood * risk.inherentImpact)} ({risk.inherentLikelihood * risk.inherentImpact})
|
||||||
risk.inherentLikelihood * risk.inherentImpact
|
|
||||||
)}{" "}
|
|
||||||
({risk.inherentLikelihood * risk.inherentImpact})
|
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
@@ -866,25 +783,9 @@ function ListRiskViewContent({
|
|||||||
{risk.residualLikelihood && risk.residualImpact ? (
|
{risk.residualLikelihood && risk.residualImpact ? (
|
||||||
<span
|
<span
|
||||||
className="px-2 py-0.5 text-xs rounded-full font-medium inline-block"
|
className="px-2 py-0.5 text-xs rounded-full font-medium inline-block"
|
||||||
style={{
|
style={getRiskLevelColor(calculateRiskLevel(risk.residualLikelihood * risk.residualImpact))}
|
||||||
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",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{riskScoreToSeverity(
|
{calculateRiskLevel(risk.residualLikelihood * risk.residualImpact)} ({risk.residualLikelihood * risk.residualImpact})
|
||||||
risk.residualLikelihood * risk.residualImpact
|
|
||||||
)}{" "}
|
|
||||||
({risk.residualLikelihood * risk.residualImpact})
|
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
"Not set"
|
"Not set"
|
||||||
|
|||||||
@@ -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) {
|
function getRiskSeverity(likelihood: number, impact: number) {
|
||||||
const score = likelihood * impact;
|
const score = likelihood * impact;
|
||||||
if (score >= 20)
|
const level = calculateRiskLevel(score);
|
||||||
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",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return {
|
return {
|
||||||
level: "Negligible",
|
level: level,
|
||||||
score: score,
|
score: score,
|
||||||
style: {
|
style: getRiskLevelColor(level),
|
||||||
backgroundColor: "#94a3b8",
|
|
||||||
color: "inherit",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add helper functions to convert numerical values to labels
|
// Keep the getLikelihoodLabel and getImpactLabel functions as they are
|
||||||
function getLikelihoodLabel(likelihood: number): {
|
function getLikelihoodLabel(likelihood: number): {
|
||||||
label: string;
|
label: string;
|
||||||
style: React.CSSProperties;
|
style: React.CSSProperties;
|
||||||
@@ -254,26 +245,26 @@ function getLikelihoodLabel(likelihood: number): {
|
|||||||
if (likelihood === 5)
|
if (likelihood === 5)
|
||||||
return {
|
return {
|
||||||
label: "Frequent (5)",
|
label: "Frequent (5)",
|
||||||
style: { backgroundColor: "#ef4444", color: "white" },
|
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
|
||||||
};
|
};
|
||||||
if (likelihood === 4)
|
if (likelihood === 4)
|
||||||
return {
|
return {
|
||||||
label: "Probable (4)",
|
label: "Probable (4)",
|
||||||
style: { backgroundColor: "#f59e0b", color: "white" },
|
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
|
||||||
};
|
};
|
||||||
if (likelihood === 3)
|
if (likelihood === 3)
|
||||||
return {
|
return {
|
||||||
label: "Occasional (3)",
|
label: "Occasional (3)",
|
||||||
style: { backgroundColor: "#eab308", color: "black" },
|
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
|
||||||
};
|
};
|
||||||
if (likelihood === 2)
|
if (likelihood === 2)
|
||||||
return {
|
return {
|
||||||
label: "Remote (2)",
|
label: "Remote (2)",
|
||||||
style: { backgroundColor: "#10b981", color: "black" },
|
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
label: "Improbable (1)",
|
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)
|
if (impact === 5)
|
||||||
return {
|
return {
|
||||||
label: "Catastrophic (5)",
|
label: "Catastrophic (5)",
|
||||||
style: { backgroundColor: "#ef4444", color: "white" },
|
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
|
||||||
};
|
};
|
||||||
if (impact === 4)
|
if (impact === 4)
|
||||||
return {
|
return {
|
||||||
label: "Significant (4)",
|
label: "Significant (4)",
|
||||||
style: { backgroundColor: "#f59e0b", color: "white" },
|
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
|
||||||
};
|
};
|
||||||
if (impact === 3)
|
if (impact === 3)
|
||||||
return {
|
return {
|
||||||
label: "Moderate (3)",
|
label: "Moderate (3)",
|
||||||
style: { backgroundColor: "#eab308", color: "black" },
|
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
|
||||||
};
|
};
|
||||||
if (impact === 2)
|
if (impact === 2)
|
||||||
return {
|
return {
|
||||||
label: "Low (2)",
|
label: "Low (2)",
|
||||||
style: { backgroundColor: "#10b981", color: "black" },
|
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
label: "Negligible (1)",
|
label: "Negligible (1)",
|
||||||
style: { backgroundColor: "#94a3b8", color: "black" },
|
style: { backgroundColor: "#fff", color: "black", border: "1px solid #e2e8f0" },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user