@kartverket/backstage-plugin-risk-scorecard 1.0.12 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/dist/esm/{index-55c3e55b.esm.js → index-b5d09407.esm.js} +192 -42
- package/dist/esm/index-b5d09407.esm.js.map +1 -0
- package/dist/esm/{index-f0f7d618.esm.js → index-d8d91dc1.esm.js} +41 -5
- package/dist/esm/index-d8d91dc1.esm.js.map +1 -0
- package/dist/index.esm.js +1 -1
- package/package.json +2 -4
- package/config.d.ts +0 -9
- package/dist/esm/index-55c3e55b.esm.js.map +0 -1
- package/dist/esm/index-f0f7d618.esm.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,11 +4,14 @@ This is a plugin for Backstage that helps you and your team when working continu
|
|
|
4
4
|
The plugin is dependent on a backend service in order to decrypt and communicate with GitHub, and some configuration is
|
|
5
5
|
necessary for them to communicate.
|
|
6
6
|
|
|
7
|
-
Add the
|
|
7
|
+
Add the following configuration to the proxy-block in your app-config. Modify the `target` to the root url of your running backend service.
|
|
8
8
|
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
```yaml
|
|
10
|
+
proxy:
|
|
11
|
+
endpoints:
|
|
12
|
+
'/risc-proxy':
|
|
13
|
+
target: http://localhost:8080
|
|
14
|
+
allowedHeaders: ['Authorization', 'GCP-Access-Token']
|
|
12
15
|
```
|
|
13
16
|
|
|
14
17
|
The backend uses EntraID id-tokens to validate the user, and GCP access tokens to federate access to the GCP KMS.
|
|
@@ -18,4 +21,4 @@ for both:
|
|
|
18
21
|
1. Add discovery of organization data for Microsoft Org
|
|
19
22
|
2. Add authentication for both providers
|
|
20
23
|
|
|
21
|
-
Happy RiSc-ing 🌹
|
|
24
|
+
Happy RiSc-ing 🌹
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback, createContext, useContext, Fragment, useRef } from 'react';
|
|
2
|
-
import { makeStyles, FormControl, FormLabel, FormHelperText, TextField as TextField$1, Grid, Typography, Box as Box$1, useTheme, Paper, Button, Radio, CircularProgress, Dialog, DialogTitle, DialogActions, Stepper, Step, StepButton, Divider as Divider$1, Drawer, List, ListSubheader, ListItem, ListItemText as ListItemText$1, ClickAwayListener, Tooltip, DialogContent, DialogContentText, IconButton, Tab } from '@material-ui/core';
|
|
2
|
+
import { makeStyles, FormControl, FormLabel, FormHelperText, TextField as TextField$1, Grid, Typography, Box as Box$1, useTheme, Paper, Button, Radio, CircularProgress, Dialog, DialogTitle, DialogActions, Stepper, Step, StepButton, Divider as Divider$1, Drawer, List, ListSubheader, ListItem, ListItemText as ListItemText$1, ClickAwayListener, Tooltip, DialogContent, DialogContentText, IconButton, Tab, LinearProgress } from '@material-ui/core';
|
|
3
3
|
import { InfoCard, ContentHeader, SupportButton } from '@backstage/core-components';
|
|
4
4
|
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
5
5
|
import { useRouteRef, useApi, googleAuthApiRef, identityApiRef, fetchApiRef, configApiRef } from '@backstage/core-plugin-api';
|
|
6
|
-
import { s as scenarioRouteRef, r as riScRouteRef, p as pluginRiScTranslationRef } from './index-
|
|
6
|
+
import { s as scenarioRouteRef, r as riScRouteRef, p as pluginRiScTranslationRef } from './index-d8d91dc1.esm.js';
|
|
7
7
|
import { useNavigate, useLocation, useParams } from 'react-router';
|
|
8
8
|
import { useEffectOnce } from 'react-use';
|
|
9
9
|
import { useSearchParams, Routes, Route } from 'react-router-dom';
|
|
@@ -69,6 +69,13 @@ var ProcessingStatus = /* @__PURE__ */ ((ProcessingStatus2) => {
|
|
|
69
69
|
ProcessingStatus2["ErrorWhenFetchingJSONSchema"] = "ErrorWhenFetchingJSONSchema";
|
|
70
70
|
return ProcessingStatus2;
|
|
71
71
|
})(ProcessingStatus || {});
|
|
72
|
+
var ContentStatus = /* @__PURE__ */ ((ContentStatus2) => {
|
|
73
|
+
ContentStatus2["Success"] = "Success";
|
|
74
|
+
ContentStatus2["Failure"] = "Failure";
|
|
75
|
+
ContentStatus2["FileNotFound"] = "FileNotFound";
|
|
76
|
+
ContentStatus2["DecryptionFailed"] = "DecryptionFailed";
|
|
77
|
+
return ContentStatus2;
|
|
78
|
+
})(ContentStatus || {});
|
|
72
79
|
|
|
73
80
|
var $schema = "https://json-schema.org/draft/2020-12/schema";
|
|
74
81
|
var $id = "https://kartverket.github.io/backstage-plugin-risk-scorecard-backend/risc_schema_en_v3_2.json";
|
|
@@ -446,7 +453,7 @@ const requiresNewApproval = (oldRiSc, updatedRiSc) => {
|
|
|
446
453
|
return true;
|
|
447
454
|
}
|
|
448
455
|
let requiresApproval = false;
|
|
449
|
-
oldRiSc.scenarios.
|
|
456
|
+
oldRiSc.scenarios.forEach((oldScenario, index) => {
|
|
450
457
|
const updatedScenario = updatedRiSc.scenarios[index];
|
|
451
458
|
if (oldScenario.threatActors !== updatedScenario.threatActors)
|
|
452
459
|
requiresApproval = true;
|
|
@@ -462,10 +469,14 @@ const requiresNewApproval = (oldRiSc, updatedRiSc) => {
|
|
|
462
469
|
requiresApproval = true;
|
|
463
470
|
if (oldScenario.remainingRisk.consequence !== updatedScenario.remainingRisk.consequence)
|
|
464
471
|
requiresApproval = true;
|
|
465
|
-
oldScenario.actions.
|
|
466
|
-
|
|
467
|
-
|
|
472
|
+
oldScenario.actions.forEach((oldAction, i) => {
|
|
473
|
+
if (updatedScenario.actions[i] !== void 0) {
|
|
474
|
+
const updatedAction = updatedScenario.actions[i];
|
|
475
|
+
if (oldAction.deadline !== updatedAction.deadline)
|
|
476
|
+
requiresApproval = true;
|
|
477
|
+
} else {
|
|
468
478
|
requiresApproval = true;
|
|
479
|
+
}
|
|
469
480
|
});
|
|
470
481
|
});
|
|
471
482
|
return requiresApproval;
|
|
@@ -592,12 +603,12 @@ const useFetch = () => {
|
|
|
592
603
|
const googleApi = useApi(googleAuthApiRef);
|
|
593
604
|
const identityApi = useApi(identityApiRef);
|
|
594
605
|
const { fetch: fetchApi } = useApi(fetchApiRef);
|
|
595
|
-
const
|
|
596
|
-
const riScUri = `${
|
|
606
|
+
const backendUrl = useApi(configApiRef).getString("backend.baseUrl");
|
|
607
|
+
const riScUri = `${backendUrl}/api/proxy/risc-proxy/api/risc/${repoInformation.owner}/${repoInformation.name}`;
|
|
597
608
|
const uriToFetchAllRiScs = () => `${riScUri}/all`;
|
|
598
609
|
const uriToFetchRiSc = (id) => `${riScUri}/${id}`;
|
|
599
610
|
const uriToPublishRiSc = (id) => `${riScUri}/publish/${id}`;
|
|
600
|
-
const uriToFetchLatestJSONSchema = () => `${
|
|
611
|
+
const uriToFetchLatestJSONSchema = () => `${backendUrl}/api/proxy/risc-proxy/api/risc/schemas/latest`;
|
|
601
612
|
const [response, setResponse] = useResponse();
|
|
602
613
|
const fetch = (uri, method, onSuccess, onError, body) => {
|
|
603
614
|
Promise.all([
|
|
@@ -739,7 +750,6 @@ const useScenarioDrawer = (riSc, updateRiSc, scenarioIdFromParams) => {
|
|
|
739
750
|
return;
|
|
740
751
|
}
|
|
741
752
|
if (isNewScenario) {
|
|
742
|
-
setSearchParams({ step: "scenario" });
|
|
743
753
|
return;
|
|
744
754
|
}
|
|
745
755
|
const selectedScenario = riSc.content.scenarios.find(
|
|
@@ -755,12 +765,17 @@ const useScenarioDrawer = (riSc, updateRiSc, scenarioIdFromParams) => {
|
|
|
755
765
|
setOriginalScenario(selectedScenario);
|
|
756
766
|
setScenarioDrawerState(2 /* View */);
|
|
757
767
|
}
|
|
758
|
-
}, [
|
|
768
|
+
}, [riSc, scenarioIdFromParams, getRiScPath, navigate, isNewScenario]);
|
|
759
769
|
const openScenario = (id) => {
|
|
760
770
|
if (riSc) {
|
|
761
771
|
navigate(getScenarioPath({ riScId: riSc.id, scenarioId: id }));
|
|
762
772
|
}
|
|
763
773
|
};
|
|
774
|
+
const openNewScenarioWizard = (id, step) => {
|
|
775
|
+
if (riSc) {
|
|
776
|
+
navigate(`${getScenarioPath({ riScId: riSc.id, scenarioId: id })}?step=${step}`);
|
|
777
|
+
}
|
|
778
|
+
};
|
|
764
779
|
const closeScenario = () => {
|
|
765
780
|
if (riSc) {
|
|
766
781
|
setIsNewScenario(false);
|
|
@@ -813,7 +828,7 @@ const useScenarioDrawer = (riSc, updateRiSc, scenarioIdFromParams) => {
|
|
|
813
828
|
const s = emptyScenario();
|
|
814
829
|
setScenario(s);
|
|
815
830
|
setOriginalScenario(s);
|
|
816
|
-
|
|
831
|
+
openNewScenarioWizard(s.ID, "scenario");
|
|
817
832
|
}
|
|
818
833
|
};
|
|
819
834
|
const setTitle = (title) => {
|
|
@@ -923,6 +938,7 @@ const useFetchRiScs = (riScIdFromParams) => {
|
|
|
923
938
|
const location = useLocation();
|
|
924
939
|
const navigate = useNavigate();
|
|
925
940
|
const getRiScPath = useRouteRef(riScRouteRef);
|
|
941
|
+
const [isRequesting, setIsRequesting] = useState(false);
|
|
926
942
|
const {
|
|
927
943
|
fetchRiScs,
|
|
928
944
|
postRiScs,
|
|
@@ -953,8 +969,9 @@ const useFetchRiScs = (riScIdFromParams) => {
|
|
|
953
969
|
useEffectOnce(() => {
|
|
954
970
|
fetchRiScs(
|
|
955
971
|
(res) => {
|
|
956
|
-
const fetchedRiScs = res.map((riScDTO) => {
|
|
957
|
-
const
|
|
972
|
+
const fetchedRiScs = res.filter((risk) => risk.status === ContentStatus.Success).map((riScDTO) => {
|
|
973
|
+
const json = JSON.parse(riScDTO.riScContent);
|
|
974
|
+
const content = dtoToRiSc(json);
|
|
958
975
|
return {
|
|
959
976
|
id: riScDTO.riScId,
|
|
960
977
|
content,
|
|
@@ -963,6 +980,16 @@ const useFetchRiScs = (riScIdFromParams) => {
|
|
|
963
980
|
});
|
|
964
981
|
setRiScs(fetchedRiScs);
|
|
965
982
|
setIsFetching(false);
|
|
983
|
+
const errorRiScs = res.filter((risk) => risk.status !== ContentStatus.Success).map((risk) => risk.riScId);
|
|
984
|
+
if (errorRiScs.length > 0) {
|
|
985
|
+
const errorMessage = `Failed to fetch risc scorecards with ids: ${errorRiScs.join(
|
|
986
|
+
", "
|
|
987
|
+
)}`;
|
|
988
|
+
setResponse({
|
|
989
|
+
statusMessage: errorMessage,
|
|
990
|
+
status: ProcessingStatus.ErrorWhenFetchingRiScs
|
|
991
|
+
});
|
|
992
|
+
}
|
|
966
993
|
if (fetchedRiScs.length === 0) {
|
|
967
994
|
return;
|
|
968
995
|
}
|
|
@@ -1092,6 +1119,7 @@ const useFetchRiScs = (riScIdFromParams) => {
|
|
|
1092
1119
|
isError: false,
|
|
1093
1120
|
isSuccess: false
|
|
1094
1121
|
});
|
|
1122
|
+
setIsRequesting(true);
|
|
1095
1123
|
putRiScs(
|
|
1096
1124
|
updatedRiSc,
|
|
1097
1125
|
() => {
|
|
@@ -1104,6 +1132,7 @@ const useFetchRiScs = (riScIdFromParams) => {
|
|
|
1104
1132
|
setRiScs(
|
|
1105
1133
|
riScs.map((r) => r.id === selectedRiSc.id ? updatedRiSc : r)
|
|
1106
1134
|
);
|
|
1135
|
+
setIsRequesting(false);
|
|
1107
1136
|
},
|
|
1108
1137
|
() => {
|
|
1109
1138
|
setUpdateRiScStatus({
|
|
@@ -1111,6 +1140,7 @@ const useFetchRiScs = (riScIdFromParams) => {
|
|
|
1111
1140
|
isError: true,
|
|
1112
1141
|
isSuccess: false
|
|
1113
1142
|
});
|
|
1143
|
+
setIsRequesting(false);
|
|
1114
1144
|
}
|
|
1115
1145
|
);
|
|
1116
1146
|
}
|
|
@@ -1137,7 +1167,8 @@ const useFetchRiScs = (riScIdFromParams) => {
|
|
|
1137
1167
|
createNewRiSc,
|
|
1138
1168
|
updateRiSc,
|
|
1139
1169
|
approveRiSc,
|
|
1140
|
-
response
|
|
1170
|
+
response,
|
|
1171
|
+
isRequesting
|
|
1141
1172
|
};
|
|
1142
1173
|
};
|
|
1143
1174
|
|
|
@@ -1242,7 +1273,7 @@ const useInputFieldStyles = makeStyles((theme) => ({
|
|
|
1242
1273
|
paper: {
|
|
1243
1274
|
padding: theme.spacing(2),
|
|
1244
1275
|
marginBottom: theme.spacing(2),
|
|
1245
|
-
backgroundColor: theme.palette.type === "dark" ? "#404040" : "#
|
|
1276
|
+
backgroundColor: theme.palette.type === "dark" ? "#404040" : "#FFFFFF"
|
|
1246
1277
|
},
|
|
1247
1278
|
root: {
|
|
1248
1279
|
"&.Mui-disabled": {
|
|
@@ -1301,10 +1332,23 @@ const Dropdown = ({
|
|
|
1301
1332
|
handleChange,
|
|
1302
1333
|
error,
|
|
1303
1334
|
required,
|
|
1304
|
-
variant = "outlined"
|
|
1335
|
+
variant = "outlined",
|
|
1336
|
+
renderSelectedValue
|
|
1305
1337
|
}) => {
|
|
1306
1338
|
const { formLabel, formControl } = useInputFieldStyles();
|
|
1307
1339
|
const multiple = Array.isArray(selectedValues);
|
|
1340
|
+
function getOptionsValue(option) {
|
|
1341
|
+
return typeof option === "string" ? option : option.value;
|
|
1342
|
+
}
|
|
1343
|
+
function getOptionsRenderValue(option) {
|
|
1344
|
+
return typeof option === "string" ? option : option.renderedValue;
|
|
1345
|
+
}
|
|
1346
|
+
function renderSelectedValueHandler(value) {
|
|
1347
|
+
if (renderSelectedValue) {
|
|
1348
|
+
return renderSelectedValue(value);
|
|
1349
|
+
}
|
|
1350
|
+
return value;
|
|
1351
|
+
}
|
|
1308
1352
|
const onChange = (event) => handleChange(event.target.value);
|
|
1309
1353
|
const renderValue = (selected) => multiple ? /* @__PURE__ */ React.createElement(
|
|
1310
1354
|
Box,
|
|
@@ -1316,7 +1360,7 @@ const Dropdown = ({
|
|
|
1316
1360
|
paddingBottom: 0
|
|
1317
1361
|
}
|
|
1318
1362
|
},
|
|
1319
|
-
selected.map((value) => /* @__PURE__ */ React.createElement(Chip, { key: value, label: value }))
|
|
1363
|
+
selected.map((value) => /* @__PURE__ */ React.createElement(Chip, { key: value, label: renderSelectedValueHandler(value) }))
|
|
1320
1364
|
) : selected;
|
|
1321
1365
|
return /* @__PURE__ */ React.createElement(FormControl$1, { className: formControl }, label && /* @__PURE__ */ React.createElement(FormLabel$1, { className: formLabel, required }, label), /* @__PURE__ */ React.createElement(
|
|
1322
1366
|
Select,
|
|
@@ -1336,7 +1380,7 @@ const Dropdown = ({
|
|
|
1336
1380
|
}
|
|
1337
1381
|
} : {}
|
|
1338
1382
|
},
|
|
1339
|
-
options.map((name) => /* @__PURE__ */ React.createElement(MenuItem, { key: name, value: name }, multiple && /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Checkbox, { checked: selectedValues.indexOf(name) > -1 })), /* @__PURE__ */ React.createElement(ListItemText, { primary: name })))
|
|
1383
|
+
options.map((name) => /* @__PURE__ */ React.createElement(MenuItem, { key: getOptionsValue(name), value: getOptionsValue(name) }, multiple && /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Checkbox, { checked: selectedValues.indexOf(getOptionsValue(name)) > -1 })), /* @__PURE__ */ React.createElement(ListItemText, { primary: getOptionsRenderValue(name) })))
|
|
1340
1384
|
), error && /* @__PURE__ */ React.createElement(FormHelperText, { error: true }, error));
|
|
1341
1385
|
};
|
|
1342
1386
|
|
|
@@ -1353,6 +1397,20 @@ const ScenarioStep = () => {
|
|
|
1353
1397
|
setThreatActors,
|
|
1354
1398
|
setVulnerabilities
|
|
1355
1399
|
} = useContext(ScenarioContext);
|
|
1400
|
+
const translatedThreatActors = threatActorsOptions.map((threatActor) => {
|
|
1401
|
+
return {
|
|
1402
|
+
value: threatActor,
|
|
1403
|
+
/* @ts-ignore Because ts can't typecheck strings agains our keys */
|
|
1404
|
+
renderedValue: t(`threatActors.${threatActor}`)
|
|
1405
|
+
};
|
|
1406
|
+
});
|
|
1407
|
+
const translatedVulnerabilities = vulnerabilitiesOptions.map((vulnerability) => {
|
|
1408
|
+
return {
|
|
1409
|
+
value: vulnerability,
|
|
1410
|
+
/* @ts-ignore Because ts can't typecheck strings agains our keys */
|
|
1411
|
+
renderedValue: t(`vulnerabilities.${vulnerability}`)
|
|
1412
|
+
};
|
|
1413
|
+
});
|
|
1356
1414
|
return /* @__PURE__ */ React.createElement(Grid, { container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Typography, { className: h2 }, t("scenarioDrawer.title")), /* @__PURE__ */ React.createElement(Typography, { className: subtitle2 }, t("scenarioDrawer.subtitle"))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
|
|
1357
1415
|
TextField,
|
|
1358
1416
|
{
|
|
@@ -1379,8 +1437,11 @@ const ScenarioStep = () => {
|
|
|
1379
1437
|
Dropdown,
|
|
1380
1438
|
{
|
|
1381
1439
|
selectedValues: scenario.threatActors,
|
|
1382
|
-
options:
|
|
1383
|
-
handleChange: setThreatActors
|
|
1440
|
+
options: translatedThreatActors,
|
|
1441
|
+
handleChange: setThreatActors,
|
|
1442
|
+
renderSelectedValue: (value) => {
|
|
1443
|
+
return t(`threatActors.${value}`);
|
|
1444
|
+
}
|
|
1384
1445
|
}
|
|
1385
1446
|
)
|
|
1386
1447
|
), /* @__PURE__ */ React.createElement(
|
|
@@ -1399,8 +1460,11 @@ const ScenarioStep = () => {
|
|
|
1399
1460
|
Dropdown,
|
|
1400
1461
|
{
|
|
1401
1462
|
selectedValues: scenario.vulnerabilities,
|
|
1402
|
-
options:
|
|
1403
|
-
handleChange: setVulnerabilities
|
|
1463
|
+
options: translatedVulnerabilities,
|
|
1464
|
+
handleChange: setVulnerabilities,
|
|
1465
|
+
renderSelectedValue: (value) => {
|
|
1466
|
+
return t(`vulnerabilities.${value}`);
|
|
1467
|
+
}
|
|
1404
1468
|
}
|
|
1405
1469
|
)
|
|
1406
1470
|
), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
|
|
@@ -1623,6 +1687,7 @@ const ProbabilityTable = ({
|
|
|
1623
1687
|
selectedValue,
|
|
1624
1688
|
handleChange
|
|
1625
1689
|
}) => {
|
|
1690
|
+
const theme = useTheme();
|
|
1626
1691
|
const { t } = useTranslationRef(pluginRiScTranslationRef);
|
|
1627
1692
|
const { table, cell, radio } = useTableStyles$1();
|
|
1628
1693
|
const handleChangeRow = (row) => () => handleChange(row);
|
|
@@ -1631,7 +1696,10 @@ const ProbabilityTable = ({
|
|
|
1631
1696
|
{
|
|
1632
1697
|
checked: selectedValue === row,
|
|
1633
1698
|
onChange: handleChangeRow(row),
|
|
1634
|
-
style: {
|
|
1699
|
+
style: {
|
|
1700
|
+
padding: 0,
|
|
1701
|
+
color: theme.palette.type === "dark" ? "#9BC9FE" : theme.palette.primary.main
|
|
1702
|
+
}
|
|
1635
1703
|
}
|
|
1636
1704
|
), row, ": ", t(`probabilityTable.rows.${row}`)));
|
|
1637
1705
|
const contentCell = (row) => /* @__PURE__ */ React.createElement("td", { className: cell }, t(`probabilityTable.cells.${row}`));
|
|
@@ -1642,6 +1710,7 @@ const ConsequenceTable = ({
|
|
|
1642
1710
|
selectedValue,
|
|
1643
1711
|
handleChange
|
|
1644
1712
|
}) => {
|
|
1713
|
+
const theme = useTheme();
|
|
1645
1714
|
const { t } = useTranslationRef(pluginRiScTranslationRef);
|
|
1646
1715
|
const { table, labelCell, cell, voidCell, radio } = useTableStyles$1();
|
|
1647
1716
|
const handleChangeRow = (row) => () => handleChange(row);
|
|
@@ -1651,7 +1720,10 @@ const ConsequenceTable = ({
|
|
|
1651
1720
|
{
|
|
1652
1721
|
checked: selectedValue === row,
|
|
1653
1722
|
onChange: handleChangeRow(row),
|
|
1654
|
-
style: {
|
|
1723
|
+
style: {
|
|
1724
|
+
padding: 0,
|
|
1725
|
+
color: theme.palette.type === "dark" ? theme.palette.primary.light : theme.palette.primary.main
|
|
1726
|
+
}
|
|
1655
1727
|
}
|
|
1656
1728
|
), row, ": ", t(`consequenceTable.rows.${row}`)));
|
|
1657
1729
|
};
|
|
@@ -1693,11 +1765,21 @@ const RiskStep = ({ riskType, restEqualsInitial }) => {
|
|
|
1693
1765
|
useEffect(() => {
|
|
1694
1766
|
if (restEqualsInitial)
|
|
1695
1767
|
setRemainingConsequence(getConsequenceLevel(scenario.risk));
|
|
1696
|
-
}, [
|
|
1768
|
+
}, [
|
|
1769
|
+
restEqualsInitial,
|
|
1770
|
+
scenario.risk,
|
|
1771
|
+
scenario.risk.consequence,
|
|
1772
|
+
setRemainingConsequence
|
|
1773
|
+
]);
|
|
1697
1774
|
useEffect(() => {
|
|
1698
1775
|
if (restEqualsInitial)
|
|
1699
1776
|
setRemainingProbability(getProbabilityLevel(scenario.risk));
|
|
1700
|
-
}, [
|
|
1777
|
+
}, [
|
|
1778
|
+
restEqualsInitial,
|
|
1779
|
+
scenario.risk,
|
|
1780
|
+
scenario.risk.probability,
|
|
1781
|
+
setRemainingProbability
|
|
1782
|
+
]);
|
|
1701
1783
|
const setC = riskType === "initial" ? setConsequence : setRemainingConsequence;
|
|
1702
1784
|
const setP = riskType === "initial" ? setProbability : setRemainingProbability;
|
|
1703
1785
|
return /* @__PURE__ */ React.createElement(Fragment, null, /* @__PURE__ */ React.createElement(Box, { className: box }, /* @__PURE__ */ React.createElement(Typography, { className: h2 }, t(`scenarioStepper.${resourceKey}.title`)), /* @__PURE__ */ React.createElement(Typography, { className: subtitle2 }, t(`scenarioStepper.${resourceKey}.subtitle`))), /* @__PURE__ */ React.createElement(Box, { className: box }, /* @__PURE__ */ React.createElement(Typography, { className: h3 }, t("dictionary.probability")), /* @__PURE__ */ React.createElement(Typography, { className: subtitle2 }, t(`scenarioStepper.${resourceKey}.probabilitySubtitle`))), /* @__PURE__ */ React.createElement(Box, { className: box }, /* @__PURE__ */ React.createElement(
|
|
@@ -1749,6 +1831,15 @@ const useWizardStyle = makeStyles((theme) => ({
|
|
|
1749
1831
|
saveAndNextButtons: {
|
|
1750
1832
|
display: "flex",
|
|
1751
1833
|
gap: theme.spacing(2)
|
|
1834
|
+
},
|
|
1835
|
+
button: {
|
|
1836
|
+
"&.MuiButton-outlined": {
|
|
1837
|
+
color: theme.palette.type === "dark" ? "#9CC9FF" : theme.palette.link,
|
|
1838
|
+
borderr: "1px solid rgba(25, 118, 210, 0.5)",
|
|
1839
|
+
"&:hover": {
|
|
1840
|
+
borderColor: theme.palette.type === "dark" ? "#9CC9FF" : theme.palette.linkHover
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1752
1843
|
}
|
|
1753
1844
|
}));
|
|
1754
1845
|
|
|
@@ -1786,6 +1877,7 @@ const ScenarioWizard = ({
|
|
|
1786
1877
|
header,
|
|
1787
1878
|
stepper,
|
|
1788
1879
|
steps,
|
|
1880
|
+
button,
|
|
1789
1881
|
buttonContainer,
|
|
1790
1882
|
buttonContainerRight,
|
|
1791
1883
|
saveAndNextButtons
|
|
@@ -1801,6 +1893,7 @@ const ScenarioWizard = ({
|
|
|
1801
1893
|
} = useContext(ScenarioContext);
|
|
1802
1894
|
const [showCloseConfirmation, setShowCloseConfirmation] = useState(false);
|
|
1803
1895
|
const [restEqualsInitial, setRestEqualsInitial] = useState(isNewScenario);
|
|
1896
|
+
const [canCloseIfSuccessfull, setCanCloseIfSuccessfull] = useState(false);
|
|
1804
1897
|
useEffect(() => {
|
|
1805
1898
|
if (step === "restRisk")
|
|
1806
1899
|
setRestEqualsInitial(false);
|
|
@@ -1810,15 +1903,16 @@ const ScenarioWizard = ({
|
|
|
1810
1903
|
setShowCloseConfirmation(false);
|
|
1811
1904
|
}, [closeScenario]);
|
|
1812
1905
|
useEffect(() => {
|
|
1813
|
-
if (updateStatus.isSuccess) {
|
|
1906
|
+
if (updateStatus.isSuccess && canCloseIfSuccessfull) {
|
|
1814
1907
|
close();
|
|
1815
1908
|
} else if (updateStatus.isError && wizardRef.current) {
|
|
1816
1909
|
setShowCloseConfirmation(false);
|
|
1817
1910
|
wizardRef.current.scrollIntoView({ behavior: "smooth", block: "end" });
|
|
1818
1911
|
}
|
|
1819
|
-
}, [close, updateStatus]);
|
|
1912
|
+
}, [canCloseIfSuccessfull, close, updateStatus]);
|
|
1820
1913
|
const saveAndClose = () => {
|
|
1821
1914
|
saveScenario();
|
|
1915
|
+
setCanCloseIfSuccessfull(true);
|
|
1822
1916
|
};
|
|
1823
1917
|
const handleCloseStepper = () => {
|
|
1824
1918
|
if (JSON.stringify(scenario) !== JSON.stringify(originalScenario)) {
|
|
@@ -1848,7 +1942,15 @@ const ScenarioWizard = ({
|
|
|
1848
1942
|
const isLastStep = () => {
|
|
1849
1943
|
return scenarioWizardSteps.indexOf(step) === scenarioWizardSteps.length - 1;
|
|
1850
1944
|
};
|
|
1851
|
-
return /* @__PURE__ */ React.createElement("div", { ref: wizardRef }, /* @__PURE__ */ React.createElement(Box, { className: root }, /* @__PURE__ */ React.createElement(Box, { className: container }, /* @__PURE__ */ React.createElement(Box, { className: header }, /* @__PURE__ */ React.createElement(Typography, { className: h1 }, t("scenarioDrawer.title")), /* @__PURE__ */ React.createElement(
|
|
1945
|
+
return /* @__PURE__ */ React.createElement("div", { ref: wizardRef }, /* @__PURE__ */ React.createElement(Box, { className: root }, /* @__PURE__ */ React.createElement(Box, { className: container }, /* @__PURE__ */ React.createElement(Box, { className: header }, /* @__PURE__ */ React.createElement(Typography, { className: h1 }, t("scenarioDrawer.title")), /* @__PURE__ */ React.createElement(
|
|
1946
|
+
Button$1,
|
|
1947
|
+
{
|
|
1948
|
+
variant: "outlined",
|
|
1949
|
+
className: button,
|
|
1950
|
+
onClick: handleCloseStepper
|
|
1951
|
+
},
|
|
1952
|
+
t("dictionary.cancel")
|
|
1953
|
+
)), /* @__PURE__ */ React.createElement(
|
|
1852
1954
|
Stepper,
|
|
1853
1955
|
{
|
|
1854
1956
|
className: stepper,
|
|
@@ -1896,6 +1998,7 @@ const ScenarioWizard = ({
|
|
|
1896
1998
|
/* @__PURE__ */ React.createElement(Box, { className: saveAndNextButtons }, /* @__PURE__ */ React.createElement(
|
|
1897
1999
|
Button$1,
|
|
1898
2000
|
{
|
|
2001
|
+
className: button,
|
|
1899
2002
|
variant: isLastStep() ? "contained" : "outlined",
|
|
1900
2003
|
onClick: saveAndClose,
|
|
1901
2004
|
disabled: updateStatus.isLoading
|
|
@@ -1957,7 +2060,6 @@ const useScenarioDrawerStyles = makeStyles((theme) => ({
|
|
|
1957
2060
|
backgroundColor: theme.palette.type === "dark" ? "#555555" : "#e0e0e0"
|
|
1958
2061
|
},
|
|
1959
2062
|
gridContainer: {
|
|
1960
|
-
padding: theme.spacing(2),
|
|
1961
2063
|
marginBottom: theme.palette.type === "dark" ? theme.spacing(1) : "7px",
|
|
1962
2064
|
backgroundColor: theme.palette.type === "dark" ? "#55555519" : "#e0e0e019"
|
|
1963
2065
|
},
|
|
@@ -2059,7 +2161,7 @@ const RiskSection = () => {
|
|
|
2059
2161
|
));
|
|
2060
2162
|
};
|
|
2061
2163
|
|
|
2062
|
-
const useActionBoxStyles = makeStyles((
|
|
2164
|
+
const useActionBoxStyles = makeStyles((theme) => ({
|
|
2063
2165
|
alignCenter: {
|
|
2064
2166
|
display: "flex",
|
|
2065
2167
|
alignItems: "center"
|
|
@@ -2067,6 +2169,28 @@ const useActionBoxStyles = makeStyles((_) => ({
|
|
|
2067
2169
|
justifyEnd: {
|
|
2068
2170
|
display: "flex",
|
|
2069
2171
|
justifyContent: "flex-end"
|
|
2172
|
+
},
|
|
2173
|
+
row: {
|
|
2174
|
+
display: "flex",
|
|
2175
|
+
flexDirection: "row",
|
|
2176
|
+
justifyContent: "space-between",
|
|
2177
|
+
padding: theme.spacing(1)
|
|
2178
|
+
},
|
|
2179
|
+
column: {
|
|
2180
|
+
display: "flex",
|
|
2181
|
+
flexDirection: "column",
|
|
2182
|
+
justifyContent: "center"
|
|
2183
|
+
},
|
|
2184
|
+
paddingTop: {
|
|
2185
|
+
paddingTop: theme.spacing(2)
|
|
2186
|
+
},
|
|
2187
|
+
actionDescription: {
|
|
2188
|
+
marginTop: theme.spacing(-2)
|
|
2189
|
+
},
|
|
2190
|
+
chipDropdown: {
|
|
2191
|
+
display: "flex",
|
|
2192
|
+
flexDirection: "row",
|
|
2193
|
+
justifyContent: "flex-end"
|
|
2070
2194
|
}
|
|
2071
2195
|
}));
|
|
2072
2196
|
|
|
@@ -2087,7 +2211,7 @@ const ChipDropdown = ({
|
|
|
2087
2211
|
style: {
|
|
2088
2212
|
margin: 0,
|
|
2089
2213
|
padding: 0,
|
|
2090
|
-
backgroundColor: "#D9D9D9",
|
|
2214
|
+
backgroundColor: selectedValue === "Completed" ? "#6BC6A4" : "#D9D9D9",
|
|
2091
2215
|
color: "#000000"
|
|
2092
2216
|
},
|
|
2093
2217
|
label: selectedValue,
|
|
@@ -2126,7 +2250,14 @@ const ActionBox = ({
|
|
|
2126
2250
|
const { t } = useTranslationRef(pluginRiScTranslationRef);
|
|
2127
2251
|
const { body2, label } = useFontStyles();
|
|
2128
2252
|
const { gridContainer } = useScenarioDrawerStyles();
|
|
2129
|
-
const {
|
|
2253
|
+
const {
|
|
2254
|
+
alignCenter,
|
|
2255
|
+
row,
|
|
2256
|
+
paddingTop,
|
|
2257
|
+
actionDescription,
|
|
2258
|
+
column,
|
|
2259
|
+
chipDropdown
|
|
2260
|
+
} = useActionBoxStyles();
|
|
2130
2261
|
const [previousAction, setPreviousAction] = useState(action);
|
|
2131
2262
|
useEffect(() => {
|
|
2132
2263
|
if (previousAction !== action) {
|
|
@@ -2140,19 +2271,28 @@ const ActionBox = ({
|
|
|
2140
2271
|
status
|
|
2141
2272
|
});
|
|
2142
2273
|
};
|
|
2143
|
-
return /* @__PURE__ */ React.createElement(Grid, { container: true, className: gridContainer }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, className: alignCenter }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.measure"), " ", index)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs:
|
|
2274
|
+
return /* @__PURE__ */ React.createElement(Grid, { container: true, className: gridContainer }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, className: alignCenter }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6, className: paddingTop }, /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.measure"), " ", index))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, className: actionDescription }, /* @__PURE__ */ React.createElement(Typography, { className: body2 }, action.description)), /* @__PURE__ */ React.createElement(Grid, { container: true, xs: 12, className: row }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 4 }, /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.measureOwner")), /* @__PURE__ */ React.createElement(Typography, { className: body2 }, action.owner)), /* @__PURE__ */ React.createElement(Grid, { container: true, xs: 4, className: column }, /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.deadline")), /* @__PURE__ */ React.createElement(Typography, { className: body2 }, formatDate(action.deadline))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 3, className: chipDropdown }, /* @__PURE__ */ React.createElement(
|
|
2144
2275
|
ChipDropdown,
|
|
2145
2276
|
{
|
|
2146
2277
|
options: actionStatusOptions,
|
|
2147
2278
|
selectedValue: action.status,
|
|
2148
2279
|
handleChange: setStatus
|
|
2149
2280
|
}
|
|
2150
|
-
)))
|
|
2281
|
+
))));
|
|
2282
|
+
};
|
|
2283
|
+
|
|
2284
|
+
const ExistingActionBox = ({
|
|
2285
|
+
existingAction
|
|
2286
|
+
}) => {
|
|
2287
|
+
const { body2 } = useFontStyles();
|
|
2288
|
+
const { gridContainer } = useScenarioDrawerStyles();
|
|
2289
|
+
const { alignCenter } = useActionBoxStyles();
|
|
2290
|
+
return /* @__PURE__ */ React.createElement(Grid, { container: true, className: gridContainer }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, className: alignCenter }, /* @__PURE__ */ React.createElement(Typography, { className: body2 }, existingAction)));
|
|
2151
2291
|
};
|
|
2152
2292
|
|
|
2153
2293
|
const ActionsSection = () => {
|
|
2154
2294
|
const { t } = useTranslationRef(pluginRiScTranslationRef);
|
|
2155
|
-
const { h3, button } = useFontStyles();
|
|
2295
|
+
const { h3, button, label } = useFontStyles();
|
|
2156
2296
|
const { titleAndButton, section, editIcon } = useScenarioDrawerStyles();
|
|
2157
2297
|
const { scenario, editScenario, saveScenario, updateAction } = useContext(ScenarioContext);
|
|
2158
2298
|
return /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(Paper, { className: section, style: { padding: "1rem" } }, /* @__PURE__ */ React.createElement(
|
|
@@ -2165,7 +2305,7 @@ const ActionsSection = () => {
|
|
|
2165
2305
|
marginBottom: "12px"
|
|
2166
2306
|
}
|
|
2167
2307
|
},
|
|
2168
|
-
/* @__PURE__ */ React.createElement(Typography, { className: h3
|
|
2308
|
+
/* @__PURE__ */ React.createElement(Typography, { className: h3 }, t("scenarioDrawer.measureTab.actionsTitle")),
|
|
2169
2309
|
/* @__PURE__ */ React.createElement(
|
|
2170
2310
|
Button,
|
|
2171
2311
|
{
|
|
@@ -2176,7 +2316,7 @@ const ActionsSection = () => {
|
|
|
2176
2316
|
startIcon: /* @__PURE__ */ React.createElement(EditIcon, { className: editIcon, "aria-label": "Edit" })
|
|
2177
2317
|
}
|
|
2178
2318
|
)
|
|
2179
|
-
), scenario.actions.map((action, index) => /* @__PURE__ */ React.createElement(Fragment, { key: action.ID }, /* @__PURE__ */ React.createElement(
|
|
2319
|
+
), scenario.existingActions && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { className: label }, t("scenarioDrawer.measureTab.existingMeasure")), /* @__PURE__ */ React.createElement(ExistingActionBox, { existingAction: scenario.existingActions }), scenario.actions.length > 0 && /* @__PURE__ */ React.createElement(Divider$1, { variant: "fullWidth" })), scenario.actions.map((action, index) => /* @__PURE__ */ React.createElement(Fragment, { key: action.ID }, /* @__PURE__ */ React.createElement(
|
|
2180
2320
|
ActionBox,
|
|
2181
2321
|
{
|
|
2182
2322
|
action,
|
|
@@ -3119,10 +3259,19 @@ const RiScInfo = ({ riSc, approveRiSc, edit }) => {
|
|
|
3119
3259
|
), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, sm: 6, md: 4 }, /* @__PURE__ */ React.createElement(RiScStatusComponent, { selectedRiSc: riSc, publishRiScFn: approveRiSc })));
|
|
3120
3260
|
};
|
|
3121
3261
|
|
|
3262
|
+
const useLinearProgressStyle = makeStyles((theme) => ({
|
|
3263
|
+
linearProgress: {
|
|
3264
|
+
position: "sticky",
|
|
3265
|
+
top: 0,
|
|
3266
|
+
margin: theme.spacing(2)
|
|
3267
|
+
}
|
|
3268
|
+
}));
|
|
3269
|
+
|
|
3122
3270
|
const Plugin = () => {
|
|
3123
3271
|
var _a, _b;
|
|
3124
3272
|
const params = useParams();
|
|
3125
3273
|
const { t } = useTranslationRef(pluginRiScTranslationRef);
|
|
3274
|
+
const { linearProgress } = useLinearProgressStyle();
|
|
3126
3275
|
const [RiScDialogState, setRiScDialogState] = useState(
|
|
3127
3276
|
RiScDialogStates.Closed
|
|
3128
3277
|
);
|
|
@@ -3139,7 +3288,8 @@ const Plugin = () => {
|
|
|
3139
3288
|
updateRiScStatus,
|
|
3140
3289
|
resetRiScStatus,
|
|
3141
3290
|
approveRiSc,
|
|
3142
|
-
response
|
|
3291
|
+
response,
|
|
3292
|
+
isRequesting
|
|
3143
3293
|
} = useFetchRiScs(params.riScId);
|
|
3144
3294
|
const scenario = useScenarioDrawer(
|
|
3145
3295
|
selectedRiSc != null ? selectedRiSc : null,
|
|
@@ -3152,7 +3302,7 @@ const Plugin = () => {
|
|
|
3152
3302
|
if (scenarioWizardStep !== null)
|
|
3153
3303
|
resetRiScStatus();
|
|
3154
3304
|
}, [resetRiScStatus, scenarioWizardStep]);
|
|
3155
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ScenarioContext.Provider, { value: scenario }, response && /* @__PURE__ */ React.createElement(Alert, { severity: getAlertSeverity(response.status) }, /* @__PURE__ */ React.createElement(Typography, null, response.statusMessage)), scenarioWizardStep !== null ? /* @__PURE__ */ React.createElement(
|
|
3305
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ScenarioContext.Provider, { value: scenario }, response && /* @__PURE__ */ React.createElement(Alert, { severity: getAlertSeverity(response.status) }, /* @__PURE__ */ React.createElement(Typography, null, response.statusMessage)), isRequesting && /* @__PURE__ */ React.createElement(LinearProgress, { className: linearProgress }), scenarioWizardStep !== null ? /* @__PURE__ */ React.createElement(
|
|
3156
3306
|
ScenarioWizard,
|
|
3157
3307
|
{
|
|
3158
3308
|
step: scenarioWizardStep,
|
|
@@ -3214,4 +3364,4 @@ const RiScPlugin = () => {
|
|
|
3214
3364
|
};
|
|
3215
3365
|
|
|
3216
3366
|
export { RiScPlugin };
|
|
3217
|
-
//# sourceMappingURL=index-
|
|
3367
|
+
//# sourceMappingURL=index-b5d09407.esm.js.map
|