Repository: kartverket/backstage-plugin-risk-scorecard-frontend
Pull request #541: 🔢  change calculations to use base number 20
Author: login: ingelinsu
Created: 2025-04-15T07:41:26Z | Merged: 2025-05-15T10:01:01Z

Change RiSc calculations to use 20 as a base number with the formula 20^(S+K-1) where S is probability and K is consequence, see below: 
<img width="675" alt="Screenshot 2025-04-15 at 09 41 45" src="https://github.com/user-attachments/assets/8b9cb40c-2ff2-4110-8863-115bc2303a5b" />

It is possible to change the values locally to anything as there is a mappign to the nearest value in frontend.

## Before 
<img width="532" alt="Screenshot 2025-04-15 at 11 33 22" src="https://github.com/user-attachments/assets/fa5fa164-23c3-4159-aab5-458f93a0a0a4" />
<img width="817" alt="Screenshot 2025-04-15 at 11 33 49" src="https://github.com/user-attachments/assets/e4b1f393-b134-4d34-9bcf-35d49ab9a251" />

## After
<img width="584" alt="Screenshot 2025-05-02 at 14 47 00" src="https://github.com/user-attachments/assets/6c6f3d06-6792-4f8d-94d6-c13a12c4b36f" />
<img width="845" alt="Screenshot 2025-05-02 at 14 47 12" src="https://github.com/user-attachments/assets/e533b98c-1604-4967-ad72-f64e57638972" />
<img width="806" alt="Screenshot 2025-05-02 at 10 11 30" src="https://github.com/user-attachments/assets/81f9e269-0973-4208-9f48-b44919f98da4" />



