@kartverket/backstage-plugin-risk-scorecard 1.0.13 → 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 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 root url to your running backend service to the app-config:
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
- ``` yaml
10
- app:
11
- backendUrl: http://localhost:8080
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-7e0035e6.esm.js';
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';
@@ -453,7 +453,7 @@ const requiresNewApproval = (oldRiSc, updatedRiSc) => {
453
453
  return true;
454
454
  }
455
455
  let requiresApproval = false;
456
- oldRiSc.scenarios.map((oldScenario, index) => {
456
+ oldRiSc.scenarios.forEach((oldScenario, index) => {
457
457
  const updatedScenario = updatedRiSc.scenarios[index];
458
458
  if (oldScenario.threatActors !== updatedScenario.threatActors)
459
459
  requiresApproval = true;
@@ -469,10 +469,14 @@ const requiresNewApproval = (oldRiSc, updatedRiSc) => {
469
469
  requiresApproval = true;
470
470
  if (oldScenario.remainingRisk.consequence !== updatedScenario.remainingRisk.consequence)
471
471
  requiresApproval = true;
472
- oldScenario.actions.map((oldAction, i) => {
473
- const updatedAction = updatedScenario.actions[i];
474
- if (oldAction.deadline !== updatedAction.deadline)
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 {
475
478
  requiresApproval = true;
479
+ }
476
480
  });
477
481
  });
478
482
  return requiresApproval;
@@ -599,12 +603,12 @@ const useFetch = () => {
599
603
  const googleApi = useApi(googleAuthApiRef);
600
604
  const identityApi = useApi(identityApiRef);
601
605
  const { fetch: fetchApi } = useApi(fetchApiRef);
602
- const baseUri = useApi(configApiRef).getString("riskAssessment.baseUrl");
603
- const riScUri = `${baseUri}/api/risc/${repoInformation.owner}/${repoInformation.name}`;
606
+ const backendUrl = useApi(configApiRef).getString("backend.baseUrl");
607
+ const riScUri = `${backendUrl}/api/proxy/risc-proxy/api/risc/${repoInformation.owner}/${repoInformation.name}`;
604
608
  const uriToFetchAllRiScs = () => `${riScUri}/all`;
605
609
  const uriToFetchRiSc = (id) => `${riScUri}/${id}`;
606
610
  const uriToPublishRiSc = (id) => `${riScUri}/publish/${id}`;
607
- const uriToFetchLatestJSONSchema = () => `${baseUri}/api/risc/schemas/latest`;
611
+ const uriToFetchLatestJSONSchema = () => `${backendUrl}/api/proxy/risc-proxy/api/risc/schemas/latest`;
608
612
  const [response, setResponse] = useResponse();
609
613
  const fetch = (uri, method, onSuccess, onError, body) => {
610
614
  Promise.all([
@@ -934,6 +938,7 @@ const useFetchRiScs = (riScIdFromParams) => {
934
938
  const location = useLocation();
935
939
  const navigate = useNavigate();
936
940
  const getRiScPath = useRouteRef(riScRouteRef);
941
+ const [isRequesting, setIsRequesting] = useState(false);
937
942
  const {
938
943
  fetchRiScs,
939
944
  postRiScs,
@@ -1114,6 +1119,7 @@ const useFetchRiScs = (riScIdFromParams) => {
1114
1119
  isError: false,
1115
1120
  isSuccess: false
1116
1121
  });
1122
+ setIsRequesting(true);
1117
1123
  putRiScs(
1118
1124
  updatedRiSc,
1119
1125
  () => {
@@ -1126,6 +1132,7 @@ const useFetchRiScs = (riScIdFromParams) => {
1126
1132
  setRiScs(
1127
1133
  riScs.map((r) => r.id === selectedRiSc.id ? updatedRiSc : r)
1128
1134
  );
1135
+ setIsRequesting(false);
1129
1136
  },
1130
1137
  () => {
1131
1138
  setUpdateRiScStatus({
@@ -1133,6 +1140,7 @@ const useFetchRiScs = (riScIdFromParams) => {
1133
1140
  isError: true,
1134
1141
  isSuccess: false
1135
1142
  });
1143
+ setIsRequesting(false);
1136
1144
  }
1137
1145
  );
1138
1146
  }
@@ -1159,7 +1167,8 @@ const useFetchRiScs = (riScIdFromParams) => {
1159
1167
  createNewRiSc,
1160
1168
  updateRiSc,
1161
1169
  approveRiSc,
1162
- response
1170
+ response,
1171
+ isRequesting
1163
1172
  };
1164
1173
  };
1165
1174
 
@@ -1264,7 +1273,7 @@ const useInputFieldStyles = makeStyles((theme) => ({
1264
1273
  paper: {
1265
1274
  padding: theme.spacing(2),
1266
1275
  marginBottom: theme.spacing(2),
1267
- backgroundColor: theme.palette.type === "dark" ? "#404040" : "#e0e0e0"
1276
+ backgroundColor: theme.palette.type === "dark" ? "#404040" : "#FFFFFF"
1268
1277
  },
1269
1278
  root: {
1270
1279
  "&.Mui-disabled": {
@@ -1323,10 +1332,23 @@ const Dropdown = ({
1323
1332
  handleChange,
1324
1333
  error,
1325
1334
  required,
1326
- variant = "outlined"
1335
+ variant = "outlined",
1336
+ renderSelectedValue
1327
1337
  }) => {
1328
1338
  const { formLabel, formControl } = useInputFieldStyles();
1329
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
+ }
1330
1352
  const onChange = (event) => handleChange(event.target.value);
1331
1353
  const renderValue = (selected) => multiple ? /* @__PURE__ */ React.createElement(
1332
1354
  Box,
@@ -1338,7 +1360,7 @@ const Dropdown = ({
1338
1360
  paddingBottom: 0
1339
1361
  }
1340
1362
  },
1341
- selected.map((value) => /* @__PURE__ */ React.createElement(Chip, { key: value, label: value }))
1363
+ selected.map((value) => /* @__PURE__ */ React.createElement(Chip, { key: value, label: renderSelectedValueHandler(value) }))
1342
1364
  ) : selected;
1343
1365
  return /* @__PURE__ */ React.createElement(FormControl$1, { className: formControl }, label && /* @__PURE__ */ React.createElement(FormLabel$1, { className: formLabel, required }, label), /* @__PURE__ */ React.createElement(
1344
1366
  Select,
@@ -1358,7 +1380,7 @@ const Dropdown = ({
1358
1380
  }
1359
1381
  } : {}
1360
1382
  },
1361
- 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) })))
1362
1384
  ), error && /* @__PURE__ */ React.createElement(FormHelperText, { error: true }, error));
1363
1385
  };
1364
1386
 
@@ -1375,6 +1397,20 @@ const ScenarioStep = () => {
1375
1397
  setThreatActors,
1376
1398
  setVulnerabilities
1377
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
+ });
1378
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(
1379
1415
  TextField,
1380
1416
  {
@@ -1401,8 +1437,11 @@ const ScenarioStep = () => {
1401
1437
  Dropdown,
1402
1438
  {
1403
1439
  selectedValues: scenario.threatActors,
1404
- options: threatActorsOptions,
1405
- handleChange: setThreatActors
1440
+ options: translatedThreatActors,
1441
+ handleChange: setThreatActors,
1442
+ renderSelectedValue: (value) => {
1443
+ return t(`threatActors.${value}`);
1444
+ }
1406
1445
  }
1407
1446
  )
1408
1447
  ), /* @__PURE__ */ React.createElement(
@@ -1421,8 +1460,11 @@ const ScenarioStep = () => {
1421
1460
  Dropdown,
1422
1461
  {
1423
1462
  selectedValues: scenario.vulnerabilities,
1424
- options: vulnerabilitiesOptions,
1425
- handleChange: setVulnerabilities
1463
+ options: translatedVulnerabilities,
1464
+ handleChange: setVulnerabilities,
1465
+ renderSelectedValue: (value) => {
1466
+ return t(`vulnerabilities.${value}`);
1467
+ }
1426
1468
  }
1427
1469
  )
1428
1470
  ), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
@@ -1645,6 +1687,7 @@ const ProbabilityTable = ({
1645
1687
  selectedValue,
1646
1688
  handleChange
1647
1689
  }) => {
1690
+ const theme = useTheme();
1648
1691
  const { t } = useTranslationRef(pluginRiScTranslationRef);
1649
1692
  const { table, cell, radio } = useTableStyles$1();
1650
1693
  const handleChangeRow = (row) => () => handleChange(row);
@@ -1653,7 +1696,10 @@ const ProbabilityTable = ({
1653
1696
  {
1654
1697
  checked: selectedValue === row,
1655
1698
  onChange: handleChangeRow(row),
1656
- style: { padding: 0, color: "#9BC9FE" }
1699
+ style: {
1700
+ padding: 0,
1701
+ color: theme.palette.type === "dark" ? "#9BC9FE" : theme.palette.primary.main
1702
+ }
1657
1703
  }
1658
1704
  ), row, ": ", t(`probabilityTable.rows.${row}`)));
1659
1705
  const contentCell = (row) => /* @__PURE__ */ React.createElement("td", { className: cell }, t(`probabilityTable.cells.${row}`));
@@ -1664,6 +1710,7 @@ const ConsequenceTable = ({
1664
1710
  selectedValue,
1665
1711
  handleChange
1666
1712
  }) => {
1713
+ const theme = useTheme();
1667
1714
  const { t } = useTranslationRef(pluginRiScTranslationRef);
1668
1715
  const { table, labelCell, cell, voidCell, radio } = useTableStyles$1();
1669
1716
  const handleChangeRow = (row) => () => handleChange(row);
@@ -1673,7 +1720,10 @@ const ConsequenceTable = ({
1673
1720
  {
1674
1721
  checked: selectedValue === row,
1675
1722
  onChange: handleChangeRow(row),
1676
- style: { padding: 0, color: "#9BC9FE" }
1723
+ style: {
1724
+ padding: 0,
1725
+ color: theme.palette.type === "dark" ? theme.palette.primary.light : theme.palette.primary.main
1726
+ }
1677
1727
  }
1678
1728
  ), row, ": ", t(`consequenceTable.rows.${row}`)));
1679
1729
  };
@@ -1715,11 +1765,21 @@ const RiskStep = ({ riskType, restEqualsInitial }) => {
1715
1765
  useEffect(() => {
1716
1766
  if (restEqualsInitial)
1717
1767
  setRemainingConsequence(getConsequenceLevel(scenario.risk));
1718
- }, [restEqualsInitial, scenario.risk, scenario.risk.consequence, setRemainingConsequence]);
1768
+ }, [
1769
+ restEqualsInitial,
1770
+ scenario.risk,
1771
+ scenario.risk.consequence,
1772
+ setRemainingConsequence
1773
+ ]);
1719
1774
  useEffect(() => {
1720
1775
  if (restEqualsInitial)
1721
1776
  setRemainingProbability(getProbabilityLevel(scenario.risk));
1722
- }, [restEqualsInitial, scenario.risk, scenario.risk.probability, setRemainingProbability]);
1777
+ }, [
1778
+ restEqualsInitial,
1779
+ scenario.risk,
1780
+ scenario.risk.probability,
1781
+ setRemainingProbability
1782
+ ]);
1723
1783
  const setC = riskType === "initial" ? setConsequence : setRemainingConsequence;
1724
1784
  const setP = riskType === "initial" ? setProbability : setRemainingProbability;
1725
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(
@@ -1771,6 +1831,15 @@ const useWizardStyle = makeStyles((theme) => ({
1771
1831
  saveAndNextButtons: {
1772
1832
  display: "flex",
1773
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
+ }
1774
1843
  }
1775
1844
  }));
1776
1845
 
@@ -1808,6 +1877,7 @@ const ScenarioWizard = ({
1808
1877
  header,
1809
1878
  stepper,
1810
1879
  steps,
1880
+ button,
1811
1881
  buttonContainer,
1812
1882
  buttonContainerRight,
1813
1883
  saveAndNextButtons
@@ -1872,7 +1942,15 @@ const ScenarioWizard = ({
1872
1942
  const isLastStep = () => {
1873
1943
  return scenarioWizardSteps.indexOf(step) === scenarioWizardSteps.length - 1;
1874
1944
  };
1875
- 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(Button$1, { variant: "outlined", onClick: handleCloseStepper }, t("dictionary.cancel"))), /* @__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(
1876
1954
  Stepper,
1877
1955
  {
1878
1956
  className: stepper,
@@ -1920,6 +1998,7 @@ const ScenarioWizard = ({
1920
1998
  /* @__PURE__ */ React.createElement(Box, { className: saveAndNextButtons }, /* @__PURE__ */ React.createElement(
1921
1999
  Button$1,
1922
2000
  {
2001
+ className: button,
1923
2002
  variant: isLastStep() ? "contained" : "outlined",
1924
2003
  onClick: saveAndClose,
1925
2004
  disabled: updateStatus.isLoading
@@ -1981,7 +2060,6 @@ const useScenarioDrawerStyles = makeStyles((theme) => ({
1981
2060
  backgroundColor: theme.palette.type === "dark" ? "#555555" : "#e0e0e0"
1982
2061
  },
1983
2062
  gridContainer: {
1984
- padding: theme.spacing(2),
1985
2063
  marginBottom: theme.palette.type === "dark" ? theme.spacing(1) : "7px",
1986
2064
  backgroundColor: theme.palette.type === "dark" ? "#55555519" : "#e0e0e019"
1987
2065
  },
@@ -2083,7 +2161,7 @@ const RiskSection = () => {
2083
2161
  ));
2084
2162
  };
2085
2163
 
2086
- const useActionBoxStyles = makeStyles((_) => ({
2164
+ const useActionBoxStyles = makeStyles((theme) => ({
2087
2165
  alignCenter: {
2088
2166
  display: "flex",
2089
2167
  alignItems: "center"
@@ -2091,6 +2169,28 @@ const useActionBoxStyles = makeStyles((_) => ({
2091
2169
  justifyEnd: {
2092
2170
  display: "flex",
2093
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"
2094
2194
  }
2095
2195
  }));
2096
2196
 
@@ -2111,7 +2211,7 @@ const ChipDropdown = ({
2111
2211
  style: {
2112
2212
  margin: 0,
2113
2213
  padding: 0,
2114
- backgroundColor: "#D9D9D9",
2214
+ backgroundColor: selectedValue === "Completed" ? "#6BC6A4" : "#D9D9D9",
2115
2215
  color: "#000000"
2116
2216
  },
2117
2217
  label: selectedValue,
@@ -2150,7 +2250,14 @@ const ActionBox = ({
2150
2250
  const { t } = useTranslationRef(pluginRiScTranslationRef);
2151
2251
  const { body2, label } = useFontStyles();
2152
2252
  const { gridContainer } = useScenarioDrawerStyles();
2153
- const { alignCenter, justifyEnd } = useActionBoxStyles();
2253
+ const {
2254
+ alignCenter,
2255
+ row,
2256
+ paddingTop,
2257
+ actionDescription,
2258
+ column,
2259
+ chipDropdown
2260
+ } = useActionBoxStyles();
2154
2261
  const [previousAction, setPreviousAction] = useState(action);
2155
2262
  useEffect(() => {
2156
2263
  if (previousAction !== action) {
@@ -2164,19 +2271,28 @@ const ActionBox = ({
2164
2271
  status
2165
2272
  });
2166
2273
  };
2167
- 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: 6, className: justifyEnd }, /* @__PURE__ */ React.createElement(
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(
2168
2275
  ChipDropdown,
2169
2276
  {
2170
2277
  options: actionStatusOptions,
2171
2278
  selectedValue: action.status,
2172
2279
  handleChange: setStatus
2173
2280
  }
2174
- ))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Typography, { className: body2 }, action.description)), /* @__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, { item: true, xs: 4 }, /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.deadline")), /* @__PURE__ */ React.createElement(Typography, { className: body2 }, formatDate(action.deadline))));
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)));
2175
2291
  };
2176
2292
 
2177
2293
  const ActionsSection = () => {
2178
2294
  const { t } = useTranslationRef(pluginRiScTranslationRef);
2179
- const { h3, button } = useFontStyles();
2295
+ const { h3, button, label } = useFontStyles();
2180
2296
  const { titleAndButton, section, editIcon } = useScenarioDrawerStyles();
2181
2297
  const { scenario, editScenario, saveScenario, updateAction } = useContext(ScenarioContext);
2182
2298
  return /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(Paper, { className: section, style: { padding: "1rem" } }, /* @__PURE__ */ React.createElement(
@@ -2189,7 +2305,7 @@ const ActionsSection = () => {
2189
2305
  marginBottom: "12px"
2190
2306
  }
2191
2307
  },
2192
- /* @__PURE__ */ React.createElement(Typography, { className: h3, gutterBottom: true }, t("scenarioDrawer.measureTab.actionsTitle")),
2308
+ /* @__PURE__ */ React.createElement(Typography, { className: h3 }, t("scenarioDrawer.measureTab.actionsTitle")),
2193
2309
  /* @__PURE__ */ React.createElement(
2194
2310
  Button,
2195
2311
  {
@@ -2200,7 +2316,7 @@ const ActionsSection = () => {
2200
2316
  startIcon: /* @__PURE__ */ React.createElement(EditIcon, { className: editIcon, "aria-label": "Edit" })
2201
2317
  }
2202
2318
  )
2203
- ), 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(
2204
2320
  ActionBox,
2205
2321
  {
2206
2322
  action,
@@ -3143,10 +3259,19 @@ const RiScInfo = ({ riSc, approveRiSc, edit }) => {
3143
3259
  ), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, sm: 6, md: 4 }, /* @__PURE__ */ React.createElement(RiScStatusComponent, { selectedRiSc: riSc, publishRiScFn: approveRiSc })));
3144
3260
  };
3145
3261
 
3262
+ const useLinearProgressStyle = makeStyles((theme) => ({
3263
+ linearProgress: {
3264
+ position: "sticky",
3265
+ top: 0,
3266
+ margin: theme.spacing(2)
3267
+ }
3268
+ }));
3269
+
3146
3270
  const Plugin = () => {
3147
3271
  var _a, _b;
3148
3272
  const params = useParams();
3149
3273
  const { t } = useTranslationRef(pluginRiScTranslationRef);
3274
+ const { linearProgress } = useLinearProgressStyle();
3150
3275
  const [RiScDialogState, setRiScDialogState] = useState(
3151
3276
  RiScDialogStates.Closed
3152
3277
  );
@@ -3163,7 +3288,8 @@ const Plugin = () => {
3163
3288
  updateRiScStatus,
3164
3289
  resetRiScStatus,
3165
3290
  approveRiSc,
3166
- response
3291
+ response,
3292
+ isRequesting
3167
3293
  } = useFetchRiScs(params.riScId);
3168
3294
  const scenario = useScenarioDrawer(
3169
3295
  selectedRiSc != null ? selectedRiSc : null,
@@ -3176,7 +3302,7 @@ const Plugin = () => {
3176
3302
  if (scenarioWizardStep !== null)
3177
3303
  resetRiScStatus();
3178
3304
  }, [resetRiScStatus, scenarioWizardStep]);
3179
- 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(
3180
3306
  ScenarioWizard,
3181
3307
  {
3182
3308
  step: scenarioWizardStep,
@@ -3238,4 +3364,4 @@ const RiScPlugin = () => {
3238
3364
  };
3239
3365
 
3240
3366
  export { RiScPlugin };
3241
- //# sourceMappingURL=index-e091ae7d.esm.js.map
3367
+ //# sourceMappingURL=index-b5d09407.esm.js.map