Diff:
diff --git a/plugins/ros/src/components/common/Select.tsx b/plugins/ros/src/components/common/Select.tsx
index 4bb32c759..3dbcb54a7 100644
--- a/plugins/ros/src/components/common/Select.tsx
+++ b/plugins/ros/src/components/common/Select.tsx
@@ -24,7 +24,10 @@ type Props<T extends FieldValues> = SelectProps & {
   control?: Control<T, any>;
   name: Path<T>;
   labelTranslationKey?: string;
-  options: { value: string | number; renderedValue: string | number }[];
+  options: {
+    value: string | number;
+    renderedValue: React.ReactNode | string | number;
+  }[];
 };
 
 export function Select<T extends FieldValues>({
diff --git a/plugins/ros/src/components/riskMatrix/AggregatedCost.tsx b/plugins/ros/src/components/riskMatrix/AggregatedCost.tsx
index 6a3410346..8a5c0bf33 100644
--- a/plugins/ros/src/components/riskMatrix/AggregatedCost.tsx
+++ b/plugins/ros/src/components/riskMatrix/AggregatedCost.tsx
@@ -1,12 +1,12 @@
-import { useState } from 'react';
+import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import { Box, IconButton, Typography } from '@material-ui/core';
 import InfoIcon from '@mui/icons-material/Info';
+import { useState } from 'react';
+import { useFontStyles } from '../../utils/style';
+import { pluginRiScTranslationRef } from '../../utils/translations';
 import { RiSc } from '../../utils/types';
-import { EstimatedRiskInfoDialog } from './EstimatedRiskInfoDialog';
 import { formatNumber } from '../../utils/utilityfunctions';
-import { pluginRiScTranslationRef } from '../../utils/translations';
-import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
-import { useFontStyles } from '../../utils/style';
+import { EstimatedRiskInfoDialog } from './EstimatedRiskInfoDialog';
 import { useAggregatedCostStyles } from './aggregatedCostStyle';
 
 interface AggregatedCostProps {
@@ -22,12 +22,12 @@ export function AggregatedCost({ riSc, initialRisk }: AggregatedCostProps) {
   const [showDialog, setShowDialog] = useState(false);
 
   const cost = riSc.scenarios
-    .map(scenario =>
-      initialRisk
+    .map(scenario => {
+      return initialRisk
         ? scenario.risk.probability * scenario.risk.consequence
         : scenario.remainingRisk.probability *
-          scenario.remainingRisk.consequence,
-    )
+            scenario.remainingRisk.consequence;
+    })
     .reduce((a, b) => a + b, 0);
 
   return (
diff --git a/plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx b/plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
index e70f694db..a26674dc1 100644
--- a/plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
+++ b/plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
@@ -1,4 +1,4 @@
-import { Fragment } from 'react';
+import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import {
   Dialog,
   DialogContent,
@@ -7,10 +7,14 @@ import {
   Paper,
   Typography,
 } from '@material-ui/core';
-import { pluginRiScTranslationRef } from '../../utils/translations';
-import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
+import { Fragment } from 'react';
 import { consequenceOptions, probabilityOptions } from '../../utils/constants';
-import { formatNOK } from '../../utils/utilityfunctions';
+import { pluginRiScTranslationRef } from '../../utils/translations';
+import {
+  consequenceIndexToTranslationKeys,
+  formatNOK,
+  probabilityIndexToTranslationKeys,
+} from '../../utils/utilityfunctions';
 import { useEstimatedRiskInfoDialogStyles } from './estimatedRiskInfoDialogStyle';
 
 interface EstimatedRiskInfoDialogProps {
@@ -38,14 +42,18 @@ export function EstimatedRiskInfoDialog({
           </Typography>
           <DialogContentText className={text}>
             {t('infoDialog.calculatedHow')}
+            <sup>{t('infoDialog.calculatedHowExponent')}</sup>{' '}
+            {t('riskMatrix.estimatedRisk.unit.nokPerYear')}.
           </DialogContentText>
           <Typography style={{ fontWeight: 'bold' }}>
             {t('infoDialog.consequenceTitle')}
           </Typography>
           <DialogContentText className={text}>
             {consequenceOptions.map((option, index) => (
-              <Fragment key={option}>
-                {index + 1}: {formatNOK(option)}
+              <Fragment key={index}>
+                <b>{index + 1}</b>: 20<sup>{index + 3}</sup> ={' '}
+                {formatNOK(option)} {t('infoDialog.consequenceUnit')} ={' '}
+                <b>{t(consequenceIndexToTranslationKeys[index] as any, {})}</b>
                 <br />
               </Fragment>
             ))}
@@ -55,16 +63,23 @@ export function EstimatedRiskInfoDialog({
           </Typography>
           <DialogContentText className={text}>
             {probabilityOptions.map((option, index) => (
-              <Fragment key={option}>
-                {index + 1}: {option}, {/* @ts-ignore */}
-                {t(`infoDialog.probabilityDescription.${index}`)}
+              <Fragment key={index}>
+                <b>{index + 1}</b>: 20<sup>{index - 2}</sup> = {option}{' '}
+                {t('infoDialog.probabilityUnit')} ={' '}
+                <b>{t(probabilityIndexToTranslationKeys[index] as any, {})}</b>
                 <br />
               </Fragment>
             ))}
           </DialogContentText>
 
           <DialogContentText className={text}>
-            {t('infoDialog.example')}
+            {t('infoDialog.example.part1')}
+            <b>2</b> (20<sup>4</sup> = 160,000 {t('infoDialog.consequenceUnit')}
+            ){t('infoDialog.example.part2')}
+            <b>4</b> (20<sup>1</sup> = 20 {t('infoDialog.probabilityUnit')})
+            {t('infoDialog.example.part3')}
+            20<sup>1+4-1</sup> = 160,000{' '}
+            {t('riskMatrix.estimatedRisk.unit.nokPerYear')}.
           </DialogContentText>
         </DialogContent>
       </Paper>
diff --git a/plugins/ros/src/components/riskMatrix/RiskMatrixScenarioCount.tsx b/plugins/ros/src/components/riskMatrix/RiskMatrixScenarioCount.tsx
index 74e064974..a4cac8535 100644
--- a/plugins/ros/src/components/riskMatrix/RiskMatrixScenarioCount.tsx
+++ b/plugins/ros/src/components/riskMatrix/RiskMatrixScenarioCount.tsx
@@ -10,12 +10,15 @@ import {
 } from '@material-ui/core';
 import { useState } from 'react';
 import CircleIcon from '@material-ui/icons/FiberManualRecord';
-import { consequenceOptions, probabilityOptions } from '../../utils/constants';
 import { RiSc } from '../../utils/types';
 import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import { pluginRiScTranslationRef } from '../../utils/translations';
 import { useRiskMatrixStyles } from './riskMatrixStyle';
 import { useScenario } from '../../contexts/ScenarioContext';
+import {
+  findConsequenceIndex,
+  findProbabilityIndex,
+} from '../../utils/utilityfunctions';
 
 interface ScenarioCountProps {
   riSc: RiSc;
@@ -51,12 +54,12 @@ export function RiskMatrixScenarioCount({
 
   const scenarios = riSc.scenarios.filter(
     scenario =>
-      probabilityOptions.indexOf(
+      findProbabilityIndex(
         initialRisk
           ? scenario.risk.probability
           : scenario.remainingRisk.probability,
       ) === probability &&
-      consequenceOptions.indexOf(
+      findConsequenceIndex(
         initialRisk
           ? scenario.risk.consequence
           : scenario.remainingRisk.consequence,
diff --git a/plugins/ros/src/components/scenarioDrawer/components/RiskFormSection.tsx b/plugins/ros/src/components/scenarioDrawer/components/RiskFormSection.tsx
index e310540c6..b4a0375df 100644
--- a/plugins/ros/src/components/scenarioDrawer/components/RiskFormSection.tsx
+++ b/plugins/ros/src/components/scenarioDrawer/components/RiskFormSection.tsx
@@ -1,15 +1,23 @@
-import { UseFormReturn } from 'react-hook-form';
-import { FormScenario } from '../../../utils/types';
-import { pluginRiScTranslationRef } from '../../../utils/translations';
 import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
+import InfoIcon from '@mui/icons-material/Info';
+import Box from '@mui/material/Box';
+import IconButton from '@mui/material/IconButton';
+import Paper from '@mui/material/Paper';
+import Typography from '@mui/material/Typography';
+import { UseFormReturn } from 'react-hook-form';
 import {
   consequenceOptions,
   probabilityOptions,
 } from '../../../utils/constants';
+import { pluginRiScTranslationRef } from '../../../utils/translations';
+import { FormScenario } from '../../../utils/types';
+import {
+  consequenceIndexToTranslationKeys,
+  findConsequenceIndex,
+  findProbabilityIndex,
+  probabilityIndexToTranslationKeys,
+} from '../../../utils/utilityfunctions';
 import { Select } from '../../common/Select';
-import Box from '@mui/material/Box';
-import Paper from '@mui/material/Paper';
-import Typography from '@mui/material/Typography';
 import { heading3 } from '../../common/typography';
 import {
   headerSection,
@@ -17,8 +25,7 @@ import {
   section,
   selectSection,
 } from '../scenarioDrawerComponents';
-import IconButton from '@mui/material/IconButton';
-import InfoIcon from '@mui/icons-material/Info';
+import RiskOptionDisplay from './RiskOptionDisplay';
 
 function ScenarioForm({
   formMethods,
@@ -28,19 +35,59 @@ function ScenarioForm({
   setIsMatrixDialogOpen: React.Dispatch<React.SetStateAction<boolean>>;
 }) {
   const { t } = useTranslationRef(pluginRiScTranslationRef);
-  const { control } = formMethods;
+  const { watch, control } = formMethods;
+
+  const [
+    riskProbability,
+    riskConsequence,
+    remainingRiskProbability,
+    remainingRiskConsequence,
+  ] = watch([
+    'risk.probability',
+    'risk.consequence',
+    'remainingRisk.probability',
+    'remainingRisk.consequence',
+  ]);
 
-  const probabilityValues = probabilityOptions.map((value, index) => ({
-    value: `${value}`,
-    /* @ts-ignore Because ts can't typecheck strings against our keys */
-    renderedValue: `${index + 1}: ${t(`probabilityTable.rows.${index + 1}`)}`,
-  }));
+  const probabilityValues = (selectedValue: number) => {
+    return probabilityOptions.map((value, index) => ({
+      value: `${value}`,
+      renderedValue: (
+        <RiskOptionDisplay
+          isSelected={
+            findProbabilityIndex(value) === findProbabilityIndex(selectedValue)
+          }
+          level={index + 1}
+          label={
+            /* @ts-ignore Because ts can't typecheck strings against our keys */
+            `${t(`probabilityTable.rows.${index + 1}`)} (${t(
+              probabilityIndexToTranslationKeys[index],
+            )})`
+          }
+        />
+      ),
+    }));
+  };
 
-  const consequenceValues = consequenceOptions.map((value, index) => ({
-    value: `${value}`,
-    /* @ts-ignore Because ts can't typecheck strings against our keys */
-    renderedValue: `${index + 1}: ${t(`consequenceTable.rows.${index + 1}`)}`,
-  }));
+  const consequenceValues = (selectedValue: number) => {
+    return consequenceOptions.map((value, index) => ({
+      value: `${value}`,
+      renderedValue: (
+        <RiskOptionDisplay
+          isSelected={
+            findConsequenceIndex(value) === findConsequenceIndex(selectedValue)
+          }
+          level={index + 1}
+          label={
+            /* @ts-ignore Because ts can't typecheck strings against our keys */
+            `${t(`consequenceTable.rows.${index + 1}`)} (${t(
+              consequenceIndexToTranslationKeys[index],
+            )})`
+          }
+        />
+      ),
+    }));
+  };
 
   return (
     <Paper sx={section}>
@@ -63,14 +110,14 @@ function ScenarioForm({
             <Select<FormScenario>
               control={control}
               name="risk.probability"
-              label={t('dictionary.probability')}
-              options={probabilityValues}
+              label={t('infoDialog.probabilityTitle')}
+              options={probabilityValues(Number(riskProbability))}
             />
             <Select<FormScenario>
               control={control}
               name="risk.consequence"
-              label={t('dictionary.consequence')}
-              options={consequenceValues}
+              label={t('infoDialog.consequenceTitle')}
+              options={consequenceValues(Number(riskConsequence))}
             />
           </Box>
         </Paper>
@@ -87,14 +134,14 @@ function ScenarioForm({
             <Select<FormScenario>
               control={control}
               name="remainingRisk.probability"
-              label={t('dictionary.probability')}
-              options={probabilityValues}
+              label={t('infoDialog.probabilityTitle')}
+              options={probabilityValues(Number(remainingRiskProbability))}
             />
             <Select<FormScenario>
               control={control}
               name="remainingRisk.consequence"
-              label={t('dictionary.consequence')}
-              options={consequenceValues}
+              label={t('infoDialog.consequenceTitle')}
+              options={consequenceValues(Number(remainingRiskConsequence))}
             />
           </Box>
         </Paper>
diff --git a/plugins/ros/src/components/scenarioDrawer/components/RiskOptionDisplay.tsx b/plugins/ros/src/components/scenarioDrawer/components/RiskOptionDisplay.tsx
new file mode 100644
index 000000000..d8f8f3106
--- /dev/null
+++ b/plugins/ros/src/components/scenarioDrawer/components/RiskOptionDisplay.tsx
@@ -0,0 +1,41 @@
+import { Box } from '@mui/material';
+
+export default function RiskOptionDisplay({
+  isSelected,
+  level,
+  label,
+}: {
+  isSelected: boolean;
+  level: number;
+  label: string;
+}) {
+  return (
+    <Box
+      sx={{
+        display: 'flex',
+        alignItems: 'center',
+        gap: '8px',
+      }}
+    >
+      <Box
+        sx={{
+          display: 'inline-flex',
+          justifyContent: 'center',
+          alignItems: 'center',
+          width: '24px',
+          height: '24px',
+          borderRadius: '50%',
+          backgroundColor: isSelected ? 'primary.main' : 'transparent',
+          border: '1px solid',
+          borderColor: isSelected ? 'primary.main' : 'primary.main',
+          color: isSelected ? 'white' : 'primary.main',
+          fontSize: '14px',
+          fontWeight: 'bold',
+        }}
+      >
+        {level}
+      </Box>
+      <span>{label}</span>
+    </Box>
+  );
+}
diff --git a/plugins/ros/src/components/scenarioDrawer/components/RiskSection.tsx b/plugins/ros/src/components/scenarioDrawer/components/RiskSection.tsx
index 73a472ac6..a7b749ee0 100644
--- a/plugins/ros/src/components/scenarioDrawer/components/RiskSection.tsx
+++ b/plugins/ros/src/components/scenarioDrawer/components/RiskSection.tsx
@@ -1,31 +1,33 @@
+import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
+import KeyboardDoubleArrowRightIcon from '@mui/icons-material/KeyboardDoubleArrowRight';
 import Box from '@mui/material/Box';
 import Paper from '@mui/material/Paper';
 import Typography from '@mui/material/Typography';
+import { useScenario } from '../../../contexts/ScenarioContext';
+import { pluginRiScTranslationRef } from '../../../utils/translations';
+import { Risk } from '../../../utils/types';
 import {
   formatNOK,
   getConsequenceLevel,
-  getRiskMatrixColor,
   getProbabilityLevel,
+  getRiskMatrixColor,
 } from '../../../utils/utilityfunctions';
-import KeyboardDoubleArrowRightIcon from '@mui/icons-material/KeyboardDoubleArrowRight';
-import { pluginRiScTranslationRef } from '../../../utils/translations';
-import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
-import { useScenario } from '../../../contexts/ScenarioContext';
-import { Risk } from '../../../utils/types';
-import { section } from '../scenarioDrawerComponents';
 import { body1, heading3, label, label2 } from '../../common/typography';
+import { section } from '../scenarioDrawerComponents';
 
 interface RiskProps {
   risk: Risk;
-  riskType: 'initialRisk' | 'restRisk';
+  heading: string;
 }
 
-function RiskBox({ risk, riskType }: RiskProps) {
+function RiskBox({ risk, heading }: RiskProps) {
   const { t } = useTranslationRef(pluginRiScTranslationRef);
 
+  const cost = risk.probability * risk.consequence;
+
   return (
     <Paper sx={section}>
-      <Typography sx={heading3}>{t(`dictionary.${riskType}`)}</Typography>
+      <Typography sx={heading3}>{heading}</Typography>
 
       <Box>
         <Box
@@ -63,8 +65,7 @@ function RiskBox({ risk, riskType }: RiskProps) {
       <Box>
         <Typography sx={label}>{t('dictionary.estimatedRisk')}</Typography>
         <Typography sx={body1}>
-          {formatNOK(risk.consequence * risk.probability)}{' '}
-          {t('riskMatrix.estimatedRisk.unit.nokPerYear')}
+          {formatNOK(cost)} {t('riskMatrix.estimatedRisk.unit.nokPerYear')}
         </Typography>
       </Box>
     </Paper>
@@ -73,12 +74,16 @@ function RiskBox({ risk, riskType }: RiskProps) {
 
 export function RiskSection() {
   const { scenario } = useScenario();
+  const { t } = useTranslationRef(pluginRiScTranslationRef);
 
   return (
     <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
-      <RiskBox risk={scenario.risk} riskType="initialRisk" />
+      <RiskBox risk={scenario.risk} heading={t('dictionary.initialRisk')} />
       <KeyboardDoubleArrowRightIcon sx={{ fontSize: '48px' }} />
-      <RiskBox risk={scenario.remainingRisk} riskType="restRisk" />
+      <RiskBox
+        risk={scenario.remainingRisk}
+        heading={t('dictionary.restRisk')}
+      />
     </Box>
   );
 }
diff --git a/plugins/ros/src/contexts/ScenarioContext.tsx b/plugins/ros/src/contexts/ScenarioContext.tsx
index 6e1ca1131..de401a374 100644
--- a/plugins/ros/src/contexts/ScenarioContext.tsx
+++ b/plugins/ros/src/contexts/ScenarioContext.tsx
@@ -1,19 +1,23 @@
+import { useRouteRef } from '@backstage/core-plugin-api';
+import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import {
   ReactNode,
-  useState,
-  useEffect,
-  useContext,
   createContext,
+  useContext,
+  useEffect,
+  useState,
 } from 'react';
-import { useRouteRef } from '@backstage/core-plugin-api';
-import { Action, FormScenario, Scenario } from '../utils/types';
-import { riScRouteRef, scenarioRouteRef } from '../routes';
 import { useNavigate, useParams } from 'react-router';
 import { useSearchParams } from 'react-router-dom';
-import { generateRandomId } from '../utils/utilityfunctions';
-import { useRiScs } from './RiScContext';
-import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
+import { riScRouteRef, scenarioRouteRef } from '../routes';
 import { pluginRiScTranslationRef } from '../utils/translations';
+import { Action, FormScenario, Scenario } from '../utils/types';
+import {
+  generateRandomId,
+  roundConsequenceToNearestConsequenceOption,
+  roundProbabilityToNearestProbabilityOption,
+} from '../utils/utilityfunctions';
+import { useRiScs } from './RiScContext';
 
 export const emptyAction = (): Action => ({
   ID: generateRandomId(),
@@ -268,13 +272,13 @@ export function ScenarioProvider({ children }: { children: ReactNode }) {
       ...apiScenario,
       risk: {
         ...apiScenario.risk,
-        probability: `${apiScenario.risk.probability}`,
-        consequence: `${apiScenario.risk.consequence}`,
+        probability: `${roundProbabilityToNearestProbabilityOption(apiScenario.risk.probability)}`,
+        consequence: `${roundConsequenceToNearestConsequenceOption(apiScenario.risk.consequence)}`,
       },
       remainingRisk: {
         ...apiScenario.remainingRisk,
-        probability: `${apiScenario.remainingRisk.probability}`,
-        consequence: `${apiScenario.remainingRisk.consequence}`,
+        probability: `${roundProbabilityToNearestProbabilityOption(apiScenario.remainingRisk.probability)}`,
+        consequence: `${roundConsequenceToNearestConsequenceOption(apiScenario.remainingRisk.consequence)}`,
       },
     };
     return returnFormScenario;
diff --git a/plugins/ros/src/risc_schema_en_v4_1.json b/plugins/ros/src/risc_schema_en_v4_1.json
new file mode 100644
index 000000000..ecafff99d
--- /dev/null
+++ b/plugins/ros/src/risc_schema_en_v4_1.json
@@ -0,0 +1,234 @@
+{
+  "$schema": "https://json-schema.org/draft/2020-12/schema",
+  "$id": "https://kartverket.github.io/backstage-plugin-risk-scorecard-backend/risc_schema_en_v4_1.json",
+  "title": "Risk Scorecard (RiSc)",
+  "description": "Schema for risk scorecard",
+  "type": "object",
+  "properties": {
+    "schemaVersion": {
+      "description": "Schema version",
+      "type": "string",
+      "default": "'4.1'"
+    },
+    "title": {
+      "description": "Title of the risk scorecard",
+      "type": "string"
+    },
+    "scope": {
+      "description": "The scope of the score card",
+      "type": "string"
+    },
+    "valuations": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "properties": {
+          "description": {
+            "type": "string"
+          },
+          "confidentiality": {
+            "type": "string",
+            "enum": [
+              "Public",
+              "Internal",
+              "Confidential",
+              "Strictly confidential"
+            ]
+          },
+          "integrity": {
+            "type": "string",
+            "enum": ["Insignificant", "Expected", "Dependent", "Critical"]
+          },
+          "availability": {
+            "type": "string",
+            "enum": ["Insignificant", "2 days", "4 hours", "Immediate"]
+          }
+        },
+        "required": [
+          "description",
+          "confidentiality",
+          "integrity",
+          "availability"
+        ],
+        "additionalProperties": false
+      },
+      "uniqueItems": true
+    },
+    "scenarios": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "properties": {
+          "title": {
+            "type": "string"
+          },
+          "scenario": {
+            "$ref": "#/$defs/scenario"
+          }
+        },
+        "required": ["title", "scenario"],
+        "additionalProperties": false
+      }
+    },
+    "sops": {
+      "type": "object"
+    }
+  },
+  "required": ["schemaVersion", "title", "scope", "scenarios"],
+  "additionalProperties": false,
+  "$defs": {
+    "risk": {
+      "type": "object",
+      "properties": {
+        "summary": {
+          "type": "string"
+        },
+        "probability": {
+          "description": "Number of events/day",
+          "type": "number",
+          "anyOf": [
+            {
+              "type": "number",
+              "enum": [0.0025, 0.05, 1, 20, 400]
+            },
+            {
+              "type": "number",
+              "minimum": 0
+            }
+          ]
+        },
+        "consequence": {
+          "description": "Monetary amount per day",
+          "type": "number",
+          "anyOf": [
+            {
+              "type": "number",
+              "enum": [8000, 160000, 3200000, 64000000, 1280000000]
+            },
+            {
+              "type": "number",
+              "minimum": 0
+            }
+          ]
+        }
+      },
+      "required": ["probability", "consequence"],
+      "additionalProperties": false
+    },
+    "url": {
+      "type": "string",
+      "pattern": "^(?:(https?|s?ftp):\\/\\/)?(?:([\\w-]+)\\.)?([\\w-]+)\\.([\\w]+)\\/?(?:([^?#$]+))?(?:\\?([^#$]+))?(?:#(.*))?$"
+    },
+    "scenario": {
+      "type": "object",
+      "properties": {
+        "ID": {
+          "type": "string",
+          "pattern": "^[a-zA-Z0-9]{5,}$"
+        },
+        "description": {
+          "type": "string"
+        },
+        "url": {
+          "$ref": "#/$defs/url"
+        },
+        "threatActors": {
+          "type": "array",
+          "items": {
+            "type": "string",
+            "enum": [
+              "Script kiddie",
+              "Hacktivist",
+              "Reckless employee",
+              "Insider",
+              "Organised crime",
+              "Terrorist organisation",
+              "Nation/government"
+            ]
+          }
+        },
+        "vulnerabilities": {
+          "type": "array",
+          "items": {
+            "type": "string",
+            "enum": [
+              "Flawed design",
+              "Misconfiguration",
+              "Dependency vulnerability",
+              "Unauthorized access",
+              "Unmonitored use",
+              "Input tampering",
+              "Information leak",
+              "Excessive use"
+            ]
+          }
+        },
+        "risk": {
+          "$ref": "#/$defs/risk"
+        },
+        "actions": {
+          "type": "array",
+          "items": {
+            "type": "object",
+            "properties": {
+              "title": {
+                "type": "string"
+              },
+              "action": {
+                "$ref": "#/$defs/action"
+              }
+            },
+            "required": ["title", "action"],
+            "additionalProperties": false
+          }
+        },
+        "remainingRisk": {
+          "$ref": "#/$defs/risk"
+        }
+      },
+      "required": [
+        "ID",
+        "description",
+        "threatActors",
+        "vulnerabilities",
+        "risk",
+        "actions",
+        "remainingRisk"
+      ],
+      "additionalProperties": false
+    },
+    "action": {
+      "type": "object",
+      "properties": {
+        "ID": {
+          "type": "string",
+          "pattern": "^[a-zA-Z0-9]{5,}$"
+        },
+        "description": {
+          "type": "string"
+        },
+        "url": {
+          "anyOf": [
+            { "$ref": "#/$defs/url" },
+            {
+              "type": "string",
+              "maxLength": 0
+            }
+          ]
+        },
+        "status": {
+          "type": "string",
+          "enum": [
+            "Not started",
+            "In progress",
+            "On hold",
+            "Completed",
+            "Aborted"
+          ]
+        }
+      },
+      "required": ["ID", "description", "status"],
+      "additionalProperties": false
+    }
+  }
+}
diff --git a/plugins/ros/src/utils/constants.ts b/plugins/ros/src/utils/constants.ts
index 86ce573e7..acd124489 100644
--- a/plugins/ros/src/utils/constants.ts
+++ b/plugins/ros/src/utils/constants.ts
@@ -1,10 +1,18 @@
-export const schemaPath = '../risc_schema_en_v4_0.json';
+export const schemaPath = '../risc_schema_en_v4_1.json';
 
-export const latestSupportedVersion = '4.0';
+export const latestSupportedVersion = '4.1';
 
-export const consequenceOptions = [1000, 30000, 1000000, 30000000, 1000000000];
+export const BASE_NUMBER = 20.0;
+export const CONSEQUENCE_SCALE_OFFSET = 3;
+export const PROBABILITY_SCALE_OFFSET = -2;
 
-export const probabilityOptions = [0.01, 0.1, 1, 50, 300];
+export const consequenceOptions = Array.from({ length: 5 }, (_, i) =>
+  Math.pow(BASE_NUMBER, i + CONSEQUENCE_SCALE_OFFSET),
+);
+
+export const probabilityOptions = Array.from({ length: 5 }, (_, i) =>
+  Math.pow(BASE_NUMBER, i + PROBABILITY_SCALE_OFFSET),
+);
 
 export enum ThreatActorsOptions {
   ScriptKiddie = 'Script kiddie',
diff --git a/plugins/ros/src/utils/translations.ts b/plugins/ros/src/utils/translations.ts
index becde5b83..aed8ddc05 100644
--- a/plugins/ros/src/utils/translations.ts
+++ b/plugins/ros/src/utils/translations.ts
@@ -173,20 +173,32 @@ export const pluginRiScMessages = {
       'The estimated risk is a calculation based on the risks the different scenarios pose. If there is a high probability that a serious consequence will occur, this could potentially become a large cost for the organization. In other words, the cost is an attempt to make the risk value more tangible and is the sum of the estimated risk for all the risk scenarios in this risk scorecard.',
     calculatedHowTitle: 'How do we calculate the estimated risk?',
     calculatedHow:
-      'Consequence is measured in cost (in NOK) per incident and probability is measured in incidents per year. ' +
-      'The estimated risk is calculated as C x P.',
+      'Consequence (C) is measured in cost (in NOK) per incident and probability (P) is measured in incidents per year. ' +
+      'The estimated risk is calculated as 20',
+    calculatedHowExponent: 'P+C-1',
     consequenceTitle: 'Consequence (NOK/incident)', // incident, event or occurrence
+    consequenceUnit: 'NOK/incident',
+    consequenceDescription: {
+      oneworkday: '1 workday',
+      oneworkmonth: '1 work month',
+      oneworkyear: '1 work year',
+      '20workyears': '20 work years',
+      '400workyears': '400 work years',
+    },
     probabilityTitle: 'Probability (incidents/year)',
+    probabilityUnit: 'incidents/year',
     probabilityDescription: {
-      '0': 'every 100 years', // 100 year intervals?
-      '1': 'every 10 years', // 10 year intervals?
-      '2': 'yearly', // almost yearly?
-      '3': 'weekly',
-      '4': 'daily',
+      every400years: 'Once every 400 years',
+      every20years: 'Once every 20 years',
+      annualy: 'Annually',
+      monthly: 'Monthly',
+      daily: 'Daily',
+    },
+    example: {
+      part1: 'A risk scenario with consequence ',
+      part2: ' and probability ',
+      part3: ' has an estimated risk of ',
     },
-    example:
-      'A scenario with consequence 2 and probability 4 has an estimated risk of ' +
-      '30 000 NOK/incident x 50 incidents/year = 1 500 000 NOK/year.',
   },
   rosDialog: {
     titleNew: 'New risk scorecard',
@@ -346,11 +358,11 @@ export const pluginRiScMessages = {
         '5': 'Death or permanent severe health implications for many people. Severe damage to the environment across large areas.',
       },
       economical: {
-        '1': 'Temporary minor financial loss.\n\nMay be fixed within an hour by one employee',
-        '2': 'Temporary financial loss.\n\nMay be fixed within three days by one employee',
-        '3': 'Financial loss of some duration.\n\nMay be fixed within a month by three employees',
-        '4': 'Financial loss of considerable duration for the organization and any third parties.\n\nMay be fixed within a year by 10 employees',
-        '5': 'Permanent and severe financial loss.\n\nMay be fixed within three years by 100 employees',
+        '1': 'Temporary minor financial loss.\n\nMay be fixed with 1 workday.',
+        '2': 'Temporary financial loss.\n\nMay be fixed with 1 work month.',
+        '3': 'Financial loss of some duration.\n\nMay be fixed with 1 work year.',
+        '4': 'Financial loss of considerable duration for the organization and any third parties.\n\nMay be fixed with 20 work years.',
+        '5': 'Permanent and severe financial loss.\n\nMay be fixed with 400 work years.',
       },
       privacy: {
         '1': 'The right to privacy is violated for a very short period and does not involve sensitive categories or vulnerable groups', // særlige kategorier = sensitive kategorier? special/particular/sensitive categories
@@ -377,11 +389,11 @@ export const pluginRiScMessages = {
       '5': 'Very high', // svært stor
     },
     cells: {
-      '1': 'The scenario is unlikely to occur.\n\nIt occurs less often than every 100 years',
-      '2': 'The scenario is unlikely to occur.\n\nIt can occur every 10 years',
-      '3': 'The scenario can occur.\n\nIt can occur almost every year',
-      '4': 'The scenario will very likely occur.\n\nIt can occur almost weekly',
-      '5': 'The scenario is almost guaranteed to occur.\n\nIt can occur almost daily',
+      '1': 'The scenario is unlikely to occur.\n\nIt occurs less often than every 400 years',
+      '2': 'The scenario is unlikely to occur.\n\nIt can occur every 20 years',
+      '3': 'The scenario can occur.\n\nIt can occur yearly',
+      '4': 'The scenario will very likely occur.\n\nIt can occur monthly',
+      '5': 'The scenario is almost guaranteed to occur.\n\nIt can occur daily',
     },
   },
   threatActors: {
@@ -614,16 +626,30 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens (K) måles i kroner per hendelse og sannsynlighet (S) måles i hendelser per år. Den estimerte risikoen blir da: 20',
+          'infoDialog.calculatedHowExponent': 'S+K-1',
           'infoDialog.consequenceTitle': 'Konsekvens (kr/hendelse)',
+          'infoDialog.consequenceUnit': 'kr/hendelse',
+          'infoDialog.consequenceDescription.oneworkday': '1 dagsverk',
+          'infoDialog.consequenceDescription.oneworkmonth': '1 månedsverk',
+          'infoDialog.consequenceDescription.oneworkyear': '1 årsverk',
+          'infoDialog.consequenceDescription.20workyears': '20 årsverk',
+          'infoDialog.consequenceDescription.400workyears': '400 årsverk',
           'infoDialog.probabilityTitle': 'Sannsynlighet (hendelser/år)',
-          'infoDialog.probabilityDescription.0': 'ca hvert 100. år',
-          'infoDialog.probabilityDescription.1': 'ca hvert 10. år',
-          'infoDialog.probabilityDescription.2': 'ca årlig',
-          'infoDialog.probabilityDescription.3': 'ca ukentlig',
-          'infoDialog.probabilityDescription.4': 'ca daglig',
-          'infoDialog.example':
-            'Et risikoscenario med konsekvens 2 og sannsynlighet 4 har en estimert risiko på 30 000 kr/hendelse x 50 hendelser/år = 1 500 000 kr/år.',
+          'infoDialog.probabilityUnit': 'hendelse/år',
+          'infoDialog.probabilityDescription.every400years':
+            '1 gang hvert 400. år',
+          'infoDialog.probabilityDescription.every20years':
+            '1 gang hvert 20. år',
+          'infoDialog.probabilityDescription.annualy': 'Årlig',
+          'infoDialog.probabilityDescription.monthly': 'Månedlig',
+          'infoDialog.probabilityDescription.daily': 'Daglig',
+          'infoDialog.example.part1': 'Et risikoscenario med konsekvens ',
+          'infoDialog.example.part2': ' og sannsynlighet ',
+          'infoDialog.example.part3': ' har en estimert risiko på ',
+          'infoDialog.units.nokPerIncident': 'kr/hendelse',
+          'infoDialog.units.incidentsPerYear': 'hendelser/år',
+          'infoDialog.units.nokPerYear': 'kr/år',
 
           'rosDialog.titleNew': 'Ny risiko- og sårbarhetsanalyse',
           'rosDialog.titleEdit': 'Rediger tittel og omfang',
@@ -769,17 +795,17 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'consequenceTable.cells.health.4':
             'Et fåtall alvorlige personskader.\n\nModerat skade på miljø på et begrenset område.',
           'consequenceTable.cells.health.5':
-            'Dødsfall. Svært alvorlig skade på milhø over store områder.',
+            'Dødsfall. Svært alvorlig skade på miljø over store områder.',
           'consequenceTable.cells.economical.1':
-            'Ubetydelig økonomisk tap.\n\nKan fikses i løpet av en time av én ansatt.',
+            'Ubetydelig økonomisk tap.\n\nKan fikses med 1 dagsverk.',
           'consequenceTable.cells.economical.2':
-            'Mindre økonomisk tap.\n\nKan fikses i løpet av tre dager av én ansatt.',
+            'Mindre økonomisk tap.\n\nKan fikses med 1 månedsverk.',
           'consequenceTable.cells.economical.3':
-            'Moderat økonomisk tap.\n\nKan fikses i løpet av en måned av tre ansatte.',
+            'Moderat økonomisk tap.\n\nKan fikses med 1 årsverk.',
           'consequenceTable.cells.economical.4':
-            'Større økonomisk tap.\n\nKan fikses i løpet av et år av 10 ansatte.',
+            'Større økonomisk tap.\n\nKan fikses med 20 årsverk.',
           'consequenceTable.cells.economical.5':
-            'Kritisk økonomisk tap.\n\nKan fikses i løpet av tre år av 100 ansatte.',
+            'Kritisk økonomisk tap.\n\nKan fikses med 400 årsverk.',
           'consequenceTable.cells.privacy.1': 'Ingen brudd.',
           'consequenceTable.cells.privacy.2': 'Mindre brudd.',
           'consequenceTable.cells.privacy.3':
diff --git a/plugins/ros/src/utils/utilityfunctions.test.ts b/plugins/ros/src/utils/utilityfunctions.test.ts
index b3de99b0d..e8c21cc99 100644
--- a/plugins/ros/src/utils/utilityfunctions.test.ts
+++ b/plugins/ros/src/utils/utilityfunctions.test.ts
@@ -11,11 +11,15 @@ import {
   calculateDaysSince,
   calculateUpdatedStatus,
   deleteScenario,
+  findConsequenceIndex,
+  findProbabilityIndex,
   formatNOK,
   formatNumber,
   generateRandomId,
   isDeeplyEqual,
   requiresNewApproval,
+  roundConsequenceToNearestConsequenceOption,
+  roundProbabilityToNearestProbabilityOption,
   threatActorOptionsToTranslationKeys,
   vulnerabiltiesOptionsToTranslationKeys,
 } from './utilityfunctions';
@@ -582,3 +586,134 @@ describe('Translation mappings match translation keys', () => {
     });
   });
 });
+
+describe('Enum to translation key maps', () => {
+  it('actionStatusOptionsToTranslationKeys keys should match ActionStatusOptions values', () => {
+    const enumValues = Object.values(ActionStatusOptions);
+    const mapKeys = Object.keys(actionStatusOptionsToTranslationKeys);
+    expect(mapKeys.sort()).toEqual(enumValues.sort());
+  });
+
+  it('threatActorOptionsToTranslationKeys keys should match ThreatActorsOptions values', () => {
+    const enumValues = Object.values(ThreatActorsOptions);
+    const mapKeys = Object.keys(threatActorOptionsToTranslationKeys);
+    expect(mapKeys.sort()).toEqual(enumValues.sort());
+  });
+
+  it('vulnerabiltiesOptionsToTranslationKeys keys should match VulnerabilitiesOptions values', () => {
+    const enumValues = Object.values(VulnerabilitiesOptions);
+    const mapKeys = Object.keys(vulnerabiltiesOptionsToTranslationKeys);
+    expect(mapKeys.sort()).toEqual(enumValues.sort());
+  });
+});
+
+describe('findProbabilityIndex', () => {
+  it.each([
+    [1 / 400, 0], // 20^-2
+    [1 / 20, 1], // 20^-1
+    [1, 2], // 20^0
+    [20, 3], // 20^1
+    [400, 4], // 20^2
+  ])('returns %i for probability %f', (input, expected) => {
+    expect(findProbabilityIndex(input)).toBe(expected);
+  });
+
+  it('clamps low values to 0', () => {
+    expect(findProbabilityIndex(1e-10)).toBe(0);
+  });
+
+  it('clamps high values to 4', () => {
+    expect(findProbabilityIndex(1e5)).toBe(4);
+  });
+
+  it('handles values between defined steps', () => {
+    // log_20(4.47) + 2 = ~2.49 => round to 2
+    expect(findProbabilityIndex(4.47)).toBe(2);
+  });
+});
+
+describe('findConsequenceIndex', () => {
+  it.each([
+    [8000, 0], // 20^3
+    [160000, 1], // 20^4
+    [3200000, 2], // 20^5
+    [64000000, 3], // 20^6
+    [1280000000, 4], // 20^7
+  ])('returns %i for consequence %f', (input, expected) => {
+    expect(findConsequenceIndex(input)).toBe(expected);
+  });
+
+  it('clamps low values to 0', () => {
+    expect(findConsequenceIndex(1000)).toBe(0);
+  });
+
+  it('clamps high values to 4', () => {
+    expect(findConsequenceIndex(1e10)).toBe(4);
+  });
+
+  it('handles values between defined steps', () => {
+    // log_20(15M) - 3 = ~2.51 => round to 3
+    expect(findConsequenceIndex(15000000)).toBe(3);
+  });
+});
+
+describe('roundConsequenceToNearestConsequenceOption', () => {
+  it.each([
+    [8000, 8000], // Exact match for index 0
+    [160000, 160000], // Exact match for index 1
+    [3200000, 3200000], // Exact match for index 2
+    [64000000, 64000000], // Exact match for index 3
+    [1280000000, 1280000000], // Exact match for index 4
+  ])('returns exact match %i for consequence %f', (input, expected) => {
+    expect(roundConsequenceToNearestConsequenceOption(input)).toBe(expected);
+  });
+
+  it.each([
+    [10000, 8000], // round to 8000 (index 0)
+    [1000000, 3200000], // round to 3200000 (index 3)
+  ])('rounds %i to nearest consequence %f', (input, expected) => {
+    expect(roundConsequenceToNearestConsequenceOption(input)).toBe(expected);
+  });
+
+  it.each([
+    [1e10, 1280000000], // Clamped to index 4
+    [1000, 8000], // Clamped to index 0
+  ])('clamps %i to consequence %f', (input, expected) => {
+    expect(roundConsequenceToNearestConsequenceOption(input)).toBe(expected);
+  });
+});
+
+describe('roundProbabilityToNearestProbabilityOption', () => {
+  it.each([
+    [1 / 400, 1 / 400], // index 0 => 20^-2
+    [1 / 20, 1 / 20], // index 1 => 20^-1
+    [1, 1], // index 2 => 20^0
+    [20, 20], // index 3 => 20^1
+    [400, 400], // index 4 => 20^2
+  ])('returns exact match %i for probability %f', (input, expected) => {
+    expect(roundProbabilityToNearestProbabilityOption(input)).toBeCloseTo(
+      expected,
+      5,
+    );
+  });
+
+  it.each([
+    [0.01, 0.0025], // round to 1/400 (index 0)
+    [10, 20], // round to 1/20 (index 3)
+  ])('rounds %i to nearest probability %f', (input, expected) => {
+    expect(roundProbabilityToNearestProbabilityOption(input)).toBeCloseTo(
+      expected,
+      5,
+    );
+  });
+
+  it.each([
+    [0.0001, 1 / 400], // clamped to index 0
+    [1000, 400], // clamped to index 4
+  ])('clamps %i to probability %f', (input, expected) => {
+    expect(roundProbabilityToNearestProbabilityOption(input)).toBeCloseTo(
+      expected,
+      5,
+    );
+  });
+});
diff --git a/plugins/ros/src/utils/utilityfunctions.ts b/plugins/ros/src/utils/utilityfunctions.ts
index a85444ca6..9e1926249 100644
--- a/plugins/ros/src/utils/utilityfunctions.ts
+++ b/plugins/ros/src/utils/utilityfunctions.ts
@@ -2,11 +2,12 @@ import { formatISO } from 'date-fns';
 import { UpdateStatus } from '../contexts/RiScContext';
 import {
   ActionStatusOptions,
+  BASE_NUMBER,
+  CONSEQUENCE_SCALE_OFFSET,
+  PROBABILITY_SCALE_OFFSET,
   ThreatActorsOptions,
   VulnerabilitiesOptions,
-  consequenceOptions,
   latestSupportedVersion,
-  probabilityOptions,
   riskMatrix,
 } from './constants';
 import { RiSc, RiScWithMetadata, Risk, Scenario } from './types';
@@ -40,17 +41,17 @@ export function getAlertSeverity(
 }
 
 export function getRiskMatrixColor(risiko: Risk) {
-  const sannsynlighet = probabilityOptions.indexOf(risiko.probability);
-  const konsekvens = consequenceOptions.indexOf(risiko.consequence);
+  const sannsynlighet = findProbabilityIndex(risiko.probability);
+  const konsekvens = findConsequenceIndex(risiko.consequence);
   return riskMatrix[4 - konsekvens][sannsynlighet];
 }
 
 export function getProbabilityLevel(risiko: Risk) {
-  return probabilityOptions.indexOf(risiko.probability) + 1;
+  return findProbabilityIndex(risiko.probability) + 1;
 }
 
 export function getConsequenceLevel(risiko: Risk) {
-  return consequenceOptions.indexOf(risiko.consequence) + 1;
+  return findConsequenceIndex(risiko.consequence) + 1;
 }
 
 export function emptyRiSc(): RiSc {
@@ -358,6 +359,74 @@ export function isDeeplyEqual<T>(
   return false;
 }
 
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+/*
+  Probability is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^-2 = 1 / 400
+  1 = 20^-1 = 1 / 20
+  2 = 20^-0 = 1
+  3 = 20^1  = 20
+  4 = 20^2  = 400
+*/
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) - PROBABILITY_SCALE_OFFSET,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+/*
+  Consequence  is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^3 = 8 000
+  1 = 20^4 = 160 000
+  2 = 20^5 = 3 200 000
+  3 = 20^6 = 64 000 000
+  4 = 20^7 = 1 280 000 000
+*/
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.round(
+    logBase(consequence, BASE_NUMBER) - CONSEQUENCE_SCALE_OFFSET,
+  );
+  return Math.min(4, Math.max(0, consequenceIndex));
+}
+
+/*
+  Logarithmically round the consequence to the nearest consequence option.
+*/
+export function roundConsequenceToNearestConsequenceOption(
+  consequence: number,
+): number {
+  return Math.pow(BASE_NUMBER, findConsequenceIndex(consequence) + 3);
+}
+
+/*
+  Logarithmically round the probability to the nearest probability option.
+*/
+export function roundProbabilityToNearestProbabilityOption(
+  probability: number,
+): number {
+  return Math.pow(BASE_NUMBER, findProbabilityIndex(probability) - 2);
+}
+
+export const consequenceIndexToTranslationKeys: Record<number, string> = {
+  0: 'infoDialog.consequenceDescription.oneworkday',
+  1: 'infoDialog.consequenceDescription.oneworkmonth',
+  2: 'infoDialog.consequenceDescription.oneworkyear',
+  3: 'infoDialog.consequenceDescription.20workyears',
+  4: 'infoDialog.consequenceDescription.400workyears',
+};
+
+export const probabilityIndexToTranslationKeys: Record<number, string> = {
+  0: 'infoDialog.probabilityDescription.every400years',
+  1: 'infoDialog.probabilityDescription.every20years',
+  2: 'infoDialog.probabilityDescription.annualy',
+  3: 'infoDialog.probabilityDescription.monthly',
+  4: 'infoDialog.probabilityDescription.daily',
+};
+
 export const actionStatusOptionsToTranslationKeys: Record<
   ActionStatusOptions,
   string


Reviews:
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:19:37Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:19:55Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:28:29Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:32:58Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:35:20Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:36:25Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:40:08Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:43:10Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:43:55Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:45:06Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T08:47:39Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T09:01:30Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T09:02:28Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T11:56:43Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T11:56:50Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T11:58:21Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T14:05:11Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T14:13:04Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T14:14:00Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T14:20:41Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T14:22:13Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T14:22:47Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T14:22:57Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T14:23:59Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T21:32:57Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T21:37:26Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T21:41:34Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T21:41:50Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T21:43:25Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T11:24:30Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T11:24:36Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T14:18:46Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T14:40:16Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T08:48:32Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T09:04:23Z
user: login: halvardhummel
body: Som også kommentert i PR-en for backenden, så synes jeg hele oppsettet med at man får lov til å skrive andre verdier lokalt enn de 5 som brukes av frontenden, er veldig rart.

Sånn det ser ut for meg, var dette ikke støttet i frontenden før endringen man gjør her. Og endringene her ser ut til å i all hovedsak ignorere andre verdier enn de 5 som er basert på grunntall 20. Man runder av til den nærmeste av disse 5 (på forskjellig måte for konsekvens og sannsynlighet) og viser kun denne avrundede verdien, både når man viser hvilket nivå man er på (1-5) og når man regner ut kostnaden. Det vil si, endringene man gjør lokalt med andre verdier ignoreres med mindre man ser på ROSen lokalt. Brukere som ser på ROSen i nettleseren har da ingen anelse om at andre verdier brukes og vil lett kunne ende opp med å skrive over disse uten å mene det?
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T09:21:27Z
user: login: AndersHR
body: Smooth!

<img src="https://media2.giphy.com/media/v1.Y2lkPWJkM2VhNTdlaTl1Nzg4eTl1OW1qYTBrZHA0M2M3cXh2ZHJwMXQ5d3dyMmljdDlsYSZlcD12MV9naWZzX3NlYXJjaCZjdD1n/JmCFfTlNy12wFpOK8Y/giphy.gif"/>
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T09:41:48Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T10:44:31Z
user: login: emilte
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T11:05:45Z
user: login: ingelinsu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T12:49:39Z
user: login: halvardhummel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T09:24:43Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T07:51:28Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T13:05:08Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T13:05:26Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T13:05:35Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T13:05:41Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T13:05:44Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T13:05:50Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T13:05:57Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T13:07:34Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T13:07:38Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T13:07:47Z
user: login: halvardhummel
body: Begynner å se bra ut! Har litt mer småpirk til.

Kanskje @emilte også burde ta en titt. Han har mer oversikt over frontenden enn meg.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T09:20:10Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T10:32:18Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T10:32:22Z
user: login: AndersHR
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T08:48:14Z
user: login: emilte
body: Synes det ser veldig bra ut
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T09:16:32Z

Review comments:
diff_hunk: @@ -82,5 +82,8 @@
       "prettier --write"
     ]
   },
-  "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6"
+  "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6",
+  "dependencies": {
+    "html-react-parser": "^5.2.3"
+  }
path: package.json
user: login: emilte
body: Installer i ros prosjektet
created_at: 2025-04-22T08:19:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -82,5 +82,8 @@
       "prettier --write"
     ]
   },
-  "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6"
+  "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6",
+  "dependencies": {
+    "html-react-parser": "^5.2.3"
+  }
path: package.json
user: login: emilte
body: kan fjernes herfra
created_at: 2025-04-22T08:19:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -53,6 +53,7 @@
     "@uiw/react-md-editor": "^4.0.5",
     "date-fns": "^4.1.0",
     "github-markdown-css": "^5.8.1",
+    "html-react-parser": "^5.2.3",
path: plugins/ros/package.json
user: login: emilte
body: Det er veldig lite usecase for dette. Foreslår å bruke dangerouslySetInnerHTML i stedet
created_at: 2025-04-22T08:28:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,12 +12,47 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import { consequenceOptions, probabilityOptions } from '../../utils/constants';
 import { formatNOK } from '../../utils/utilityfunctions';
 import { useEstimatedRiskInfoDialogStyles } from './estimatedRiskInfoDialogStyle';
+import parse from 'html-react-parser';
 
 interface EstimatedRiskInfoDialogProps {
   isOpen: boolean;
   onClose: () => void;
 }
 
+const ConsequenceDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index + 3}</sup> = {formatNOK(option)}{' '}
+    {t('infoDialog.consequenceDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.consequenceDescription.${index}`))}</b>
+    <br />
+  </Fragment>
+);
+
+const ProbabilityDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index - 2}</sup> = {option}{' '}
+    {t('infoDialog.probabilityDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.probabilityDescription.${index}`))}</b>
+    <br />
+  </Fragment>
+);
+
path: plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
user: login: emilte
body: Gjerne bruk `function` og separer i egne filer
created_at: 2025-04-22T08:32:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,12 +12,47 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import { consequenceOptions, probabilityOptions } from '../../utils/constants';
 import { formatNOK } from '../../utils/utilityfunctions';
 import { useEstimatedRiskInfoDialogStyles } from './estimatedRiskInfoDialogStyle';
+import parse from 'html-react-parser';
 
 interface EstimatedRiskInfoDialogProps {
   isOpen: boolean;
   onClose: () => void;
 }
 
+const ConsequenceDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index + 3}</sup> = {formatNOK(option)}{' '}
+    {t('infoDialog.consequenceDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.consequenceDescription.${index}`))}</b>
+    <br />
+  </Fragment>
+);
+
+const ProbabilityDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index - 2}</sup> = {option}{' '}
+    {t('infoDialog.probabilityDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.probabilityDescription.${index}`))}</b>
+    <br />
+  </Fragment>
+);
+
path: plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
user: login: emilte
body: Det er egt ikke så mye å hente her, logikker er minimal. Foreslår å bare legge det inn direkte der de brukes. Dette er ikke mye gjenbrukbart
created_at: 2025-04-22T08:35:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,9 +21,18 @@ interface RiskProps {
   riskType: 'initialRisk' | 'restRisk';
 }
 
+function calculateCost(risk: Risk, initialRisk: boolean): number {
+  const probability = initialRisk ? risk.probability : risk.probability;
+  const consequence = initialRisk ? risk.consequence : risk.consequence;
+
+  return calculateRiScCost(probability, consequence);
+}
path: plugins/ros/src/components/scenarioDrawer/components/RiskSection.tsx
user: login: emilte
body: Hva skjer her?
created_at: 2025-04-22T08:36:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,9 +21,18 @@ interface RiskProps {
   riskType: 'initialRisk' | 'restRisk';
 }
 
+function calculateCost(risk: Risk, initialRisk: boolean): number {
+  const probability = initialRisk ? risk.probability : risk.probability;
+  const consequence = initialRisk ? risk.consequence : risk.consequence;
+
+  return calculateRiScCost(probability, consequence);
+}
+
 function RiskBox({ risk, riskType }: RiskProps) {
path: plugins/ros/src/components/scenarioDrawer/components/RiskSection.tsx
user: login: emilte
body: Har ikke helt sansen for `riskType` prop. Foreslår å skrive om til å ta inn `heading` prop
created_at: 2025-04-22T08:40:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -617,16 +625,23 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: 20<sup>S+K-1</sup> kr/år.',
           'infoDialog.consequenceTitle': 'Konsekvens (kr/hendelse)',
+          'infoDialog.consequenceDescriptionGeneral': 'kr/hendelse',
+          'infoDialog.consequenceDescription.0': '1 dagsverk',
+          'infoDialog.consequenceDescription.1': '1 månedsverk',
+          'infoDialog.consequenceDescription.2': '1 årsverk',
+          'infoDialog.consequenceDescription.3': '50 årsverk',
+          'infoDialog.consequenceDescription.4': '1000 årsverk',
           'infoDialog.probabilityTitle': 'Sannsynlighet (hendelser/år)',
-          'infoDialog.probabilityDescription.0': 'ca hvert 100. år',
-          'infoDialog.probabilityDescription.1': 'ca hvert 10. år',
-          'infoDialog.probabilityDescription.2': 'ca årlig',
-          'infoDialog.probabilityDescription.3': 'ca ukentlig',
-          'infoDialog.probabilityDescription.4': 'ca daglig',
+          'infoDialog.probabilityDescriptionGeneral': 'hendelse/år',
path: plugins/ros/src/utils/translations.ts
user: login: emilte
body: ```suggestion
          'infoDialog.probabilityUnit': 'hendelse/år',
```
created_at: 2025-04-22T08:43:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -617,16 +625,23 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: 20<sup>S+K-1</sup> kr/år.',
path: plugins/ros/src/utils/translations.ts
user: login: emilte
body: Denne kan forenkles og skrives som vanlig react. Lag en t for unit kr/år
created_at: 2025-04-22T08:43:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -617,16 +625,23 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: 20<sup>S+K-1</sup> kr/år.',
           'infoDialog.consequenceTitle': 'Konsekvens (kr/hendelse)',
+          'infoDialog.consequenceDescriptionGeneral': 'kr/hendelse',
+          'infoDialog.consequenceDescription.0': '1 dagsverk',
+          'infoDialog.consequenceDescription.1': '1 månedsverk',
+          'infoDialog.consequenceDescription.2': '1 årsverk',
+          'infoDialog.consequenceDescription.3': '50 årsverk',
+          'infoDialog.consequenceDescription.4': '1000 årsverk',
           'infoDialog.probabilityTitle': 'Sannsynlighet (hendelser/år)',
-          'infoDialog.probabilityDescription.0': 'ca hvert 100. år',
-          'infoDialog.probabilityDescription.1': 'ca hvert 10. år',
-          'infoDialog.probabilityDescription.2': 'ca årlig',
-          'infoDialog.probabilityDescription.3': 'ca ukentlig',
-          'infoDialog.probabilityDescription.4': 'ca daglig',
+          'infoDialog.probabilityDescriptionGeneral': 'hendelse/år',
+          'infoDialog.probabilityDescription.0': '1 gang hvert 400. år',
+          'infoDialog.probabilityDescription.1': '1 gang hvert 20. år',
+          'infoDialog.probabilityDescription.2': 'Årlig',
+          'infoDialog.probabilityDescription.3': 'Månedlig',
+          'infoDialog.probabilityDescription.4': 'Daglig',
           'infoDialog.example':
-            'Et risikoscenario med konsekvens 2 og sannsynlighet 4 har en estimert risiko på 30 000 kr/hendelse x 50 hendelser/år = 1 500 000 kr/år.',
+            'Et risikoscenario med konsekvens 2 (20<sup>4</sup> = 160 000 kr/hendelse) og sannsynlighet 4 (20<sup>1</sup> = 20 hendelser/år) har en estimert risiko på 20<sup>1+4-1</sup> = 160 000 kr/år.',
path: plugins/ros/src/utils/translations.ts
user: login: emilte
body: Foreslår dangerouslySetInnerHTML
created_at: 2025-04-22T08:45:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,12 +12,47 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import { consequenceOptions, probabilityOptions } from '../../utils/constants';
 import { formatNOK } from '../../utils/utilityfunctions';
 import { useEstimatedRiskInfoDialogStyles } from './estimatedRiskInfoDialogStyle';
+import parse from 'html-react-parser';
 
 interface EstimatedRiskInfoDialogProps {
   isOpen: boolean;
   onClose: () => void;
 }
 
+const ConsequenceDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index + 3}</sup> = {formatNOK(option)}{' '}
+    {t('infoDialog.consequenceDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.consequenceDescription.${index}`))}</b>
path: plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
user: login: emilte
body: trans er egt ikke så glad i dynamiske strenger. Foreslår å lage mapping fra index til key. Da kan keys også ha bedre navn
created_at: 2025-04-22T08:47:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -37,34 +72,37 @@ export function EstimatedRiskInfoDialog({
             {t('infoDialog.calculatedHowTitle')}
           </Typography>
           <DialogContentText className={text}>
-            {t('infoDialog.calculatedHow')}
+            {parse(t('infoDialog.calculatedHow'))}
           </DialogContentText>
           <Typography style={{ fontWeight: 'bold' }}>
             {t('infoDialog.consequenceTitle')}
           </Typography>
           <DialogContentText className={text}>
             {consequenceOptions.map((option, index) => (
-              <Fragment key={option}>
-                {index + 1}: {formatNOK(option)}
-                <br />
-              </Fragment>
+              <ConsequenceDescription
+                key={index}
+                option={option}
+                index={index}
+                t={t}
+              />
             ))}
           </DialogContentText>
           <Typography style={{ fontWeight: 'bold' }}>
             {t('infoDialog.probabilityTitle')}
           </Typography>
           <DialogContentText className={text}>
             {probabilityOptions.map((option, index) => (
-              <Fragment key={option}>
-                {index + 1}: {option}, {/* @ts-ignore */}
-                {t(`infoDialog.probabilityDescription.${index}`)}
-                <br />
-              </Fragment>
+              <ProbabilityDescription
+                key={index}
+                option={option}
+                index={index}
+                t={t}
+              />
             ))}
           </DialogContentText>
path: plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
user: login: emilte
body: Kunne en tabell kanskje vært enklere å lese?
created_at: 2025-04-22T09:01:29Z
author_association: CONTRIBUTOR
path: plugins/ros/src/risc_schema_en_v4_1.json
user: login: emilte
body: Hvilke problemer er det som oppstår ifm versjonsendring i skjema?
created_at: 2025-04-22T09:02:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -82,5 +82,8 @@
       "prettier --write"
     ]
   },
-  "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6"
+  "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6",
+  "dependencies": {
+    "html-react-parser": "^5.2.3"
+  }
path: package.json
user: login: ingelinsu
body: done
created_at: 2025-04-23T11:56:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -53,6 +53,7 @@
     "@uiw/react-md-editor": "^4.0.5",
     "date-fns": "^4.1.0",
     "github-markdown-css": "^5.8.1",
+    "html-react-parser": "^5.2.3",
path: plugins/ros/package.json
user: login: ingelinsu
body: done
created_at: 2025-04-23T11:56:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,12 +12,47 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import { consequenceOptions, probabilityOptions } from '../../utils/constants';
 import { formatNOK } from '../../utils/utilityfunctions';
 import { useEstimatedRiskInfoDialogStyles } from './estimatedRiskInfoDialogStyle';
+import parse from 'html-react-parser';
 
 interface EstimatedRiskInfoDialogProps {
   isOpen: boolean;
   onClose: () => void;
 }
 
+const ConsequenceDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index + 3}</sup> = {formatNOK(option)}{' '}
+    {t('infoDialog.consequenceDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.consequenceDescription.${index}`))}</b>
+    <br />
+  </Fragment>
+);
+
+const ProbabilityDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index - 2}</sup> = {option}{' '}
+    {t('infoDialog.probabilityDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.probabilityDescription.${index}`))}</b>
+    <br />
+  </Fragment>
+);
+
path: plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
user: login: ingelinsu
body: done
created_at: 2025-04-23T11:58:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -617,16 +625,23 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: 20<sup>S+K-1</sup> kr/år.',
           'infoDialog.consequenceTitle': 'Konsekvens (kr/hendelse)',
+          'infoDialog.consequenceDescriptionGeneral': 'kr/hendelse',
+          'infoDialog.consequenceDescription.0': '1 dagsverk',
+          'infoDialog.consequenceDescription.1': '1 månedsverk',
+          'infoDialog.consequenceDescription.2': '1 årsverk',
+          'infoDialog.consequenceDescription.3': '50 årsverk',
+          'infoDialog.consequenceDescription.4': '1000 årsverk',
           'infoDialog.probabilityTitle': 'Sannsynlighet (hendelser/år)',
-          'infoDialog.probabilityDescription.0': 'ca hvert 100. år',
-          'infoDialog.probabilityDescription.1': 'ca hvert 10. år',
-          'infoDialog.probabilityDescription.2': 'ca årlig',
-          'infoDialog.probabilityDescription.3': 'ca ukentlig',
-          'infoDialog.probabilityDescription.4': 'ca daglig',
+          'infoDialog.probabilityDescriptionGeneral': 'hendelse/år',
path: plugins/ros/src/utils/translations.ts
user: login: ingelinsu
body: done
created_at: 2025-04-23T14:05:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,9 +21,18 @@ interface RiskProps {
   riskType: 'initialRisk' | 'restRisk';
 }
 
+function calculateCost(risk: Risk, initialRisk: boolean): number {
+  const probability = initialRisk ? risk.probability : risk.probability;
+  const consequence = initialRisk ? risk.consequence : risk.consequence;
+
+  return calculateRiScCost(probability, consequence);
+}
path: plugins/ros/src/components/scenarioDrawer/components/RiskSection.tsx
user: login: ingelinsu
body: removed
created_at: 2025-04-23T14:13:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,9 +21,18 @@ interface RiskProps {
   riskType: 'initialRisk' | 'restRisk';
 }
 
+function calculateCost(risk: Risk, initialRisk: boolean): number {
+  const probability = initialRisk ? risk.probability : risk.probability;
+  const consequence = initialRisk ? risk.consequence : risk.consequence;
+
+  return calculateRiScCost(probability, consequence);
+}
+
 function RiskBox({ risk, riskType }: RiskProps) {
path: plugins/ros/src/components/scenarioDrawer/components/RiskSection.tsx
user: login: ingelinsu
body: usikker på hva du mener her?
created_at: 2025-04-23T14:14:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,9 +21,18 @@ interface RiskProps {
   riskType: 'initialRisk' | 'restRisk';
 }
 
+function calculateCost(risk: Risk, initialRisk: boolean): number {
+  const probability = initialRisk ? risk.probability : risk.probability;
+  const consequence = initialRisk ? risk.consequence : risk.consequence;
+
+  return calculateRiScCost(probability, consequence);
+}
+
 function RiskBox({ risk, riskType }: RiskProps) {
path: plugins/ros/src/components/scenarioDrawer/components/RiskSection.tsx
user: login: emilte
body: Se på hva den er brukt til, den setter kun dymanisk translation. Bedre å bare sende riktig tekst i utgangspunktet
created_at: 2025-04-23T14:20:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -617,16 +625,23 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: 20<sup>S+K-1</sup> kr/år.',
path: plugins/ros/src/utils/translations.ts
user: login: ingelinsu
body: hvordan da tenker du? er ulik formel på norsk og engelsk
created_at: 2025-04-23T14:22:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -617,16 +625,23 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: 20<sup>S+K-1</sup> kr/år.',
           'infoDialog.consequenceTitle': 'Konsekvens (kr/hendelse)',
+          'infoDialog.consequenceDescriptionGeneral': 'kr/hendelse',
+          'infoDialog.consequenceDescription.0': '1 dagsverk',
+          'infoDialog.consequenceDescription.1': '1 månedsverk',
+          'infoDialog.consequenceDescription.2': '1 årsverk',
+          'infoDialog.consequenceDescription.3': '50 årsverk',
+          'infoDialog.consequenceDescription.4': '1000 årsverk',
           'infoDialog.probabilityTitle': 'Sannsynlighet (hendelser/år)',
-          'infoDialog.probabilityDescription.0': 'ca hvert 100. år',
-          'infoDialog.probabilityDescription.1': 'ca hvert 10. år',
-          'infoDialog.probabilityDescription.2': 'ca årlig',
-          'infoDialog.probabilityDescription.3': 'ca ukentlig',
-          'infoDialog.probabilityDescription.4': 'ca daglig',
+          'infoDialog.probabilityDescriptionGeneral': 'hendelse/år',
+          'infoDialog.probabilityDescription.0': '1 gang hvert 400. år',
+          'infoDialog.probabilityDescription.1': '1 gang hvert 20. år',
+          'infoDialog.probabilityDescription.2': 'Årlig',
+          'infoDialog.probabilityDescription.3': 'Månedlig',
+          'infoDialog.probabilityDescription.4': 'Daglig',
           'infoDialog.example':
-            'Et risikoscenario med konsekvens 2 og sannsynlighet 4 har en estimert risiko på 30 000 kr/hendelse x 50 hendelser/år = 1 500 000 kr/år.',
+            'Et risikoscenario med konsekvens 2 (20<sup>4</sup> = 160 000 kr/hendelse) og sannsynlighet 4 (20<sup>1</sup> = 20 hendelser/år) har en estimert risiko på 20<sup>1+4-1</sup> = 160 000 kr/år.',
path: plugins/ros/src/utils/translations.ts
user: login: ingelinsu
body: hvordan ville man gjort det i translations-filen?
created_at: 2025-04-23T14:22:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,12 +12,47 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import { consequenceOptions, probabilityOptions } from '../../utils/constants';
 import { formatNOK } from '../../utils/utilityfunctions';
 import { useEstimatedRiskInfoDialogStyles } from './estimatedRiskInfoDialogStyle';
+import parse from 'html-react-parser';
 
 interface EstimatedRiskInfoDialogProps {
   isOpen: boolean;
   onClose: () => void;
 }
 
+const ConsequenceDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index + 3}</sup> = {formatNOK(option)}{' '}
+    {t('infoDialog.consequenceDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.consequenceDescription.${index}`))}</b>
path: plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
user: login: ingelinsu
body: har du et forslag?
created_at: 2025-04-23T14:22:57Z
author_association: CONTRIBUTOR
path: plugins/ros/src/risc_schema_en_v4_1.json
user: login: ingelinsu
body: den bruker gamle tall - virker som den sammenligner med de tidligere tallene - tanken var også at man kan trekke ut verdiene fra schema, men ikke fått til 
created_at: 2025-04-23T14:23:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,12 +12,47 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import { consequenceOptions, probabilityOptions } from '../../utils/constants';
 import { formatNOK } from '../../utils/utilityfunctions';
 import { useEstimatedRiskInfoDialogStyles } from './estimatedRiskInfoDialogStyle';
+import parse from 'html-react-parser';
 
 interface EstimatedRiskInfoDialogProps {
   isOpen: boolean;
   onClose: () => void;
 }
 
+const ConsequenceDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index + 3}</sup> = {formatNOK(option)}{' '}
+    {t('infoDialog.consequenceDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.consequenceDescription.${index}`))}</b>
path: plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
user: login: emilte
body: Nøklene kan beskrive hva de faktisk er 
```
infoDialog.probabilityDescription.4': 'Daglig'
vs
infoDialog.probabilityDaily': 'Daglig'
```

created_at: 2025-04-23T21:32:57Z
author_association: CONTRIBUTOR
path: plugins/ros/src/risc_schema_en_v4_1.json
user: login: emilte
body: Du må nok endre import i `plugins/ros/src/utils/constants.ts`
created_at: 2025-04-23T21:37:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -617,16 +625,23 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: 20<sup>S+K-1</sup> kr/år.',
path: plugins/ros/src/utils/translations.ts
user: login: emilte
body: Den kunne vært splittet i tre deler for å unngå html i translation og dangerouslySetInnerHTML.
created_at: 2025-04-23T21:41:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -617,16 +625,23 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: 20<sup>S+K-1</sup> kr/år.',
           'infoDialog.consequenceTitle': 'Konsekvens (kr/hendelse)',
+          'infoDialog.consequenceDescriptionGeneral': 'kr/hendelse',
+          'infoDialog.consequenceDescription.0': '1 dagsverk',
+          'infoDialog.consequenceDescription.1': '1 månedsverk',
+          'infoDialog.consequenceDescription.2': '1 årsverk',
+          'infoDialog.consequenceDescription.3': '50 årsverk',
+          'infoDialog.consequenceDescription.4': '1000 årsverk',
           'infoDialog.probabilityTitle': 'Sannsynlighet (hendelser/år)',
-          'infoDialog.probabilityDescription.0': 'ca hvert 100. år',
-          'infoDialog.probabilityDescription.1': 'ca hvert 10. år',
-          'infoDialog.probabilityDescription.2': 'ca årlig',
-          'infoDialog.probabilityDescription.3': 'ca ukentlig',
-          'infoDialog.probabilityDescription.4': 'ca daglig',
+          'infoDialog.probabilityDescriptionGeneral': 'hendelse/år',
+          'infoDialog.probabilityDescription.0': '1 gang hvert 400. år',
+          'infoDialog.probabilityDescription.1': '1 gang hvert 20. år',
+          'infoDialog.probabilityDescription.2': 'Årlig',
+          'infoDialog.probabilityDescription.3': 'Månedlig',
+          'infoDialog.probabilityDescription.4': 'Daglig',
           'infoDialog.example':
-            'Et risikoscenario med konsekvens 2 og sannsynlighet 4 har en estimert risiko på 30 000 kr/hendelse x 50 hendelser/år = 1 500 000 kr/år.',
+            'Et risikoscenario med konsekvens 2 (20<sup>4</sup> = 160 000 kr/hendelse) og sannsynlighet 4 (20<sup>1</sup> = 20 hendelser/år) har en estimert risiko på 20<sup>1+4-1</sup> = 160 000 kr/år.',
path: plugins/ros/src/utils/translations.ts
user: login: emilte
body: Tenkte mer på der man injecter den
created_at: 2025-04-23T21:41:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,12 +12,47 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import { consequenceOptions, probabilityOptions } from '../../utils/constants';
 import { formatNOK } from '../../utils/utilityfunctions';
 import { useEstimatedRiskInfoDialogStyles } from './estimatedRiskInfoDialogStyle';
+import parse from 'html-react-parser';
 
 interface EstimatedRiskInfoDialogProps {
   isOpen: boolean;
   onClose: () => void;
 }
 
+const ConsequenceDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index + 3}</sup> = {formatNOK(option)}{' '}
+    {t('infoDialog.consequenceDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.consequenceDescription.${index}`))}</b>
path: plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
user: login: emilte
body: Du kan til og med mappe fra de ekte verdiene til t-keys, det tror jeg hadde være smud
created_at: 2025-04-23T21:43:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -617,16 +625,23 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: 20<sup>S+K-1</sup> kr/år.',
path: plugins/ros/src/utils/translations.ts
user: login: ingelinsu
body: done
created_at: 2025-04-24T11:24:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -617,16 +625,23 @@ export const pluginRiScNorwegianTranslation = createTranslationResource({
           'infoDialog.calculatedHowTitle':
             'Hvordan regner vi ut estimert risiko?',
           'infoDialog.calculatedHow':
-            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: K x S.',
+            'Konsekvens måles i kroner per hendelse og sannsynlighet måles i hendelser per år. Den estimerte risikoen blir da: 20<sup>S+K-1</sup> kr/år.',
           'infoDialog.consequenceTitle': 'Konsekvens (kr/hendelse)',
+          'infoDialog.consequenceDescriptionGeneral': 'kr/hendelse',
+          'infoDialog.consequenceDescription.0': '1 dagsverk',
+          'infoDialog.consequenceDescription.1': '1 månedsverk',
+          'infoDialog.consequenceDescription.2': '1 årsverk',
+          'infoDialog.consequenceDescription.3': '50 årsverk',
+          'infoDialog.consequenceDescription.4': '1000 årsverk',
           'infoDialog.probabilityTitle': 'Sannsynlighet (hendelser/år)',
-          'infoDialog.probabilityDescription.0': 'ca hvert 100. år',
-          'infoDialog.probabilityDescription.1': 'ca hvert 10. år',
-          'infoDialog.probabilityDescription.2': 'ca årlig',
-          'infoDialog.probabilityDescription.3': 'ca ukentlig',
-          'infoDialog.probabilityDescription.4': 'ca daglig',
+          'infoDialog.probabilityDescriptionGeneral': 'hendelse/år',
+          'infoDialog.probabilityDescription.0': '1 gang hvert 400. år',
+          'infoDialog.probabilityDescription.1': '1 gang hvert 20. år',
+          'infoDialog.probabilityDescription.2': 'Årlig',
+          'infoDialog.probabilityDescription.3': 'Månedlig',
+          'infoDialog.probabilityDescription.4': 'Daglig',
           'infoDialog.example':
-            'Et risikoscenario med konsekvens 2 og sannsynlighet 4 har en estimert risiko på 30 000 kr/hendelse x 50 hendelser/år = 1 500 000 kr/år.',
+            'Et risikoscenario med konsekvens 2 (20<sup>4</sup> = 160 000 kr/hendelse) og sannsynlighet 4 (20<sup>1</sup> = 20 hendelser/år) har en estimert risiko på 20<sup>1+4-1</sup> = 160 000 kr/år.',
path: plugins/ros/src/utils/translations.ts
user: login: ingelinsu
body: done
created_at: 2025-04-24T11:24:36Z
author_association: CONTRIBUTOR
path: plugins/ros/src/risc_schema_en_v4_1.json
user: login: emilte
body: Ser ut som Backend også må oppdateres
created_at: 2025-04-24T14:18:46Z
author_association: CONTRIBUTOR
path: plugins/ros/src/risc_schema_en_v4_1.json
user: login: ingelinsu
body: yup - lagd en draft PR
created_at: 2025-04-24T14:40:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,3 +349,29 @@ export function isDeeplyEqual<T>(
   // If a or b is null or a primitive type, then they are not equal as a === b should have returned true already.
   return false;
 }
+
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.ceil(logBase(consequence, BASE_NUMBER) - 2 - 1);
+  return Math.min(4, Math.max(0, consequenceIndex));
+}
+
+export function calculateCost(
+  probability: number,
+  consequence: number,
+): number {
+  const probabilityLevel = findProbabilityIndex(probability) + 1;
+  const consequenceLevel = findConsequenceIndex(consequence) + 1;
+
+  return Math.pow(20, probabilityLevel + consequenceLevel - 1);
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: emilte
body: ```suggestion
  return Math.pow(BASE_NUMBER, probabilityLevel + consequenceLevel - 1);
```
created_at: 2025-05-02T08:48:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,3 +349,29 @@ export function isDeeplyEqual<T>(
   // If a or b is null or a primitive type, then they are not equal as a === b should have returned true already.
   return false;
 }
+
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.ceil(logBase(consequence, BASE_NUMBER) - 2 - 1);
+  return Math.min(4, Math.max(0, consequenceIndex));
+}
+
+export function calculateCost(
+  probability: number,
+  consequence: number,
+): number {
+  const probabilityLevel = findProbabilityIndex(probability) + 1;
+  const consequenceLevel = findConsequenceIndex(consequence) + 1;
+
+  return Math.pow(20, probabilityLevel + consequenceLevel - 1);
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: ingelinsu
body: nice, catch!

created_at: 2025-05-02T09:04:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,3 +349,29 @@ export function isDeeplyEqual<T>(
   // If a or b is null or a primitive type, then they are not equal as a === b should have returned true already.
   return false;
 }
+
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.ceil(logBase(consequence, BASE_NUMBER) - 2 - 1);
+  return Math.min(4, Math.max(0, consequenceIndex));
+}
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: halvardhummel
body: Dette her burde dokumenteres. Jeg skjønner, etter å ha sjekket alle verdiene fra versjon 4.0, at `ceil` brukes for konsekvenser og `round` brukes for sannsynlighet slik at verdiene fra versjon 4.0 mappes til samme verdi (1-5) som tidligere. Men, det er ikke veldig intuitivt at dette er grunnen til at koden er sånn.
created_at: 2025-05-02T09:07:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,3 +349,29 @@ export function isDeeplyEqual<T>(
   // If a or b is null or a primitive type, then they are not equal as a === b should have returned true already.
   return false;
 }
+
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.ceil(logBase(consequence, BASE_NUMBER) - 2 - 1);
+  return Math.min(4, Math.max(0, consequenceIndex));
+}
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: emilte
body: Litt forklaringer hadde vært nice
created_at: 2025-05-02T10:44:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -19,6 +19,55 @@ import {
 } from '../scenarioDrawerComponents';
 import IconButton from '@mui/material/IconButton';
 import InfoIcon from '@mui/icons-material/Info';
+import {
+  findProbabilityIndex,
+  findConsequenceIndex,
+} from '../../../utils/utilityfunctions';
+
+const createValues = (
path: plugins/ros/src/components/scenarioDrawer/components/RiskFormSection.tsx
user: login: emilte
body: Gjerne bruk function
created_at: 2025-05-02T11:05:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -19,6 +19,55 @@ import {
 } from '../scenarioDrawerComponents';
 import IconButton from '@mui/material/IconButton';
 import InfoIcon from '@mui/icons-material/Info';
+import {
+  findProbabilityIndex,
+  findConsequenceIndex,
+} from '../../../utils/utilityfunctions';
+
+const createValues = (
path: plugins/ros/src/components/scenarioDrawer/components/RiskFormSection.tsx
user: login: ingelinsu
body: done
created_at: 2025-05-02T12:49:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -358,6 +357,70 @@ export function isDeeplyEqual<T>(
   return false;
 }
 
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+/*
+  Probability is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^-2 = 1 / 400
+  1 = 20^-1 = 1 / 20
+  2 = 20^-0 = 1
+  3 = 20^1  = 20
+  4 = 20^2  = 400
+*/
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: halvardhummel
body: ```suggestion
    logBase(probability, BASE_NUMBER) + 2,
```
created_at: 2025-05-12T08:39:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -358,6 +357,70 @@ export function isDeeplyEqual<T>(
   return false;
 }
 
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+/*
+  Probability is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^-2 = 1 / 400
+  1 = 20^-1 = 1 / 20
+  2 = 20^-0 = 1
+  3 = 20^1  = 20
+  4 = 20^2  = 400
+*/
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+/*
+  Consequence  is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^3 = 8 000
+  1 = 20^4 = 160 000
+  2 = 20^5 = 3 200 000
+  3 = 20^6 = 64 000 000
+  4 = 20^7 = 1 280 000 000
+*/
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.round(
+    logBase(consequence, BASE_NUMBER) - 2 - 1,
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: halvardhummel
body: ```suggestion
    logBase(consequence, BASE_NUMBER) - 3,
```
created_at: 2025-05-12T08:39:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -358,6 +357,70 @@ export function isDeeplyEqual<T>(
   return false;
 }
 
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+/*
+  Probability is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^-2 = 1 / 400
+  1 = 20^-1 = 1 / 20
+  2 = 20^-0 = 1
+  3 = 20^1  = 20
+  4 = 20^2  = 400
+*/
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+/*
+  Consequence  is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^3 = 8 000
+  1 = 20^4 = 160 000
+  2 = 20^5 = 3 200 000
+  3 = 20^6 = 64 000 000
+  4 = 20^7 = 1 280 000 000
+*/
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.round(
+    logBase(consequence, BASE_NUMBER) - 2 - 1,
+  );
+  return Math.min(4, Math.max(0, consequenceIndex));
+}
+
+/*
+  Cost is calculated as probability in events per year multiplied by consequence in NOK per event.
+  The unit of cost is NOK per year.
+*/
+export function calculateCost(
+  probability: number,
+  consequence: number,
+): number {
+  const probabilityLevel = findProbabilityIndex(probability) + 1;
+  const consequenceLevel = findConsequenceIndex(consequence) + 1;
+
+  return Math.pow(BASE_NUMBER, probabilityLevel + consequenceLevel - 1);
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: halvardhummel
body: Skjønner ikke hva som er tanken med å legge til med 1 her og så trekke fra 1?

```suggestion
  const probabilityLevel = findProbabilityIndex(probability);
  const consequenceLevel = findConsequenceIndex(consequence);

  return Math.pow(BASE_NUMBER, probabilityLevel + consequenceLevel + 1);
```
created_at: 2025-05-12T08:48:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -358,6 +357,70 @@ export function isDeeplyEqual<T>(
   return false;
 }
 
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+/*
+  Probability is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^-2 = 1 / 400
+  1 = 20^-1 = 1 / 20
+  2 = 20^-0 = 1
+  3 = 20^1  = 20
+  4 = 20^2  = 400
+*/
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+/*
+  Consequence  is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^3 = 8 000
+  1 = 20^4 = 160 000
+  2 = 20^5 = 3 200 000
+  3 = 20^6 = 64 000 000
+  4 = 20^7 = 1 280 000 000
+*/
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.round(
+    logBase(consequence, BASE_NUMBER) - 2 - 1,
+  );
+  return Math.min(4, Math.max(0, consequenceIndex));
+}
+
+/*
+  Cost is calculated as probability in events per year multiplied by consequence in NOK per event.
+  The unit of cost is NOK per year.
+*/
+export function calculateCost(
+  probability: number,
+  consequence: number,
+): number {
+  const probabilityLevel = findProbabilityIndex(probability) + 1;
+  const consequenceLevel = findConsequenceIndex(consequence) + 1;
+
+  return Math.pow(BASE_NUMBER, probabilityLevel + consequenceLevel - 1);
+}
+
+export const consequenceIndexToTranslationKeys: Record<number, string> = {
+  0: 'infoDialog.consequenceDescription.oneworkday',
+  1: 'infoDialog.consequenceDescription.oneworkmonth',
+  2: 'infoDialog.consequenceDescription.oneworkyear',
+  3: 'infoDialog.consequenceDescription.50workyears',
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: halvardhummel
body: Hvordan kommer vi oss fra 1 til 50, når grunntallet er 20?
created_at: 2025-05-12T08:49:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -377,10 +389,10 @@ export const pluginRiScMessages = {
       '5': 'Very high', // svært stor
     },
     cells: {
-      '1': 'The scenario is unlikely to occur.\n\nIt occurs less often than every 100 years',
-      '2': 'The scenario is unlikely to occur.\n\nIt can occur every 10 years',
+      '1': 'The scenario is unlikely to occur.\n\nIt occurs less often than every 400 years',
+      '2': 'The scenario is unlikely to occur.\n\nIt can occur every 40 years',
       '3': 'The scenario can occur.\n\nIt can occur almost every year',
path: plugins/ros/src/utils/translations.ts
user: login: halvardhummel
body: Hvordan går vi fra 1 til 40 til 400 når grunntallet er 20? Skal vel være 1 -> 20 -> 400.
created_at: 2025-05-12T08:51:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -377,10 +389,10 @@ export const pluginRiScMessages = {
       '5': 'Very high', // svært stor
     },
     cells: {
-      '1': 'The scenario is unlikely to occur.\n\nIt occurs less often than every 100 years',
-      '2': 'The scenario is unlikely to occur.\n\nIt can occur every 10 years',
+      '1': 'The scenario is unlikely to occur.\n\nIt occurs less often than every 400 years',
+      '2': 'The scenario is unlikely to occur.\n\nIt can occur every 40 years',
       '3': 'The scenario can occur.\n\nIt can occur almost every year',
-      '4': 'The scenario will very likely occur.\n\nIt can occur almost weekly',
+      '4': 'The scenario will very likely occur.\n\nIt can occur almost monthly',
path: plugins/ros/src/utils/translations.ts
user: login: halvardhummel
body: 20 ganger i året er mer enn månedlig, ikke nesten månedlig.

```suggestion
      '4': 'The scenario will very likely occur.\n\nIt can occur monthly',
```
created_at: 2025-05-12T08:52:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -346,11 +358,11 @@ export const pluginRiScMessages = {
         '5': 'Death or permanent severe health implications for many people. Severe damage to the environment across large areas.',
       },
       economical: {
-        '1': 'Temporary minor financial loss.\n\nMay be fixed within an hour by one employee',
-        '2': 'Temporary financial loss.\n\nMay be fixed within three days by one employee',
-        '3': 'Financial loss of some duration.\n\nMay be fixed within a month by three employees',
-        '4': 'Financial loss of considerable duration for the organization and any third parties.\n\nMay be fixed within a year by 10 employees',
-        '5': 'Permanent and severe financial loss.\n\nMay be fixed within three years by 100 employees',
+        '1': 'Temporary minor financial loss.\n\nMay be fixed with 1 workday.',
+        '2': 'Temporary financial loss.\n\nMay be fixed with 1 work month.',
+        '3': 'Financial loss of some duration.\n\nMay be fixed with 1 work year.',
+        '4': 'Financial loss of considerable duration for the organization and any third parties.\n\nMay be fixed with 50 work years.',
+        '5': 'Permanent and severe financial loss.\n\nMay be fixed with 1000 work years.',
path: plugins/ros/src/utils/translations.ts
user: login: halvardhummel
body: Hvordan går vi fra 1 til 50 til 1000 årsverk her? Bør vel være 1 til 20 til 400 årsverk?
created_at: 2025-05-12T08:53:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,12 +22,16 @@ export function AggregatedCost({ riSc, initialRisk }: AggregatedCostProps) {
   const [showDialog, setShowDialog] = useState(false);
 
   const cost = riSc.scenarios
-    .map(scenario =>
-      initialRisk
-        ? scenario.risk.probability * scenario.risk.consequence
-        : scenario.remainingRisk.probability *
-          scenario.remainingRisk.consequence,
-    )
+    .map(scenario => {
+      const probability = initialRisk
+        ? scenario.risk.probability
+        : scenario.remainingRisk.probability;
+      const consequence = initialRisk
+        ? scenario.risk.consequence
+        : scenario.remainingRisk.consequence;
+
+      return calculateCost(probability, consequence);
+    })
path: plugins/ros/src/components/riskMatrix/AggregatedCost.tsx
user: login: halvardhummel
body: Jeg er litt bekymret for denne samlede utregningen. Den ser ut til å ignorere de eksisterende verdiene i skjemaet og runde disse av?
created_at: 2025-05-12T09:08:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,24 +1,75 @@
-import { UseFormReturn } from 'react-hook-form';
-import { FormScenario } from '../../../utils/types';
-import { pluginRiScTranslationRef } from '../../../utils/translations';
 import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
+import InfoIcon from '@mui/icons-material/Info';
+import Box from '@mui/material/Box';
+import IconButton from '@mui/material/IconButton';
+import Paper from '@mui/material/Paper';
+import Typography from '@mui/material/Typography';
+import { UseFormReturn } from 'react-hook-form';
 import {
   consequenceOptions,
   probabilityOptions,
 } from '../../../utils/constants';
+import { pluginRiScTranslationRef } from '../../../utils/translations';
+import { FormScenario } from '../../../utils/types';
+import {
+  consequenceIndexToTranslationKeys,
+  findConsequenceIndex,
+  findProbabilityIndex,
+  probabilityIndexToTranslationKeys,
+} from '../../../utils/utilityfunctions';
 import { Select } from '../../common/Select';
-import Box from '@mui/material/Box';
-import Paper from '@mui/material/Paper';
-import Typography from '@mui/material/Typography';
 import { heading3 } from '../../common/typography';
 import {
   headerSection,
   riscSection,
   section,
   selectSection,
 } from '../scenarioDrawerComponents';
-import IconButton from '@mui/material/IconButton';
-import InfoIcon from '@mui/icons-material/Info';
+
path: plugins/ros/src/components/scenarioDrawer/components/RiskFormSection.tsx
user: login: halvardhummel
body: Hele oppsettet her med `createValues` og hvordan `options` og `value` regnes fremstår som mye mer komplekst enn det trenger å være. Er det mulig å forenkle dette? Jeg vil for eksempel tro at koden blir ryddigere om man introduserer en komponent for alt som ligger i `renderedValue`. I tillegg burde det være mulig å forenkle hvordan `options` konstrueres. Funksjonaliteten nå er vel ikke veldig langt unna det den var før endringen.
created_at: 2025-05-12T09:24:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -346,11 +358,11 @@ export const pluginRiScMessages = {
         '5': 'Death or permanent severe health implications for many people. Severe damage to the environment across large areas.',
       },
       economical: {
-        '1': 'Temporary minor financial loss.\n\nMay be fixed within an hour by one employee',
-        '2': 'Temporary financial loss.\n\nMay be fixed within three days by one employee',
-        '3': 'Financial loss of some duration.\n\nMay be fixed within a month by three employees',
-        '4': 'Financial loss of considerable duration for the organization and any third parties.\n\nMay be fixed within a year by 10 employees',
-        '5': 'Permanent and severe financial loss.\n\nMay be fixed within three years by 100 employees',
+        '1': 'Temporary minor financial loss.\n\nMay be fixed with 1 workday.',
+        '2': 'Temporary financial loss.\n\nMay be fixed with 1 work month.',
+        '3': 'Financial loss of some duration.\n\nMay be fixed with 1 work year.',
+        '4': 'Financial loss of considerable duration for the organization and any third parties.\n\nMay be fixed with 50 work years.',
+        '5': 'Permanent and severe financial loss.\n\nMay be fixed with 1000 work years.',
path: plugins/ros/src/utils/translations.ts
user: login: AndersHR
body: Her er det de gamle verdiene som er brukt. 1 -> 20 -> 400 er riktig ja
created_at: 2025-05-13T07:51:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,12 +22,16 @@ export function AggregatedCost({ riSc, initialRisk }: AggregatedCostProps) {
   const [showDialog, setShowDialog] = useState(false);
 
   const cost = riSc.scenarios
-    .map(scenario =>
-      initialRisk
-        ? scenario.risk.probability * scenario.risk.consequence
-        : scenario.remainingRisk.probability *
-          scenario.remainingRisk.consequence,
-    )
+    .map(scenario => {
+      const probability = initialRisk
+        ? scenario.risk.probability
+        : scenario.remainingRisk.probability;
+      const consequence = initialRisk
+        ? scenario.risk.consequence
+        : scenario.remainingRisk.consequence;
+
+      return calculateCost(probability, consequence);
+    })
path: plugins/ros/src/components/riskMatrix/AggregatedCost.tsx
user: login: AndersHR
body: Ja denne blir feil. Jeg bruker nå direkte cost = probability * consequence ✅ 
created_at: 2025-05-13T13:05:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,24 +1,75 @@
-import { UseFormReturn } from 'react-hook-form';
-import { FormScenario } from '../../../utils/types';
-import { pluginRiScTranslationRef } from '../../../utils/translations';
 import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
+import InfoIcon from '@mui/icons-material/Info';
+import Box from '@mui/material/Box';
+import IconButton from '@mui/material/IconButton';
+import Paper from '@mui/material/Paper';
+import Typography from '@mui/material/Typography';
+import { UseFormReturn } from 'react-hook-form';
 import {
   consequenceOptions,
   probabilityOptions,
 } from '../../../utils/constants';
+import { pluginRiScTranslationRef } from '../../../utils/translations';
+import { FormScenario } from '../../../utils/types';
+import {
+  consequenceIndexToTranslationKeys,
+  findConsequenceIndex,
+  findProbabilityIndex,
+  probabilityIndexToTranslationKeys,
+} from '../../../utils/utilityfunctions';
 import { Select } from '../../common/Select';
-import Box from '@mui/material/Box';
-import Paper from '@mui/material/Paper';
-import Typography from '@mui/material/Typography';
 import { heading3 } from '../../common/typography';
 import {
   headerSection,
   riscSection,
   section,
   selectSection,
 } from '../scenarioDrawerComponents';
-import IconButton from '@mui/material/IconButton';
-import InfoIcon from '@mui/icons-material/Info';
+
path: plugins/ros/src/components/scenarioDrawer/components/RiskFormSection.tsx
user: login: AndersHR
body: Jeg har prøvd å forenkle dette nå ✅ 
created_at: 2025-05-13T13:05:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -346,11 +358,11 @@ export const pluginRiScMessages = {
         '5': 'Death or permanent severe health implications for many people. Severe damage to the environment across large areas.',
       },
       economical: {
-        '1': 'Temporary minor financial loss.\n\nMay be fixed within an hour by one employee',
-        '2': 'Temporary financial loss.\n\nMay be fixed within three days by one employee',
-        '3': 'Financial loss of some duration.\n\nMay be fixed within a month by three employees',
-        '4': 'Financial loss of considerable duration for the organization and any third parties.\n\nMay be fixed within a year by 10 employees',
-        '5': 'Permanent and severe financial loss.\n\nMay be fixed within three years by 100 employees',
+        '1': 'Temporary minor financial loss.\n\nMay be fixed with 1 workday.',
+        '2': 'Temporary financial loss.\n\nMay be fixed with 1 work month.',
+        '3': 'Financial loss of some duration.\n\nMay be fixed with 1 work year.',
+        '4': 'Financial loss of considerable duration for the organization and any third parties.\n\nMay be fixed with 50 work years.',
+        '5': 'Permanent and severe financial loss.\n\nMay be fixed with 1000 work years.',
path: plugins/ros/src/utils/translations.ts
user: login: AndersHR
body: ✅ 
created_at: 2025-05-13T13:05:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -377,10 +389,10 @@ export const pluginRiScMessages = {
       '5': 'Very high', // svært stor
     },
     cells: {
-      '1': 'The scenario is unlikely to occur.\n\nIt occurs less often than every 100 years',
-      '2': 'The scenario is unlikely to occur.\n\nIt can occur every 10 years',
+      '1': 'The scenario is unlikely to occur.\n\nIt occurs less often than every 400 years',
+      '2': 'The scenario is unlikely to occur.\n\nIt can occur every 40 years',
       '3': 'The scenario can occur.\n\nIt can occur almost every year',
-      '4': 'The scenario will very likely occur.\n\nIt can occur almost weekly',
+      '4': 'The scenario will very likely occur.\n\nIt can occur almost monthly',
path: plugins/ros/src/utils/translations.ts
user: login: AndersHR
body: ✅ 
created_at: 2025-05-13T13:05:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -377,10 +389,10 @@ export const pluginRiScMessages = {
       '5': 'Very high', // svært stor
     },
     cells: {
-      '1': 'The scenario is unlikely to occur.\n\nIt occurs less often than every 100 years',
-      '2': 'The scenario is unlikely to occur.\n\nIt can occur every 10 years',
+      '1': 'The scenario is unlikely to occur.\n\nIt occurs less often than every 400 years',
+      '2': 'The scenario is unlikely to occur.\n\nIt can occur every 40 years',
       '3': 'The scenario can occur.\n\nIt can occur almost every year',
path: plugins/ros/src/utils/translations.ts
user: login: AndersHR
body: ✅ 
created_at: 2025-05-13T13:05:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -358,6 +357,70 @@ export function isDeeplyEqual<T>(
   return false;
 }
 
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+/*
+  Probability is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^-2 = 1 / 400
+  1 = 20^-1 = 1 / 20
+  2 = 20^-0 = 1
+  3 = 20^1  = 20
+  4 = 20^2  = 400
+*/
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+/*
+  Consequence  is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^3 = 8 000
+  1 = 20^4 = 160 000
+  2 = 20^5 = 3 200 000
+  3 = 20^6 = 64 000 000
+  4 = 20^7 = 1 280 000 000
+*/
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.round(
+    logBase(consequence, BASE_NUMBER) - 2 - 1,
+  );
+  return Math.min(4, Math.max(0, consequenceIndex));
+}
+
+/*
+  Cost is calculated as probability in events per year multiplied by consequence in NOK per event.
+  The unit of cost is NOK per year.
+*/
+export function calculateCost(
+  probability: number,
+  consequence: number,
+): number {
+  const probabilityLevel = findProbabilityIndex(probability) + 1;
+  const consequenceLevel = findConsequenceIndex(consequence) + 1;
+
+  return Math.pow(BASE_NUMBER, probabilityLevel + consequenceLevel - 1);
+}
+
+export const consequenceIndexToTranslationKeys: Record<number, string> = {
+  0: 'infoDialog.consequenceDescription.oneworkday',
+  1: 'infoDialog.consequenceDescription.oneworkmonth',
+  2: 'infoDialog.consequenceDescription.oneworkyear',
+  3: 'infoDialog.consequenceDescription.50workyears',
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: AndersHR
body: ✅ 
created_at: 2025-05-13T13:05:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -358,6 +357,70 @@ export function isDeeplyEqual<T>(
   return false;
 }
 
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+/*
+  Probability is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^-2 = 1 / 400
+  1 = 20^-1 = 1 / 20
+  2 = 20^-0 = 1
+  3 = 20^1  = 20
+  4 = 20^2  = 400
+*/
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+/*
+  Consequence  is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^3 = 8 000
+  1 = 20^4 = 160 000
+  2 = 20^5 = 3 200 000
+  3 = 20^6 = 64 000 000
+  4 = 20^7 = 1 280 000 000
+*/
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.round(
+    logBase(consequence, BASE_NUMBER) - 2 - 1,
+  );
+  return Math.min(4, Math.max(0, consequenceIndex));
+}
+
+/*
+  Cost is calculated as probability in events per year multiplied by consequence in NOK per event.
+  The unit of cost is NOK per year.
+*/
+export function calculateCost(
+  probability: number,
+  consequence: number,
+): number {
+  const probabilityLevel = findProbabilityIndex(probability) + 1;
+  const consequenceLevel = findConsequenceIndex(consequence) + 1;
+
+  return Math.pow(BASE_NUMBER, probabilityLevel + consequenceLevel - 1);
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: AndersHR
body: ✅ 
created_at: 2025-05-13T13:05:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -358,6 +357,70 @@ export function isDeeplyEqual<T>(
   return false;
 }
 
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+/*
+  Probability is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^-2 = 1 / 400
+  1 = 20^-1 = 1 / 20
+  2 = 20^-0 = 1
+  3 = 20^1  = 20
+  4 = 20^2  = 400
+*/
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+/*
+  Consequence  is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^3 = 8 000
+  1 = 20^4 = 160 000
+  2 = 20^5 = 3 200 000
+  3 = 20^6 = 64 000 000
+  4 = 20^7 = 1 280 000 000
+*/
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.round(
+    logBase(consequence, BASE_NUMBER) - 2 - 1,
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: AndersHR
body: Tanken var nok å gjøre det tydeligere hva de ulike "nivåene" er (1 = 20^3 - 5 = 20^7) og deretter at her tar vi _indeksen_ = nivå - 1

Jeg bare endret det til - 3 nå ✅
created_at: 2025-05-13T13:07:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -358,6 +357,70 @@ export function isDeeplyEqual<T>(
   return false;
 }
 
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+/*
+  Probability is categorized in 5 levels on a logarithmic scale with base 20:
+  0 = 20^-2 = 1 / 400
+  1 = 20^-1 = 1 / 20
+  2 = 20^-0 = 1
+  3 = 20^1  = 20
+  4 = 20^2  = 400
+*/
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: AndersHR
body: ✅ 
created_at: 2025-05-13T13:07:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -354,3 +349,29 @@ export function isDeeplyEqual<T>(
   // If a or b is null or a primitive type, then they are not equal as a === b should have returned true already.
   return false;
 }
+
+function logBase(value: number, base: number): number {
+  return Math.log(value) / Math.log(base);
+}
+
+export function findProbabilityIndex(probability: number): number {
+  const probabilityIndex = Math.round(
+    logBase(probability, BASE_NUMBER) + 3 - 1,
+  );
+  return Math.min(4, Math.max(0, probabilityIndex));
+}
+
+export function findConsequenceIndex(consequence: number): number {
+  const consequenceIndex = Math.ceil(logBase(consequence, BASE_NUMBER) - 2 - 1);
+  return Math.min(4, Math.max(0, consequenceIndex));
+}
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: AndersHR
body: ✅ 
created_at: 2025-05-13T13:07:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -40,17 +39,17 @@ export function getAlertSeverity(
 }
 
 export function getRiskMatrixColor(risiko: Risk) {
-  const sannsynlighet = probabilityOptions.indexOf(risiko.probability);
-  const konsekvens = consequenceOptions.indexOf(risiko.consequence);
+  const sannsynlighet = findProbabilityIndex(risiko.probability);
+  const konsekvens = findConsequenceIndex(risiko.consequence);
   return riskMatrix[4 - konsekvens][sannsynlighet];
path: plugins/ros/src/utils/utilityfunctions.ts
user: login: halvardhummel
body: ```suggestion
  const probability = findProbabilityIndex(risiko.probability);
  const consequence = findConsequenceIndex(risiko.consequence);
  return riskMatrix[4 - consequence][probability];
```
created_at: 2025-05-14T08:59:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -582,3 +582,23 @@ describe('Translation mappings match translation keys', () => {
     });
   });
 });
+
path: plugins/ros/src/utils/utilityfunctions.test.ts
user: login: halvardhummel
body: Kanskje vi burde ha tester for de nye metodene i `utilityfunctions`?
created_at: 2025-05-14T09:00:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,10 +1,16 @@
-export const schemaPath = '../risc_schema_en_v4_0.json';
+export const schemaPath = '../risc_schema_en_v4_1.json';
 
-export const latestSupportedVersion = '4.0';
+export const latestSupportedVersion = '4.1';
 
-export const consequenceOptions = [1000, 30000, 1000000, 30000000, 1000000000];
+export const BASE_NUMBER = 20.0;
 
-export const probabilityOptions = [0.01, 0.1, 1, 50, 300];
+export const consequenceOptions = Array.from({ length: 5 }, (_, i) =>
+  Math.pow(BASE_NUMBER, i + 3),
+);
+
+export const probabilityOptions = Array.from({ length: 5 }, (_, i) =>
+  Math.pow(BASE_NUMBER, i - 2),
+);
path: plugins/ros/src/utils/constants.ts
user: login: halvardhummel
body: Kanskje vi burde dra `+ 3` og `- 2` ut i egne konstanter? Disse brukes flere plasser (i `utilityfunctions.ts` og `EstimatedRiskInfoDialog.tsx` så vidt jeg kan se) og blir litt magiske konstanter uten noen forklaring der de brukes. Om vi endrer på skalaen på et senere tidspunkt, må disse kanskje oppdateres og man må huske å endre alle plassene der de brukes. Om vi hadde definert, for eksempel, `CONSEQUENCE_SCALE_OFFSET = 3` og `PROBABILITY_SCALE_OFFSET = -2`, tror jeg det hadde gjort koden mer lesbar og vedlikeholdbar.
created_at: 2025-05-14T09:10:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -582,3 +582,23 @@ describe('Translation mappings match translation keys', () => {
     });
   });
 });
+
path: plugins/ros/src/utils/utilityfunctions.test.ts
user: login: AndersHR
body: Done ✅ 
created_at: 2025-05-14T10:32:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,10 +1,16 @@
-export const schemaPath = '../risc_schema_en_v4_0.json';
+export const schemaPath = '../risc_schema_en_v4_1.json';
 
-export const latestSupportedVersion = '4.0';
+export const latestSupportedVersion = '4.1';
 
-export const consequenceOptions = [1000, 30000, 1000000, 30000000, 1000000000];
+export const BASE_NUMBER = 20.0;
 
-export const probabilityOptions = [0.01, 0.1, 1, 50, 300];
+export const consequenceOptions = Array.from({ length: 5 }, (_, i) =>
+  Math.pow(BASE_NUMBER, i + 3),
+);
+
+export const probabilityOptions = Array.from({ length: 5 }, (_, i) =>
+  Math.pow(BASE_NUMBER, i - 2),
+);
path: plugins/ros/src/utils/constants.ts
user: login: AndersHR
body: Done ✅ 
created_at: 2025-05-14T10:32:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,12 +12,47 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
 import { consequenceOptions, probabilityOptions } from '../../utils/constants';
 import { formatNOK } from '../../utils/utilityfunctions';
 import { useEstimatedRiskInfoDialogStyles } from './estimatedRiskInfoDialogStyle';
+import parse from 'html-react-parser';
 
 interface EstimatedRiskInfoDialogProps {
   isOpen: boolean;
   onClose: () => void;
 }
 
+const ConsequenceDescription = ({
+  option,
+  index,
+  t,
+}: {
+  option: number;
+  index: number;
+  t: any;
+}) => (
+  <Fragment key={option}>
+    <b>{index + 1}</b>: 20<sup>{index + 3}</sup> = {formatNOK(option)}{' '}
+    {t('infoDialog.consequenceDescriptionGeneral')} ={' '}
+    <b>{parse(t(`infoDialog.consequenceDescription.${index}`))}</b>
path: plugins/ros/src/components/riskMatrix/EstimatedRiskInfoDialog.tsx
user: login: AndersHR
body: Done ✅ 
created_at: 2025-05-15T08:48:13Z
author_association: CONTRIBUTOR

PR comments:
user: login: emilte
created_at: 2025-04-24T14:15:07Z
body: Ønsker helst å resolve egne tråder, så det er fint å bare skrive f.eks. "done"
author_association: CONTRIBUTOR
user: login: AndersHR
created_at: 2025-04-30T13:14:49Z
body: Jeg har funnet ut hva bugen vi fant kommer fra! 🐛  @ingelinsu 

Scenarioer som er opprettet før endring til base 20 får ikke opp nåværende sannsynlighet i Select-input:

![Screenshot 2025-04-30 at 15 09 28](https://github.com/user-attachments/assets/f66050a8-deb3-46d4-b9a1-0afe8c3d9a6a)

Scenarioer som er opprettet etter endring til base 20 fungerer som forventet:

![Screenshot 2025-04-30 at 15 09 56](https://github.com/user-attachments/assets/c4e80cef-4a19-41ad-8ff2-bbd469c073d8)

Problemet er den prøver å mappe verdiene for sannsynlighet til presis verdiene 20^-2, 20^-1, ..., 20^2. Dersom tallverdien ikke er eksakt en av disse så vet ikke Select-komponenten hvordan den skal mappes. Fra konsollen ser vi:

![Screenshot 2025-04-30 at 15 15 39](https://github.com/user-attachments/assets/5f0effe7-84e9-4b6b-85ed-e0d0f58c3908)

Hvis vi skal støtte at sannsynlighet og konsekvens kan settes til hvilke som helst verdier så må vi mappe vilkårlige tallverdier til konsekvens- og sannsynlighetsnivå (1 til 5) også her.
author_association: CONTRIBUTOR
user: login: ingelinsu
created_at: 2025-05-02T07:26:51Z
body: > Jeg har funnet ut hva bugen vi fant kommer fra! 🐛 @ingelinsu
> 
> Scenarioer som er opprettet før endring til base 20 får ikke opp nåværende sannsynlighet i Select-input:
> 
> ![Screenshot 2025-04-30 at 15 09 28](https://private-user-images.githubusercontent.com/33486111/439242616-f66050a8-deb3-46d4-b9a1-0afe8c3d9a6a.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDYxNzEwNzksIm5iZiI6MTc0NjE3MDc3OSwicGF0aCI6Ii8zMzQ4NjExMS80MzkyNDI2MTYtZjY2MDUwYTgtZGViMy00NmQ0LWI5YTEtMGFmZThjM2Q5YTZhLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA1MDIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNTAyVDA3MjYxOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTgxMWZhN2Y1NDZhNTU1YzcwZjgzNzU3OGYyYzllZWU2M2E0ZTg3ZGJmYjYwMzNhMjBlNGY2NTMyY2NkMWIxYWEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.aBoXV5foVHOnCIavC9fgggO5WXFfwmchBJs_2iXVYBU)
> 
> Scenarioer som er opprettet etter endring til base 20 fungerer som forventet:
> 
> ![Screenshot 2025-04-30 at 15 09 56](https://private-user-images.githubusercontent.com/33486111/439242782-c4e80cef-4a19-41ad-8ff2-bbd469c073d8.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDYxNzEwNzksIm5iZiI6MTc0NjE3MDc3OSwicGF0aCI6Ii8zMzQ4NjExMS80MzkyNDI3ODItYzRlODBjZWYtNGExOS00MWFkLThmZjItYmJkNDY5YzA3M2Q4LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA1MDIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNTAyVDA3MjYxOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWY2N2E4ZDgwNzE4YjlhYTBkOGI1OWNkNjk2YmM2YjZkMTI5OWY1Yjc0ZDFmZDRiYTE1MzkzYzFlZGY0NTE3OTgmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.Pkuyq2-MIwWpSuiMZXSxQn2duaYS8osa06Mt432Dsvo)
> 
> Problemet er den prøver å mappe verdiene for sannsynlighet til presis verdiene 20^-2, 20^-1, ..., 20^2. Dersom tallverdien ikke er eksakt en av disse så vet ikke Select-komponenten hvordan den skal mappes. Fra konsollen ser vi:
> 
> ![Screenshot 2025-04-30 at 15 15 39](https://private-user-images.githubusercontent.com/33486111/439244930-5f0effe7-84e9-4b6b-85ed-e0d0f58c3908.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDYxNzEwNzksIm5iZiI6MTc0NjE3MDc3OSwicGF0aCI6Ii8zMzQ4NjExMS80MzkyNDQ5MzAtNWYwZWZmZTctODRlOS00YjZiLTg1ZWQtZTBkMGY1OGMzOTA4LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA1MDIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNTAyVDA3MjYxOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWIzYzI4YzFhZjVkZjc0ZGEwOTMwODI1NWNmNWIxN2Y2NjIwZGZmNTBjYTM4MTM0YjQzMjBjZmY1YjQzZjBlOTYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.vmmcqhgBTmaqJOr-glazHab02KyLr6GmBihN8QeJ3eY)
> 
> Hvis vi skal støtte at sannsynlighet og konsekvens kan settes til hvilke som helst verdier så må vi mappe vilkårlige tallverdier til konsekvens- og sannsynlighetsnivå (1 til 5) også her.

Fikset!
author_association: CONTRIBUTOR
user: login: AndersHR
created_at: 2025-05-02T09:48:20Z
body: > Som også kommentert i PR-en for backenden, så synes jeg hele oppsettet med at man får lov til å skrive andre verdier lokalt enn de 5 som brukes av frontenden, er veldig rart.
> 
> Sånn det ser ut for meg, var dette ikke støttet i frontenden før endringen man gjør her. Og endringene her ser ut til å i all hovedsak ignorere andre verdier enn de 5 som er basert på grunntall 20. Man runder av til den nærmeste av disse 5 (på forskjellig måte for konsekvens og sannsynlighet) og viser kun denne avrundede verdien, både når man viser hvilket nivå man er på (1-5) og når man regner ut kostnaden. Det vil si, endringene man gjør lokalt med andre verdier ignoreres med mindre man ser på ROSen lokalt. Brukere som ser på ROSen i nettleseren har da ingen anelse om at andre verdier brukes og vil lett kunne ende opp med å skrive over disse uten å mene det?

Ja dette er generelt veldig uintuitivt og noe vi burde ta en diskusjon på. Vi har prøvd å få til en prat med Jobi som ikke har hatt tid, men i meldingene har han vært hard på at man skal kunne skrive hvilke som helst verdier lokalt. Hvis det _skal_ være mulig bør det komme frem i Frontend at det er _verdien_ som er gjeldend og "konsekvensnivå" 1 til 5 kun er avledet fra verdien.

Å bruke round og ceiling ulikt for å tvinge gamle og nye verdier til å mappes til samme nivå føles og som en strikk og binders-fiks. Alternativt kan man migrere alle gamle tallverdier til de nye (mappe 10^2 -> 20^2 i migrering fra 4.0 til 4.1) men dette var også Jobi veldig mot.
author_association: CONTRIBUTOR
user: login: halvardhummel
created_at: 2025-05-02T11:20:53Z
body: > Ja dette er generelt veldig uintuitivt og noe vi burde ta en diskusjon på. Vi har prøvd å få til en prat med Jobi som ikke har hatt tid, men i meldingene har han vært hard på at man skal kunne skrive hvilke som helst verdier lokalt. Hvis det _skal_ være mulig bør det komme frem i Frontend at det er _verdien_ som er gjeldend og "konsekvensnivå" 1 til 5 kun er avledet fra verdien.
> 
> Å bruke round og ceiling ulikt for å tvinge gamle og nye verdier til å mappes til samme nivå føles og som en strikk og binders-fiks. Alternativt kan man migrere alle gamle tallverdier til de nye (mappe 10^2 -> 20^2 i migrering fra 4.0 til 4.1) men dette var også Jobi veldig mot.

Helt enig at det bør tas en diskusjon på dette **før** endringen merges inn. Jeg synes at et viktig spørsmål i den sammenheng er:

- Hvis det er så viktig at man kan skrive inn hvilke som helst verdier lokalt, er det noen grunn til at det ikke er mulig å gjøre dette i web-varianten?
author_association: CONTRIBUTOR
user: login: jorn-ola-birkeland
created_at: 2025-05-02T12:29:43Z
body: Jeg er ikke _veldig_ imot remapping av verdier hvis det er beste løsning. Jeg mener at det _klart_ viktigste er at konsekvens og sannsynlighet lagres som hhv `<valuta>/hendelse` og `hendelse/år`. Skalaene 1-5 handler mer om visualisering (visning) og forenkling av valg (redigering). Dermed burde frontend støtte konvertering frem og tilbake, og effekten av å endre beregningen på skalaene blir at punktene flytter seg i risikomatrisen
author_association: CONTRIBUTOR
user: login: jorn-ola-birkeland
created_at: 2025-05-02T12:33:28Z
body: Infoboksen "Estimert risiko" har på screenshot'en fortsatt de gamle verdiene, altså f.eks **1. 20^3 = 1000 kr/hendelse**. Det riktige er jo nærmere ti tusen (8 000). Osv
author_association: CONTRIBUTOR
user: login: ingelinsu
created_at: 2025-05-02T12:44:37Z
body: > Infoboksen "Estimert risiko" har på screenshot'en fortsatt de gamle verdiene, altså f.eks **1. 20^3 = 1000 kr/hendelse**. Det riktige er jo nærmere ti tusen (8 000). Osv

Gamle bilder bare - oppdaterer nå!
author_association: CONTRIBUTOR