@kartverket/backstage-plugin-risk-scorecard 1.0.14 → 1.1.0

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.
@@ -1,10 +1,10 @@
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, LinearProgress } from '@material-ui/core';
1
+ import React, { useState, useEffect, useCallback, Fragment, useRef, forwardRef } from 'react';
2
+ import { makeStyles, FormControl, FormLabel, FormHelperText, TextField as TextField$1, Grid, Typography, Box as Box$1, Paper, Button, useTheme, Radio, CircularProgress, Dialog, DialogTitle, DialogActions, Stepper, Step, StepButton, List, ListSubheader, ListItem, ListItemText as ListItemText$2, ClickAwayListener, Tooltip, DialogContent, DialogContentText, IconButton as IconButton$1, Tab, Link as Link$1, 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-d8d91dc1.esm.js';
7
- import { useNavigate, useLocation, useParams } from 'react-router';
6
+ import { r as riScRouteRef, s as scenarioRouteRef, p as pluginRiScTranslationRef } from './index-c11a7a42.esm.js';
7
+ import { useLocation, useNavigate, useParams } from 'react-router';
8
8
  import { useEffectOnce } from 'react-use';
9
9
  import { useSearchParams, Routes, Route } from 'react-router-dom';
10
10
  import Box from '@material-ui/core/Box';
@@ -15,25 +15,38 @@ import FormLabel$1 from '@material-ui/core/FormLabel';
15
15
  import ListItemIcon from '@material-ui/core/ListItemIcon';
16
16
  import ListItemText from '@material-ui/core/ListItemText';
17
17
  import MenuItem from '@material-ui/core/MenuItem';
18
- import Select from '@material-ui/core/Select';
18
+ import Select$1 from '@material-ui/core/Select';
19
19
  import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
20
20
  import Divider from '@mui/material/Divider';
21
21
  import Grid$1 from '@mui/material/Grid';
22
22
  import AddCircle from '@material-ui/icons/AddCircle';
23
23
  import DeleteIcon from '@material-ui/icons/Delete';
24
- import { LocalizationProvider, DatePicker } from '@mui/x-date-pickers';
25
- import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3';
26
- import { nb } from 'date-fns/locale/nb';
27
24
  import Button$1 from '@mui/material/Button';
28
25
  import Alert from '@mui/material/Alert';
29
26
  import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';
30
27
  import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
31
28
  import Box$2 from '@mui/material/Box';
29
+ import Paper$1 from '@mui/material/Paper';
30
+ import Typography$1 from '@mui/material/Typography';
32
31
  import KeyboardDoubleArrowRightIcon from '@mui/icons-material/KeyboardDoubleArrowRight';
33
- import EditIcon from '@mui/icons-material/Edit';
34
- import Menu from '@material-ui/core/Menu';
35
- import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
32
+ import Chip$1 from '@mui/material/Chip';
33
+ import Link from '@mui/material/Link';
36
34
  import DeleteIcon$1 from '@mui/icons-material/Delete';
35
+ import Dialog$1 from '@mui/material/Dialog';
36
+ import DialogTitle$1 from '@mui/material/DialogTitle';
37
+ import DialogActions$1 from '@mui/material/DialogActions';
38
+ import { useController, useFieldArray, useForm } from 'react-hook-form';
39
+ import FormControl$2 from '@mui/material/FormControl';
40
+ import FormHelperText$1 from '@mui/material/FormHelperText';
41
+ import FormLabel$2 from '@mui/material/FormLabel';
42
+ import MUISelect from '@mui/material/Select';
43
+ import Checkbox$1 from '@mui/material/Checkbox';
44
+ import MenuItem$1 from '@mui/material/MenuItem';
45
+ import ListItemText$1 from '@mui/material/ListItemText';
46
+ import TextField$2 from '@mui/material/TextField';
47
+ import { AddCircle as AddCircle$1 } from '@mui/icons-material';
48
+ import IconButton from '@mui/material/IconButton';
49
+ import Drawer from '@mui/material/Drawer';
37
50
  import CircleIcon from '@material-ui/icons/FiberManualRecord';
38
51
  import InfoIcon from '@mui/icons-material/Info';
39
52
  import TabContext from '@material-ui/lab/TabContext';
@@ -45,8 +58,10 @@ import TableCell from '@mui/material/TableCell';
45
58
  import TableContainer from '@mui/material/TableContainer';
46
59
  import TableHead from '@mui/material/TableHead';
47
60
  import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline';
48
- import CircleIcon$1 from '@mui/icons-material/Circle';
49
- import GitHubIcon from '@mui/icons-material/GitHub';
61
+ import CheckIcon from '@mui/icons-material/Check';
62
+ import EditIcon from '@mui/icons-material/Edit';
63
+ import { CacheProvider } from '@emotion/react';
64
+ import createCache from '@emotion/cache';
50
65
 
51
66
  var RiScStatus = /* @__PURE__ */ ((RiScStatus2) => {
52
67
  RiScStatus2["Draft"] = "Draft";
@@ -78,7 +93,7 @@ var ContentStatus = /* @__PURE__ */ ((ContentStatus2) => {
78
93
  })(ContentStatus || {});
79
94
 
80
95
  var $schema = "https://json-schema.org/draft/2020-12/schema";
81
- var $id = "https://kartverket.github.io/backstage-plugin-risk-scorecard-backend/risc_schema_en_v3_2.json";
96
+ var $id = "https://kartverket.github.io/backstage-plugin-risk-scorecard-backend/risc_schema_en_v3_3.json";
82
97
  var title = "Risk Scorecard (RiSc)";
83
98
  var description = "Schema for risk scorecard";
84
99
  var type = "object";
@@ -86,7 +101,7 @@ var properties = {
86
101
  schemaVersion: {
87
102
  description: "Schema version",
88
103
  type: "string",
89
- "default": "'3.2'"
104
+ "default": "'3.3'"
90
105
  },
91
106
  title: {
92
107
  description: "Title of the risk scorecard",
@@ -390,13 +405,6 @@ const formatter = new Intl.NumberFormat("nb-NO", {
390
405
  function formatNOK(amount) {
391
406
  return formatter.format(amount);
392
407
  }
393
- function formatDate(date) {
394
- return new Date(date).toLocaleDateString("nb-NO", {
395
- year: "numeric",
396
- month: "2-digit",
397
- day: "2-digit"
398
- });
399
- }
400
408
  function getAlertSeverity(status) {
401
409
  switch (status) {
402
410
  case ProcessingStatus.UpdatedRiSc:
@@ -415,39 +423,12 @@ function getRiskMatrixColor(risiko) {
415
423
  const getProbabilityLevel = (risiko) => probabilityOptions.indexOf(risiko.probability) + 1;
416
424
  const getConsequenceLevel = (risiko) => consequenceOptions.indexOf(risiko.consequence) + 1;
417
425
  const emptyRiSc = () => ({
418
- schemaVersion: "3.2",
426
+ schemaVersion: "3.3",
419
427
  title: "",
420
428
  scope: "",
421
429
  valuations: [],
422
430
  scenarios: []
423
431
  });
424
- const emptyScenario = () => ({
425
- ID: generateRandomId(),
426
- title: "",
427
- description: "",
428
- threatActors: [],
429
- vulnerabilities: [],
430
- risk: {
431
- summary: "",
432
- probability: 0.01,
433
- consequence: 1e3
434
- },
435
- existingActions: "",
436
- actions: [],
437
- remainingRisk: {
438
- summary: "",
439
- probability: 0.01,
440
- consequence: 1e3
441
- }
442
- });
443
- const emptyAction = () => ({
444
- ID: generateRandomId(),
445
- title: "",
446
- description: "",
447
- owner: "",
448
- deadline: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
449
- status: "Not started"
450
- });
451
432
  const requiresNewApproval = (oldRiSc, updatedRiSc) => {
452
433
  if (oldRiSc.scenarios.length !== updatedRiSc.scenarios.length) {
453
434
  return true;
@@ -505,8 +486,8 @@ function formatNumber(cost, t) {
505
486
  function dtoToRiSc(riScDTO) {
506
487
  return {
507
488
  ...riScDTO,
508
- // TODO implementere løsning for migrering, kan bumpe fra 3.1 til 3.2 på denne måten manuelt ved å åpne og lagre riscen:
509
- // skjemaVersjon: '3.2',
489
+ // TODO implementere løsning for migrering, kan bumpe fra 3.2 til 3.3 på denne måten manuelt ved å åpne og lagre riscen:
490
+ // skjemaVersjon: '3.3',
510
491
  scenarios: riScDTO.scenarios.map(dtoToScenario)
511
492
  };
512
493
  }
@@ -573,7 +554,8 @@ function actionToDTO(action) {
573
554
  description: action.description,
574
555
  owner: action.owner,
575
556
  deadline: action.deadline,
576
- status: action.status
557
+ status: action.status,
558
+ url: action.url
577
559
  }
578
560
  };
579
561
  }
@@ -651,7 +633,7 @@ const useFetch = () => {
651
633
  if (onError)
652
634
  onError();
653
635
  setResponse({
654
- statusMessage: "Failed to fetch JSON schema. Fallback value 3.2 for schema version used",
636
+ statusMessage: "Failed to fetch JSON schema. Fallback value 3.3 for schema version used",
655
637
  status: ProcessingStatus.ErrorWhenFetchingJSONSchema
656
638
  });
657
639
  });
@@ -717,223 +699,6 @@ const useFetch = () => {
717
699
  fetchLatestJSONSchema
718
700
  };
719
701
  };
720
- var ScenarioDrawerState = /* @__PURE__ */ ((ScenarioDrawerState2) => {
721
- ScenarioDrawerState2[ScenarioDrawerState2["Closed"] = 0] = "Closed";
722
- ScenarioDrawerState2[ScenarioDrawerState2["Edit"] = 1] = "Edit";
723
- ScenarioDrawerState2[ScenarioDrawerState2["View"] = 2] = "View";
724
- return ScenarioDrawerState2;
725
- })(ScenarioDrawerState || {});
726
- const emptyScenarioErrors = () => ({
727
- title: false
728
- });
729
- const useScenarioDrawer = (riSc, updateRiSc, scenarioIdFromParams) => {
730
- const [scenarioDrawerState, setScenarioDrawerState] = useState(
731
- 0 /* Closed */
732
- );
733
- const [isNewScenario, setIsNewScenario] = useState(false);
734
- const [scenario, setScenario] = useState(emptyScenario());
735
- const [originalScenario, setOriginalScenario] = useState(emptyScenario());
736
- const [deleteConfirmationIsOpen, setDeleteConfirmationIsOpen] = useState(false);
737
- const [, setSearchParams] = useSearchParams();
738
- const [scenarioErrors, setScenarioErrors] = useState(emptyScenarioErrors());
739
- const navigate = useNavigate();
740
- const getScenarioPath = useRouteRef(scenarioRouteRef);
741
- const getRiScPath = useRouteRef(riScRouteRef);
742
- useEffect(() => {
743
- if (riSc) {
744
- if (!scenarioIdFromParams) {
745
- setScenarioDrawerState(0 /* Closed */);
746
- const s = emptyScenario();
747
- setScenario(s);
748
- setOriginalScenario(s);
749
- setScenarioErrors(emptyScenarioErrors());
750
- return;
751
- }
752
- if (isNewScenario) {
753
- return;
754
- }
755
- const selectedScenario = riSc.content.scenarios.find(
756
- (s) => s.ID === scenarioIdFromParams
757
- );
758
- if (!selectedScenario) {
759
- navigate(getRiScPath({ riScId: riSc.id }), {
760
- state: "Risikoscenarioet du pr\xF8ver \xE5 \xE5pne eksisterer ikke"
761
- });
762
- return;
763
- }
764
- setScenario(selectedScenario);
765
- setOriginalScenario(selectedScenario);
766
- setScenarioDrawerState(2 /* View */);
767
- }
768
- }, [riSc, scenarioIdFromParams, getRiScPath, navigate, isNewScenario]);
769
- const openScenario = (id) => {
770
- if (riSc) {
771
- navigate(getScenarioPath({ riScId: riSc.id, scenarioId: id }));
772
- }
773
- };
774
- const openNewScenarioWizard = (id, step) => {
775
- if (riSc) {
776
- navigate(`${getScenarioPath({ riScId: riSc.id, scenarioId: id })}?step=${step}`);
777
- }
778
- };
779
- const closeScenario = () => {
780
- if (riSc) {
781
- setIsNewScenario(false);
782
- navigate(getRiScPath({ riScId: riSc.id }));
783
- }
784
- };
785
- const editScenario = (step) => {
786
- setScenarioDrawerState(0 /* Closed */);
787
- setSearchParams({ step });
788
- };
789
- const validateScenario = () => {
790
- if (scenario.title === "") {
791
- setScenarioErrors({
792
- ...scenarioErrors,
793
- title: true
794
- });
795
- return false;
796
- }
797
- return true;
798
- };
799
- const saveScenario = () => {
800
- if (riSc) {
801
- if (validateScenario()) {
802
- const updatedScenarios = riSc.content.scenarios.some(
803
- (s) => s.ID === scenario.ID
804
- ) ? riSc.content.scenarios.map(
805
- (s) => s.ID === scenario.ID ? scenario : s
806
- ) : riSc.content.scenarios.concat(scenario);
807
- updateRiSc({ ...riSc.content, scenarios: updatedScenarios });
808
- return true;
809
- }
810
- }
811
- return false;
812
- };
813
- const openDeleteConfirmation = () => setDeleteConfirmationIsOpen(true);
814
- const abortDeletion = () => setDeleteConfirmationIsOpen(false);
815
- const confirmDeletion = () => {
816
- if (riSc) {
817
- setDeleteConfirmationIsOpen(false);
818
- closeScenario();
819
- const updatedScenarios = riSc.content.scenarios.filter(
820
- (s) => s.ID !== scenario.ID
821
- );
822
- updateRiSc({ ...riSc.content, scenarios: updatedScenarios });
823
- }
824
- };
825
- const newScenario = () => {
826
- if (riSc) {
827
- setIsNewScenario(true);
828
- const s = emptyScenario();
829
- setScenario(s);
830
- setOriginalScenario(s);
831
- openNewScenarioWizard(s.ID, "scenario");
832
- }
833
- };
834
- const setTitle = (title) => {
835
- setScenarioErrors({
836
- ...scenarioErrors,
837
- title: false
838
- });
839
- setScenario({
840
- ...scenario,
841
- title
842
- });
843
- };
844
- const setDescription = (description) => {
845
- setScenario({
846
- ...scenario,
847
- description
848
- });
849
- };
850
- const setThreatActors = (threatActors) => {
851
- setScenario({
852
- ...scenario,
853
- threatActors
854
- });
855
- };
856
- const setVulnerabilities = (vulnerabilities) => {
857
- setScenario({
858
- ...scenario,
859
- vulnerabilities
860
- });
861
- };
862
- const setProbability = (probabilityLevel) => setScenario({
863
- ...scenario,
864
- risk: {
865
- ...scenario.risk,
866
- probability: probabilityOptions[probabilityLevel - 1]
867
- }
868
- });
869
- const setConsequence = (consequenceLevel) => setScenario({
870
- ...scenario,
871
- risk: {
872
- ...scenario.risk,
873
- consequence: consequenceOptions[consequenceLevel - 1]
874
- }
875
- });
876
- const setExistingActions = (existingActions) => {
877
- setScenario({
878
- ...scenario,
879
- existingActions
880
- });
881
- };
882
- const addAction = () => setScenario({ ...scenario, actions: [...scenario.actions, emptyAction()] });
883
- const updateAction = (action) => {
884
- const updatedAction = scenario.actions.some((a) => a.ID === action.ID) ? scenario.actions.map((a) => a.ID === action.ID ? action : a) : [...scenario.actions, action];
885
- setScenario({ ...scenario, actions: updatedAction });
886
- };
887
- const deleteAction = (action) => {
888
- const updatedAction = scenario.actions.filter((a) => a.ID !== action.ID);
889
- setScenario({ ...scenario, actions: updatedAction });
890
- };
891
- const updateRemainingRisk = (remainingRisk) => setScenario({ ...scenario, remainingRisk });
892
- const setRemainingProbability = (probabilityLevel) => setScenario({
893
- ...scenario,
894
- remainingRisk: {
895
- ...scenario.remainingRisk,
896
- probability: probabilityOptions[probabilityLevel - 1]
897
- }
898
- });
899
- const setRemainingConsequence = (consequenceLevel) => setScenario({
900
- ...scenario,
901
- remainingRisk: {
902
- ...scenario.remainingRisk,
903
- consequence: consequenceOptions[consequenceLevel - 1]
904
- }
905
- });
906
- return {
907
- scenarioDrawerState,
908
- scenario,
909
- originalScenario,
910
- newScenario,
911
- saveScenario,
912
- editScenario,
913
- isNewScenario,
914
- openScenario,
915
- closeScenario,
916
- scenarioErrors,
917
- validateScenario,
918
- deleteConfirmationIsOpen,
919
- openDeleteConfirmation,
920
- abortDeletion,
921
- confirmDeletion,
922
- setTitle,
923
- setDescription,
924
- setThreatActors,
925
- setVulnerabilities,
926
- setProbability,
927
- setConsequence,
928
- setExistingActions,
929
- addAction,
930
- updateAction,
931
- deleteAction,
932
- updateRemainingRisk,
933
- setRemainingProbability,
934
- setRemainingConsequence
935
- };
936
- };
937
702
  const useFetchRiScs = (riScIdFromParams) => {
938
703
  const location = useLocation();
939
704
  const navigate = useNavigate();
@@ -975,7 +740,8 @@ const useFetchRiScs = (riScIdFromParams) => {
975
740
  return {
976
741
  id: riScDTO.riScId,
977
742
  content,
978
- status: riScDTO.riScStatus
743
+ status: riScDTO.riScStatus,
744
+ pullRequestUrl: riScDTO.pullRequestUrl
979
745
  };
980
746
  });
981
747
  setRiScs(fetchedRiScs);
@@ -1043,7 +809,7 @@ const useFetchRiScs = (riScIdFromParams) => {
1043
809
  );
1044
810
  const newRiSc = {
1045
811
  ...riSc,
1046
- schemaVersion: schemaVersion ? schemaVersion : "3.2"
812
+ schemaVersion: schemaVersion ? schemaVersion : "3.3"
1047
813
  };
1048
814
  postRiScs(
1049
815
  newRiSc,
@@ -1070,7 +836,7 @@ const useFetchRiScs = (riScIdFromParams) => {
1070
836
  );
1071
837
  },
1072
838
  () => {
1073
- const fallBackSchemaVersion = "3.2";
839
+ const fallBackSchemaVersion = "3.3";
1074
840
  const newRiSc = {
1075
841
  ...riSc,
1076
842
  schemaVersion: fallBackSchemaVersion
@@ -1172,53 +938,314 @@ const useFetchRiScs = (riScIdFromParams) => {
1172
938
  };
1173
939
  };
1174
940
 
1175
- const menuProps = {
1176
- PaperProps: {
1177
- style: {
1178
- maxHeight: 255
1179
- }
1180
- },
1181
- getContentAnchorEl: null,
1182
- anchorOrigin: {
1183
- vertical: "bottom",
1184
- horizontal: "center"
1185
- },
1186
- transformOrigin: {
1187
- vertical: "top",
1188
- horizontal: "center"
1189
- },
1190
- variant: "menu"
1191
- };
1192
- const useFontStyles = makeStyles((theme) => ({
1193
- h1: {
1194
- fontSize: theme.spacing(4),
1195
- fontWeight: 700
1196
- },
1197
- h2: {
1198
- fontSize: theme.spacing(3.5),
1199
- fontWeight: 500
1200
- },
1201
- h3: {
1202
- fontSize: theme.spacing(2.5),
1203
- fontWeight: 500
1204
- },
1205
- body1: {
1206
- fontSize: theme.spacing(2),
1207
- fontWeight: 700
941
+ const emptyAction = () => ({
942
+ ID: generateRandomId(),
943
+ title: "",
944
+ description: "",
945
+ owner: "",
946
+ deadline: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
947
+ status: "Not started",
948
+ url: ""
949
+ });
950
+ const emptyScenario = () => ({
951
+ ID: generateRandomId(),
952
+ title: "",
953
+ description: "",
954
+ threatActors: [],
955
+ vulnerabilities: [],
956
+ risk: {
957
+ summary: "",
958
+ probability: 0.01,
959
+ consequence: 1e3
960
+ },
961
+ existingActions: "",
962
+ actions: [],
963
+ remainingRisk: {
964
+ summary: "",
965
+ probability: 0.01,
966
+ consequence: 1e3
967
+ }
968
+ });
969
+ const ScenarioContext = React.createContext(
970
+ void 0
971
+ );
972
+ const ScenarioProvider = ({
973
+ riSc,
974
+ updateRiSc,
975
+ scenarioIdFromParams,
976
+ children
977
+ }) => {
978
+ const [isDrawerOpen, setIsDrawerOpen] = useState(false);
979
+ const [isNewScenario, setIsNewScenario] = useState(false);
980
+ const [formErrors, _setFormErrors] = useState({});
981
+ const [scenario, setScenario] = useState(emptyScenario());
982
+ const [originalScenario, setOriginalScenario] = useState(emptyScenario());
983
+ const [deleteConfirmationIsOpen, setDeleteConfirmationIsOpen] = useState(false);
984
+ const [, setSearchParams] = useSearchParams();
985
+ const navigate = useNavigate();
986
+ const getScenarioPath = useRouteRef(scenarioRouteRef);
987
+ const getRiScPath = useRouteRef(riScRouteRef);
988
+ useEffect(() => {
989
+ if (riSc) {
990
+ if (!scenarioIdFromParams) {
991
+ setIsDrawerOpen(false);
992
+ const s = emptyScenario();
993
+ setScenario(s);
994
+ setOriginalScenario(s);
995
+ return;
996
+ }
997
+ if (isNewScenario) {
998
+ return;
999
+ }
1000
+ const selectedScenario = riSc.content.scenarios.find(
1001
+ (s) => s.ID === scenarioIdFromParams
1002
+ );
1003
+ if (!selectedScenario) {
1004
+ navigate(getRiScPath({ riScId: riSc.id }), {
1005
+ state: "Risikoscenarioet du pr\xF8ver \xE5 \xE5pne eksisterer ikke"
1006
+ });
1007
+ return;
1008
+ }
1009
+ setScenario(selectedScenario);
1010
+ setOriginalScenario(selectedScenario);
1011
+ setIsDrawerOpen(true);
1012
+ }
1013
+ }, [riSc, scenarioIdFromParams, getRiScPath, navigate, isNewScenario]);
1014
+ const openScenario = (id) => {
1015
+ if (riSc) {
1016
+ navigate(getScenarioPath({ riScId: riSc.id, scenarioId: id }));
1017
+ }
1018
+ };
1019
+ const openNewScenarioWizard = (id, step) => {
1020
+ if (riSc) {
1021
+ navigate(
1022
+ `${getScenarioPath({ riScId: riSc.id, scenarioId: id })}?step=${step}`
1023
+ );
1024
+ }
1025
+ };
1026
+ const closeScenario = () => {
1027
+ if (riSc) {
1028
+ setIsNewScenario(false);
1029
+ navigate(getRiScPath({ riScId: riSc.id }));
1030
+ }
1031
+ };
1032
+ const editScenario = (step) => {
1033
+ setSearchParams({ step });
1034
+ };
1035
+ const validateScenario = () => {
1036
+ if (scenario.title === "") {
1037
+ return false;
1038
+ }
1039
+ return true;
1040
+ };
1041
+ const saveScenario = () => {
1042
+ if (riSc) {
1043
+ if (validateScenario()) {
1044
+ const updatedScenarios = riSc.content.scenarios.some(
1045
+ (s) => s.ID === scenario.ID
1046
+ ) ? riSc.content.scenarios.map(
1047
+ (s) => s.ID === scenario.ID ? scenario : s
1048
+ ) : riSc.content.scenarios.concat(scenario);
1049
+ updateRiSc({ ...riSc.content, scenarios: updatedScenarios });
1050
+ return true;
1051
+ }
1052
+ }
1053
+ return false;
1054
+ };
1055
+ const submitEditedScenarioToRiSc = (editedScenario) => {
1056
+ if (riSc) {
1057
+ updateRiSc({
1058
+ ...riSc.content,
1059
+ scenarios: riSc.content.scenarios.map(
1060
+ (s) => s.ID === editedScenario.ID ? editedScenario : s
1061
+ )
1062
+ });
1063
+ }
1064
+ };
1065
+ const openDeleteConfirmation = () => setDeleteConfirmationIsOpen(true);
1066
+ const abortDeletion = () => setDeleteConfirmationIsOpen(false);
1067
+ const confirmDeletion = () => {
1068
+ if (riSc) {
1069
+ setDeleteConfirmationIsOpen(false);
1070
+ closeScenario();
1071
+ const updatedScenarios = riSc.content.scenarios.filter(
1072
+ (s) => s.ID !== scenario.ID
1073
+ );
1074
+ updateRiSc({ ...riSc.content, scenarios: updatedScenarios });
1075
+ }
1076
+ };
1077
+ const newScenario = () => {
1078
+ if (riSc) {
1079
+ setIsNewScenario(true);
1080
+ const s = emptyScenario();
1081
+ setScenario(s);
1082
+ setOriginalScenario(s);
1083
+ openNewScenarioWizard(s.ID, "scenario");
1084
+ }
1085
+ };
1086
+ const setScenarioValue = (key, value2) => {
1087
+ setScenario({
1088
+ ...scenario,
1089
+ [key]: value2
1090
+ });
1091
+ };
1092
+ const addAction = () => setScenario({ ...scenario, actions: [...scenario.actions, emptyAction()] });
1093
+ const updateAction = (action) => {
1094
+ const updatedAction = scenario.actions.some((a) => a.ID === action.ID) ? scenario.actions.map((a) => a.ID === action.ID ? action : a) : [...scenario.actions, action];
1095
+ setScenario({ ...scenario, actions: updatedAction });
1096
+ };
1097
+ const deleteAction = (action) => {
1098
+ const updatedAction = scenario.actions.filter((a) => a.ID !== action.ID);
1099
+ setScenario({ ...scenario, actions: updatedAction });
1100
+ };
1101
+ const setProbability = (probabilityLevel) => setScenario({
1102
+ ...scenario,
1103
+ risk: {
1104
+ ...scenario.risk,
1105
+ probability: probabilityOptions[probabilityLevel - 1]
1106
+ }
1107
+ });
1108
+ const setConsequence = (consequenceLevel) => setScenario({
1109
+ ...scenario,
1110
+ risk: {
1111
+ ...scenario.risk,
1112
+ consequence: consequenceOptions[consequenceLevel - 1]
1113
+ }
1114
+ });
1115
+ const setRemainingProbability = (probabilityLevel) => setScenario({
1116
+ ...scenario,
1117
+ remainingRisk: {
1118
+ ...scenario.remainingRisk,
1119
+ probability: probabilityOptions[probabilityLevel - 1]
1120
+ }
1121
+ });
1122
+ const setRemainingConsequence = (consequenceLevel) => setScenario({
1123
+ ...scenario,
1124
+ remainingRisk: {
1125
+ ...scenario.remainingRisk,
1126
+ consequence: consequenceOptions[consequenceLevel - 1]
1127
+ }
1128
+ });
1129
+ const setProbabilityAndRemainingProbability = (probabilityLevel) => setScenario({
1130
+ ...scenario,
1131
+ risk: {
1132
+ ...scenario.risk,
1133
+ probability: probabilityOptions[probabilityLevel - 1]
1134
+ },
1135
+ remainingRisk: {
1136
+ ...scenario.remainingRisk,
1137
+ probability: probabilityOptions[probabilityLevel - 1]
1138
+ }
1139
+ });
1140
+ const setConsequenceAndRemainingConsequence = (consequenceLevel) => setScenario({
1141
+ ...scenario,
1142
+ risk: {
1143
+ ...scenario.risk,
1144
+ consequence: consequenceOptions[consequenceLevel - 1]
1145
+ },
1146
+ remainingRisk: {
1147
+ ...scenario.remainingRisk,
1148
+ consequence: consequenceOptions[consequenceLevel - 1]
1149
+ }
1150
+ });
1151
+ const setFormError = (key) => {
1152
+ _setFormErrors({ ...formErrors, [key]: true });
1153
+ };
1154
+ const removeFormError = (key) => {
1155
+ delete formErrors[key];
1156
+ _setFormErrors({ ...formErrors });
1157
+ };
1158
+ const formFieldHasErrors = (key) => Object.keys(formErrors).includes(key);
1159
+ const hasFormErrors = () => Object.keys(formErrors).length > 0;
1160
+ const value = {
1161
+ isDrawerOpen,
1162
+ scenario,
1163
+ originalScenario,
1164
+ newScenario,
1165
+ saveScenario,
1166
+ editScenario,
1167
+ isNewScenario,
1168
+ submitEditedScenarioToRiSc,
1169
+ openScenario,
1170
+ closeScenario,
1171
+ validateScenario,
1172
+ deleteConfirmationIsOpen,
1173
+ openDeleteConfirmation,
1174
+ abortDeletion,
1175
+ confirmDeletion,
1176
+ setScenarioValue,
1177
+ addAction,
1178
+ updateAction,
1179
+ deleteAction,
1180
+ setProbability,
1181
+ setConsequence,
1182
+ setRemainingProbability,
1183
+ setRemainingConsequence,
1184
+ setProbabilityAndRemainingProbability,
1185
+ setConsequenceAndRemainingConsequence,
1186
+ setFormError,
1187
+ hasFormErrors,
1188
+ removeFormError,
1189
+ formFieldHasErrors
1190
+ };
1191
+ return /* @__PURE__ */ React.createElement(ScenarioContext.Provider, { value }, children);
1192
+ };
1193
+ const useScenario = () => {
1194
+ const context = React.useContext(ScenarioContext);
1195
+ if (context === void 0) {
1196
+ throw new Error("useScenario must be used within a ScenarioProvider");
1197
+ }
1198
+ return context;
1199
+ };
1200
+
1201
+ const menuProps = {
1202
+ PaperProps: {
1203
+ style: {
1204
+ maxHeight: 255
1205
+ }
1206
+ },
1207
+ getContentAnchorEl: null,
1208
+ anchorOrigin: {
1209
+ vertical: "bottom",
1210
+ horizontal: "center"
1211
+ },
1212
+ transformOrigin: {
1213
+ vertical: "top",
1214
+ horizontal: "center"
1215
+ },
1216
+ variant: "menu"
1217
+ };
1218
+ const useFontStyles = makeStyles((theme) => ({
1219
+ h1: {
1220
+ fontSize: theme.spacing(4),
1221
+ fontWeight: 700
1222
+ },
1223
+ h2: {
1224
+ fontSize: theme.spacing(3.5),
1225
+ fontWeight: 500
1226
+ },
1227
+ h3: {
1228
+ fontSize: theme.spacing(2.5),
1229
+ fontWeight: 500
1230
+ },
1231
+ body1: {
1232
+ fontSize: theme.spacing(2),
1233
+ fontWeight: 500
1208
1234
  },
1209
1235
  body2: {
1210
1236
  fontSize: theme.spacing(2),
1211
- fontWeight: 400
1237
+ fontWeight: 400,
1238
+ whiteSpace: "pre-line"
1212
1239
  },
1213
1240
  label: {
1214
1241
  fontSize: theme.spacing(1.75),
1215
- fontWeight: 500,
1242
+ fontWeight: 700,
1216
1243
  textTransform: "uppercase",
1217
1244
  color: theme.palette.type === "dark" ? "#F8F8F8" : "rgba(0, 0, 0, 0.87)",
1218
1245
  paddingBottom: "0.4rem"
1219
1246
  },
1220
1247
  labelSubtitle: {
1221
- fontSize: theme.spacing(1.5),
1248
+ fontSize: theme.spacing(1.75),
1222
1249
  fontWeight: 400,
1223
1250
  color: theme.palette.type === "dark" ? "#F8F8F8" : "rgba(0, 0, 0, 0.87)",
1224
1251
  paddingBottom: "0.4rem",
@@ -1244,9 +1271,6 @@ const useFontStyles = makeStyles((theme) => ({
1244
1271
  paddingBottom: "1rem",
1245
1272
  marginTop: "-0.2rem"
1246
1273
  },
1247
- button: {
1248
- textTransform: "none"
1249
- },
1250
1274
  risikoLevel: {
1251
1275
  fontSize: theme.spacing(2.25),
1252
1276
  fontWeight: 700,
@@ -1299,14 +1323,40 @@ const TextField = ({
1299
1323
  label,
1300
1324
  subtitle,
1301
1325
  value,
1302
- error,
1303
1326
  required,
1304
1327
  minRows = 1,
1305
- handleChange
1328
+ handleChange,
1329
+ validateInput,
1330
+ errorMessage,
1331
+ errorKey
1306
1332
  }) => {
1333
+ const [hasError, setHasError] = useState(false);
1334
+ const { setFormError, removeFormError, formFieldHasErrors } = useScenario();
1307
1335
  const { formLabel, formControl, root } = useInputFieldStyles();
1308
1336
  const { labelSubtitle } = useFontStyles();
1309
- const onChange = (event) => handleChange && handleChange(event.target.value);
1337
+ const onChange = (event) => {
1338
+ if (handleChange) {
1339
+ handleChange(event.target.value);
1340
+ }
1341
+ };
1342
+ const onBlur = (event) => {
1343
+ if (!validateInput && !required) {
1344
+ return;
1345
+ }
1346
+ const isValid = validateInput ? validateInput(event.target.value) : true;
1347
+ const requiredValid = required ? event.target.value.length > 0 : true;
1348
+ const validationResult = isValid && requiredValid;
1349
+ setHasError(!validationResult);
1350
+ if (!errorKey) {
1351
+ return;
1352
+ }
1353
+ if (validationResult) {
1354
+ removeFormError(errorKey);
1355
+ } else {
1356
+ setFormError(errorKey);
1357
+ }
1358
+ };
1359
+ const fieldError = hasError || (errorKey ? formFieldHasErrors(errorKey) : false);
1310
1360
  return /* @__PURE__ */ React.createElement(FormControl, { className: formControl }, label && /* @__PURE__ */ React.createElement(FormLabel, { className: formLabel, required }, label), subtitle && /* @__PURE__ */ React.createElement(FormHelperText, { className: labelSubtitle }, subtitle), /* @__PURE__ */ React.createElement(
1311
1361
  TextField$1,
1312
1362
  {
@@ -1319,10 +1369,11 @@ const TextField = ({
1319
1369
  minRows,
1320
1370
  variant: "outlined",
1321
1371
  onChange,
1322
- error: !!error,
1372
+ error: fieldError,
1373
+ onBlur,
1323
1374
  InputProps: { className: root }
1324
1375
  }
1325
- ), error && /* @__PURE__ */ React.createElement(FormHelperText, { error: true }, error));
1376
+ ), fieldError && /* @__PURE__ */ React.createElement(FormHelperText, { error: true }, errorMessage));
1326
1377
  };
1327
1378
 
1328
1379
  const Dropdown = ({
@@ -1363,7 +1414,7 @@ const Dropdown = ({
1363
1414
  selected.map((value) => /* @__PURE__ */ React.createElement(Chip, { key: value, label: renderSelectedValueHandler(value) }))
1364
1415
  ) : selected;
1365
1416
  return /* @__PURE__ */ React.createElement(FormControl$1, { className: formControl }, label && /* @__PURE__ */ React.createElement(FormLabel$1, { className: formLabel, required }, label), /* @__PURE__ */ React.createElement(
1366
- Select,
1417
+ Select$1,
1367
1418
  {
1368
1419
  multiple,
1369
1420
  value: selectedValues,
@@ -1380,23 +1431,19 @@ const Dropdown = ({
1380
1431
  }
1381
1432
  } : {}
1382
1433
  },
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) })))
1434
+ options.map((name) => /* @__PURE__ */ React.createElement(MenuItem, { key: getOptionsValue(name), value: getOptionsValue(name) }, multiple && /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(
1435
+ Checkbox,
1436
+ {
1437
+ checked: selectedValues.indexOf(getOptionsValue(name)) > -1
1438
+ }
1439
+ )), /* @__PURE__ */ React.createElement(ListItemText, { primary: getOptionsRenderValue(name) })))
1384
1440
  ), error && /* @__PURE__ */ React.createElement(FormHelperText, { error: true }, error));
1385
1441
  };
1386
1442
 
1387
- const ScenarioContext = createContext(null);
1388
-
1389
1443
  const ScenarioStep = () => {
1390
1444
  const { t } = useTranslationRef(pluginRiScTranslationRef);
1391
1445
  const { labelSubtitle, label, h2, subtitle2 } = useFontStyles();
1392
- const {
1393
- scenario,
1394
- scenarioErrors,
1395
- setTitle,
1396
- setDescription,
1397
- setThreatActors,
1398
- setVulnerabilities
1399
- } = useContext(ScenarioContext);
1446
+ const { scenario, setScenarioValue } = useScenario();
1400
1447
  const translatedThreatActors = threatActorsOptions.map((threatActor) => {
1401
1448
  return {
1402
1449
  value: threatActor,
@@ -1404,22 +1451,25 @@ const ScenarioStep = () => {
1404
1451
  renderedValue: t(`threatActors.${threatActor}`)
1405
1452
  };
1406
1453
  });
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
- });
1454
+ const translatedVulnerabilities = vulnerabilitiesOptions.map(
1455
+ (vulnerability) => {
1456
+ return {
1457
+ value: vulnerability,
1458
+ /* @ts-ignore Because ts can't typecheck strings agains our keys */
1459
+ renderedValue: t(`vulnerabilities.${vulnerability}`)
1460
+ };
1461
+ }
1462
+ );
1414
1463
  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(
1415
1464
  TextField,
1416
1465
  {
1417
1466
  label: t("dictionary.title"),
1418
1467
  value: scenario.title,
1419
- error: scenarioErrors.title ? t("scenarioDrawer.titleError") : "",
1468
+ errorMessage: t("scenarioDrawer.titleError"),
1469
+ errorKey: "scenario-title",
1420
1470
  required: true,
1421
1471
  minRows: 1,
1422
- handleChange: setTitle
1472
+ handleChange: (value) => setScenarioValue("title", value)
1423
1473
  }
1424
1474
  )), /* @__PURE__ */ React.createElement(
1425
1475
  Grid,
@@ -1438,7 +1488,7 @@ const ScenarioStep = () => {
1438
1488
  {
1439
1489
  selectedValues: scenario.threatActors,
1440
1490
  options: translatedThreatActors,
1441
- handleChange: setThreatActors,
1491
+ handleChange: (value) => setScenarioValue("threatActors", value),
1442
1492
  renderSelectedValue: (value) => {
1443
1493
  return t(`threatActors.${value}`);
1444
1494
  }
@@ -1461,7 +1511,7 @@ const ScenarioStep = () => {
1461
1511
  {
1462
1512
  selectedValues: scenario.vulnerabilities,
1463
1513
  options: translatedVulnerabilities,
1464
- handleChange: setVulnerabilities,
1514
+ handleChange: (value) => setScenarioValue("vulnerabilities", value),
1465
1515
  renderSelectedValue: (value) => {
1466
1516
  return t(`vulnerabilities.${value}`);
1467
1517
  }
@@ -1473,7 +1523,7 @@ const ScenarioStep = () => {
1473
1523
  label: t("dictionary.description"),
1474
1524
  value: scenario.description,
1475
1525
  minRows: 4,
1476
- handleChange: setDescription
1526
+ handleChange: (value) => setScenarioValue("description", value)
1477
1527
  }
1478
1528
  )));
1479
1529
  };
@@ -1485,30 +1535,13 @@ const ActionEdit = ({
1485
1535
  deleteAction
1486
1536
  }) => {
1487
1537
  const { t } = useTranslationRef(pluginRiScTranslationRef);
1488
- const { paper, formControl } = useInputFieldStyles();
1489
- const { label, labelSubtitle } = useFontStyles();
1490
- const theme = useTheme();
1491
- const setDescription = (description) => updateAction({
1492
- ...action,
1493
- description
1494
- });
1495
- const setStatus = (status) => updateAction({
1496
- ...action,
1497
- status
1498
- });
1499
- const setDeadline = (deadline) => {
1500
- if (deadline) {
1501
- const formattedDeadline = new Date(deadline).toISOString().split("T")[0];
1502
- updateAction({
1503
- ...action,
1504
- deadline: formattedDeadline
1505
- });
1506
- }
1538
+ const { paper } = useInputFieldStyles();
1539
+ const { label } = useFontStyles();
1540
+ const setActionField = (field, value) => updateAction({ ...action, [field]: value });
1541
+ const validateUrl = (value) => {
1542
+ const urlRegex = /^https?:\/\/[a-z\d.-]+\.[a-z]{2,6}(\/[\w .-]*)*\/?$/;
1543
+ return urlRegex.test(value);
1507
1544
  };
1508
- const setOwner = (owner) => updateAction({
1509
- ...action,
1510
- owner
1511
- });
1512
1545
  return /* @__PURE__ */ React.createElement(Paper, { className: paper }, /* @__PURE__ */ React.createElement(Grid, { container: true, style: { display: "flex", rowGap: "0.7rem" } }, /* @__PURE__ */ React.createElement(
1513
1546
  Grid,
1514
1547
  {
@@ -1536,24 +1569,24 @@ const ActionEdit = ({
1536
1569
  {
1537
1570
  label: t("dictionary.description"),
1538
1571
  value: action.description,
1539
- handleChange: setDescription,
1540
- minRows: 1
1572
+ handleChange: (value) => setActionField("description", value),
1573
+ errorMessage: t("scenarioDrawer.action.descriptionError"),
1574
+ errorKey: `${index}-action-description`,
1575
+ minRows: 1,
1576
+ required: true
1541
1577
  }
1542
- )), /* @__PURE__ */ React.createElement(
1543
- Grid,
1578
+ )), /* @__PURE__ */ React.createElement(Grid, { item: true, md: 9, xs: 12 }, /* @__PURE__ */ React.createElement(
1579
+ TextField,
1544
1580
  {
1545
- item: true,
1546
- md: 6,
1547
- xs: 12,
1548
- style: {
1549
- display: "flex",
1550
- flexDirection: "column"
1551
- }
1552
- },
1553
- /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.measureOwner")),
1554
- /* @__PURE__ */ React.createElement(Typography, { className: labelSubtitle }, t("scenarioDrawer.measureTab.measureOwnerDescription")),
1555
- /* @__PURE__ */ React.createElement(TextField, { value: action.owner, handleChange: setOwner, minRows: 1 })
1556
- ), /* @__PURE__ */ React.createElement(
1581
+ label: t("dictionary.url"),
1582
+ value: action.url,
1583
+ handleChange: (value) => setActionField("url", value),
1584
+ validateInput: validateUrl,
1585
+ errorMessage: t("scenarioDrawer.action.urlError"),
1586
+ minRows: 1,
1587
+ errorKey: `${index}-action-url`
1588
+ }
1589
+ )), /* @__PURE__ */ React.createElement(
1557
1590
  Grid,
1558
1591
  {
1559
1592
  item: true,
@@ -1561,75 +1594,33 @@ const ActionEdit = ({
1561
1594
  xs: 12,
1562
1595
  style: {
1563
1596
  display: "flex",
1564
- flexDirection: "column",
1565
- justifyContent: "flex-end"
1597
+ flexDirection: "column"
1566
1598
  }
1567
1599
  },
1568
- /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.status")),
1569
1600
  /* @__PURE__ */ React.createElement(
1570
1601
  Dropdown,
1571
1602
  {
1572
- options: actionStatusOptions,
1573
- selectedValues: action.status,
1574
- handleChange: setStatus
1575
- }
1576
- )
1577
- ), /* @__PURE__ */ React.createElement(
1578
- Grid,
1579
- {
1580
- item: true,
1581
- md: 3,
1582
- xs: 12,
1583
- style: {
1584
- display: "flex",
1585
- flexDirection: "column",
1586
- justifyContent: "flex-end"
1587
- }
1588
- },
1589
- /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.deadline")),
1590
- /* @__PURE__ */ React.createElement(FormControl$1, { className: formControl }, /* @__PURE__ */ React.createElement(LocalizationProvider, { dateAdapter: AdapterDateFns, adapterLocale: nb }, /* @__PURE__ */ React.createElement(
1591
- DatePicker,
1592
- {
1593
- value: action.deadline,
1594
- onChange: setDeadline,
1595
- sx: {
1596
- ".MuiInputBase-root": {
1597
- color: theme.palette.type === "dark" ? "white" : "black"
1598
- },
1599
- ".MuiOutlinedInput-notchedOutline": {
1600
- borderColor: theme.palette.type === "dark" ? "#616161" : "#0000003b"
1601
- },
1602
- ".MuiOutlinedInput-root": {
1603
- "&:hover .MuiOutlinedInput-notchedOutline": {
1604
- borderColor: theme.palette.type === "dark" ? "white" : "black"
1605
- }
1606
- },
1607
- ".MuiIconButton-root": {
1608
- color: theme.palette.type === "dark" ? "white" : "black"
1609
- }
1610
- }
1603
+ options: actionStatusOptions,
1604
+ selectedValues: action.status,
1605
+ handleChange: (value) => setActionField("status", value),
1606
+ label: t("dictionary.status"),
1607
+ required: true
1611
1608
  }
1612
- )))
1609
+ )
1613
1610
  )));
1614
1611
  };
1615
1612
 
1616
1613
  const ActionsStep = () => {
1617
1614
  const { t } = useTranslationRef(pluginRiScTranslationRef);
1618
1615
  const { h2, subtitle2, actionSubtitle } = useFontStyles();
1619
- const {
1620
- scenario,
1621
- setExistingActions,
1622
- updateAction,
1623
- deleteAction,
1624
- addAction
1625
- } = useContext(ScenarioContext);
1616
+ const { scenario, setScenarioValue, updateAction, deleteAction, addAction } = useScenario();
1626
1617
  return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Typography, { className: h2 }, t("dictionary.measure")), /* @__PURE__ */ React.createElement(Typography, { className: subtitle2 }, t("scenarioDrawer.measureTab.subtitle")), /* @__PURE__ */ React.createElement(Grid$1, { item: true, xs: 12, style: { paddingTop: "1.5rem" } }, /* @__PURE__ */ React.createElement(
1627
1618
  TextField,
1628
1619
  {
1629
1620
  label: t("scenarioDrawer.measureTab.existingMeasure"),
1630
1621
  subtitle: t("scenarioDrawer.measureTab.existingMeasureSubtitle"),
1631
1622
  value: scenario.existingActions,
1632
- handleChange: setExistingActions,
1623
+ handleChange: (value) => setScenarioValue("existingActions", value),
1633
1624
  minRows: 3
1634
1625
  }
1635
1626
  )), /* @__PURE__ */ React.createElement(Typography, { variant: "h6", className: actionSubtitle }, t("scenarioDrawer.measureTab.plannedMeasures")), scenario.actions.map((action, index) => /* @__PURE__ */ React.createElement(
@@ -1690,12 +1681,11 @@ const ProbabilityTable = ({
1690
1681
  const theme = useTheme();
1691
1682
  const { t } = useTranslationRef(pluginRiScTranslationRef);
1692
1683
  const { table, cell, radio } = useTableStyles$1();
1693
- const handleChangeRow = (row) => () => handleChange(row);
1694
1684
  const radioCell = (row) => /* @__PURE__ */ React.createElement("th", { scope: "col" }, /* @__PURE__ */ React.createElement("div", { className: radio }, /* @__PURE__ */ React.createElement(
1695
1685
  Radio,
1696
1686
  {
1697
1687
  checked: selectedValue === row,
1698
- onChange: handleChangeRow(row),
1688
+ onChange: () => handleChange(row),
1699
1689
  style: {
1700
1690
  padding: 0,
1701
1691
  color: theme.palette.type === "dark" ? "#9BC9FE" : theme.palette.primary.main
@@ -1713,13 +1703,12 @@ const ConsequenceTable = ({
1713
1703
  const theme = useTheme();
1714
1704
  const { t } = useTranslationRef(pluginRiScTranslationRef);
1715
1705
  const { table, labelCell, cell, voidCell, radio } = useTableStyles$1();
1716
- const handleChangeRow = (row) => () => handleChange(row);
1717
1706
  const getRadioCell = (row) => {
1718
1707
  return /* @__PURE__ */ React.createElement("th", { scope: "col" }, /* @__PURE__ */ React.createElement("div", { className: radio }, /* @__PURE__ */ React.createElement(
1719
1708
  Radio,
1720
1709
  {
1721
1710
  checked: selectedValue === row,
1722
- onChange: handleChangeRow(row),
1711
+ onChange: () => handleChange(row),
1723
1712
  style: {
1724
1713
  padding: 0,
1725
1714
  color: theme.palette.type === "dark" ? theme.palette.primary.light : theme.palette.primary.main
@@ -1758,41 +1747,45 @@ const RiskStep = ({ riskType, restEqualsInitial }) => {
1758
1747
  setConsequence,
1759
1748
  setProbability,
1760
1749
  setRemainingConsequence,
1761
- setRemainingProbability
1762
- } = useContext(ScenarioContext);
1750
+ setRemainingProbability,
1751
+ setProbabilityAndRemainingProbability,
1752
+ setConsequenceAndRemainingConsequence
1753
+ } = useScenario();
1763
1754
  const resourceKey = riskType === "initial" ? "initialRiskStep" : "restRiskStep";
1764
1755
  const risk = riskType === "initial" ? scenario.risk : scenario.remainingRisk;
1765
- useEffect(() => {
1766
- if (restEqualsInitial)
1767
- setRemainingConsequence(getConsequenceLevel(scenario.risk));
1768
- }, [
1769
- restEqualsInitial,
1770
- scenario.risk,
1771
- scenario.risk.consequence,
1772
- setRemainingConsequence
1773
- ]);
1774
- useEffect(() => {
1775
- if (restEqualsInitial)
1776
- setRemainingProbability(getProbabilityLevel(scenario.risk));
1777
- }, [
1778
- restEqualsInitial,
1779
- scenario.risk,
1780
- scenario.risk.probability,
1781
- setRemainingProbability
1782
- ]);
1783
- const setC = riskType === "initial" ? setConsequence : setRemainingConsequence;
1784
- const setP = riskType === "initial" ? setProbability : setRemainingProbability;
1756
+ const handleProbabilityChange = (probabilityLevel) => {
1757
+ if (riskType === "initial") {
1758
+ if (restEqualsInitial) {
1759
+ setProbabilityAndRemainingProbability(probabilityLevel);
1760
+ } else {
1761
+ setProbability(probabilityLevel);
1762
+ }
1763
+ } else {
1764
+ setRemainingProbability(probabilityLevel);
1765
+ }
1766
+ };
1767
+ const handleConsequenceChange = (consequenceLevel) => {
1768
+ if (riskType === "initial") {
1769
+ if (restEqualsInitial) {
1770
+ setConsequenceAndRemainingConsequence(consequenceLevel);
1771
+ } else {
1772
+ setConsequence(consequenceLevel);
1773
+ }
1774
+ } else {
1775
+ setRemainingConsequence(consequenceLevel);
1776
+ }
1777
+ };
1785
1778
  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(
1786
1779
  ProbabilityTable,
1787
1780
  {
1788
1781
  selectedValue: getProbabilityLevel(risk),
1789
- handleChange: setP
1782
+ handleChange: handleProbabilityChange
1790
1783
  }
1791
1784
  )), /* @__PURE__ */ React.createElement(Box, { className: box }, /* @__PURE__ */ React.createElement(Typography, { className: h3 }, t("dictionary.consequence")), /* @__PURE__ */ React.createElement(Typography, { className: subtitle2 }, t(`scenarioStepper.${resourceKey}.consequenceSubtitle`))), /* @__PURE__ */ React.createElement(Box, { className: box }, /* @__PURE__ */ React.createElement(
1792
1785
  ConsequenceTable,
1793
1786
  {
1794
1787
  selectedValue: getConsequenceLevel(risk),
1795
- handleChange: setC
1788
+ handleChange: handleConsequenceChange
1796
1789
  }
1797
1790
  )));
1798
1791
  };
@@ -1889,8 +1882,9 @@ const ScenarioWizard = ({
1889
1882
  saveScenario,
1890
1883
  closeScenario,
1891
1884
  editScenario,
1892
- validateScenario
1893
- } = useContext(ScenarioContext);
1885
+ validateScenario,
1886
+ hasFormErrors
1887
+ } = useScenario();
1894
1888
  const [showCloseConfirmation, setShowCloseConfirmation] = useState(false);
1895
1889
  const [restEqualsInitial, setRestEqualsInitial] = useState(isNewScenario);
1896
1890
  const [canCloseIfSuccessfull, setCanCloseIfSuccessfull] = useState(false);
@@ -1911,6 +1905,9 @@ const ScenarioWizard = ({
1911
1905
  }
1912
1906
  }, [canCloseIfSuccessfull, close, updateStatus]);
1913
1907
  const saveAndClose = () => {
1908
+ if (hasFormErrors()) {
1909
+ return;
1910
+ }
1914
1911
  saveScenario();
1915
1912
  setCanCloseIfSuccessfull(true);
1916
1913
  };
@@ -2023,396 +2020,555 @@ const ScenarioWizard = ({
2023
2020
  ));
2024
2021
  };
2025
2022
 
2026
- const useScenarioDrawerStyles = makeStyles((theme) => ({
2027
- drawer: {
2028
- padding: theme.spacing(4),
2029
- width: "50%",
2030
- [theme.breakpoints.down("sm")]: {
2031
- width: "90%",
2032
- padding: theme.spacing(2)
2033
- }
2034
- },
2035
- header: {
2036
- display: "flex",
2037
- flexDirection: "row",
2038
- justifyContent: "space-between"
2039
- },
2040
- buttons: {
2041
- display: "flex",
2042
- flexDirection: "row",
2043
- alignItems: "center",
2044
- gap: theme.spacing(2),
2045
- marginTop: theme.spacing(4)
2046
- },
2047
- risikoBadge: {
2048
- width: "20px",
2049
- height: "48px",
2050
- borderRadius: "20%"
2051
- },
2052
- titleAndButton: {
2053
- display: "flex",
2054
- flexDirection: "row",
2055
- justifyContent: "space-between"
2056
- },
2057
- section: {
2058
- padding: theme.spacing(2),
2059
- marginBottom: theme.spacing(2),
2060
- backgroundColor: theme.palette.type === "dark" ? "#555555" : "#e0e0e0"
2061
- },
2062
- gridContainer: {
2063
- marginBottom: theme.palette.type === "dark" ? theme.spacing(1) : "7px",
2064
- backgroundColor: theme.palette.type === "dark" ? "#55555519" : "#e0e0e019"
2065
- },
2066
- editIcon: {
2067
- color: theme.palette.type === "dark" ? "#fff" : "rgba(0, 0, 0, 0.54)"
2068
- }
2069
- }));
2023
+ const section = {
2024
+ display: "flex",
2025
+ flexDirection: "column",
2026
+ gap: "16px",
2027
+ width: "100%",
2028
+ padding: 2
2029
+ };
2030
+
2031
+ const formLabel = (theme) => ({
2032
+ fontSize: theme.spacing(1.75),
2033
+ fontWeight: 700,
2034
+ textTransform: "uppercase",
2035
+ marginBottom: theme.spacing(1),
2036
+ color: theme.palette.mode === "dark" ? "#F8F8F8" : "rgba(0, 0, 0, 0.87)"
2037
+ });
2038
+ const heading1 = (theme) => ({
2039
+ fontSize: theme.spacing(4),
2040
+ fontWeight: 700
2041
+ });
2042
+ const heading3 = (theme) => ({
2043
+ fontSize: theme.spacing(2.5),
2044
+ fontWeight: 500
2045
+ });
2046
+ const body1 = (theme) => ({
2047
+ fontSize: theme.spacing(2),
2048
+ fontWeight: 500
2049
+ });
2050
+ const body2 = (theme) => ({
2051
+ fontSize: theme.spacing(2),
2052
+ fontWeight: 400,
2053
+ whiteSpace: "pre-line"
2054
+ });
2055
+ const label = (theme) => ({
2056
+ fontSize: theme.spacing(1.75),
2057
+ fontWeight: 700,
2058
+ textTransform: "uppercase",
2059
+ color: theme.palette.mode === "dark" ? "#F8F8F8" : "rgba(0, 0, 0, 0.87)",
2060
+ paddingBottom: "0.4rem"
2061
+ });
2062
+ const labelSubtitle = (theme) => ({
2063
+ fontSize: theme.spacing(1.75),
2064
+ fontWeight: 400,
2065
+ color: theme.palette.mode === "dark" ? "#F8F8F8" : "rgba(0, 0, 0, 0.87)",
2066
+ paddingBottom: "0.4rem",
2067
+ marginTop: "-0.2rem"
2068
+ });
2069
+ const label2 = (theme) => ({
2070
+ fontSize: theme.spacing(1.75),
2071
+ fontWeight: 700,
2072
+ textTransform: "uppercase",
2073
+ color: theme.palette.mode === "dark" ? "#F8F8F8" : "rgba(0, 0, 0, 0.87)"
2074
+ });
2070
2075
 
2071
- const RiskBox = ({ risk, riskType, editScenario }) => {
2072
- const { risikoBadge, titleAndButton, section, editIcon } = useScenarioDrawerStyles();
2073
- const { h3, body1, label, button } = useFontStyles();
2076
+ const RiskBox = ({ risk, riskType }) => {
2074
2077
  const { t } = useTranslationRef(pluginRiScTranslationRef);
2075
- return /* @__PURE__ */ React.createElement(Paper, { className: section, style: { padding: "1rem" } }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, className: titleAndButton }, /* @__PURE__ */ React.createElement(Typography, { className: h3 }, t(`dictionary.${riskType}`)), /* @__PURE__ */ React.createElement(
2076
- Button,
2077
- {
2078
- className: button,
2079
- variant: "text",
2080
- color: "primary",
2081
- onClick: () => editScenario(riskType),
2082
- startIcon: /* @__PURE__ */ React.createElement(EditIcon, { className: editIcon, "aria-label": "Edit" })
2083
- }
2084
- )), /* @__PURE__ */ React.createElement(Grid, { container: true }, /* @__PURE__ */ React.createElement(
2085
- Grid,
2078
+ return /* @__PURE__ */ React.createElement(Paper$1, { sx: section }, /* @__PURE__ */ React.createElement(Typography$1, { sx: heading3 }, t(`dictionary.${riskType}`)), /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(
2079
+ Box$2,
2086
2080
  {
2087
- item: true,
2088
- xs: 12,
2089
- style: {
2081
+ sx: {
2090
2082
  display: "flex",
2091
- flexDirection: "row",
2092
- gap: "0.5rem",
2093
- alignItems: "center"
2083
+ alignItems: "center",
2084
+ gap: 1
2094
2085
  }
2095
2086
  },
2096
2087
  /* @__PURE__ */ React.createElement(
2097
2088
  Box$2,
2098
2089
  {
2099
- className: risikoBadge,
2100
- style: {
2090
+ sx: {
2091
+ width: "20px",
2092
+ height: "48px",
2093
+ borderRadius: 1,
2101
2094
  backgroundColor: getRiskMatrixColor(risk)
2102
2095
  }
2103
2096
  }
2104
2097
  ),
2105
2098
  /* @__PURE__ */ React.createElement(
2106
- Grid,
2099
+ Box$2,
2107
2100
  {
2108
- item: true,
2109
- xs: 12,
2110
- style: {
2101
+ sx: {
2111
2102
  display: "flex",
2112
2103
  flexDirection: "column",
2113
- alignItems: "start"
2104
+ alignItems: "start",
2105
+ gap: 0.5
2114
2106
  }
2115
2107
  },
2116
- /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.probability"), ": ", getProbabilityLevel(risk)),
2117
- /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.consequence"), ": ", getConsequenceLevel(risk))
2108
+ /* @__PURE__ */ React.createElement(Typography$1, { sx: label2 }, t("dictionary.probability"), ": ", getProbabilityLevel(risk)),
2109
+ /* @__PURE__ */ React.createElement(Typography$1, { sx: label2 }, t("dictionary.consequence"), ": ", getConsequenceLevel(risk))
2118
2110
  )
2119
- ), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, style: { paddingBottom: 0 } }, /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.estimatedRisk"))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, style: { paddingTop: 0 } }, /* @__PURE__ */ React.createElement(Typography, { className: body1 }, formatNOK(risk.consequence * risk.probability), " ", t("riskMatrix.estimatedRisk.unit.nokPerYear")))));
2111
+ )), /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(Typography$1, { sx: label }, t("dictionary.estimatedRisk")), /* @__PURE__ */ React.createElement(Typography$1, { sx: body1 }, formatNOK(risk.consequence * risk.probability), " ", t("riskMatrix.estimatedRisk.unit.nokPerYear"))));
2120
2112
  };
2121
2113
  const RiskSection = () => {
2122
- const { scenario, editScenario } = useContext(ScenarioContext);
2123
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
2114
+ const { scenario } = useScenario();
2115
+ return /* @__PURE__ */ React.createElement(Box$2, { sx: { display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ React.createElement(RiskBox, { risk: scenario.risk, riskType: "initialRisk" }), /* @__PURE__ */ React.createElement(KeyboardDoubleArrowRightIcon, { sx: { fontSize: "48px" } }), /* @__PURE__ */ React.createElement(RiskBox, { risk: scenario.remainingRisk, riskType: "restRisk" }));
2116
+ };
2117
+
2118
+ const ActionBox = ({ action, index, saveScenario }) => {
2119
+ const { t } = useTranslationRef(pluginRiScTranslationRef);
2120
+ const [previousAction, setPreviousAction] = useState(action);
2121
+ useEffect(() => {
2122
+ if (previousAction !== action) {
2123
+ saveScenario();
2124
+ setPreviousAction(action);
2125
+ }
2126
+ }, [action, saveScenario, previousAction]);
2127
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(Typography$1, { sx: label }, t("dictionary.measure"), " ", index), /* @__PURE__ */ React.createElement(Typography$1, { sx: body2 }, action.description)), /* @__PURE__ */ React.createElement(
2124
2128
  Box$2,
2125
2129
  {
2126
- style: {
2130
+ sx: {
2127
2131
  display: "flex",
2128
- flexDirection: "row",
2129
- alignItems: "center"
2132
+ justifyContent: "space-between",
2133
+ alignItems: "end"
2130
2134
  }
2131
2135
  },
2132
- /* @__PURE__ */ React.createElement(
2133
- RiskBox,
2134
- {
2135
- risk: scenario.risk,
2136
- riskType: "initialRisk",
2137
- editScenario
2138
- }
2139
- ),
2140
- /* @__PURE__ */ React.createElement(
2141
- Grid,
2136
+ /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(Typography$1, { sx: label }, t("dictionary.url")), action.url ? /* @__PURE__ */ React.createElement(
2137
+ Link,
2142
2138
  {
2143
- item: true,
2144
- xs: 2,
2145
- style: {
2146
- display: "flex",
2147
- justifyContent: "center",
2148
- alignItems: "center"
2149
- }
2139
+ sx: body2,
2140
+ target: "_blank",
2141
+ rel: "noreferrer",
2142
+ href: action.url.startsWith("http") ? action.url : `//${action.url}`
2150
2143
  },
2151
- /* @__PURE__ */ React.createElement(KeyboardDoubleArrowRightIcon, { fontSize: "large" })
2152
- ),
2144
+ action.url
2145
+ ) : /* @__PURE__ */ React.createElement(Typography$1, { sx: body2 }, t("dictionary.emptyUrl"))),
2153
2146
  /* @__PURE__ */ React.createElement(
2154
- RiskBox,
2147
+ Chip$1,
2155
2148
  {
2156
- risk: scenario.remainingRisk,
2157
- riskType: "restRisk",
2158
- editScenario
2149
+ label: action.status,
2150
+ sx: {
2151
+ margin: 0,
2152
+ backgroundColor: action.status === "Completed" ? { backgroundColor: "#6BC6A4" } : void 0
2153
+ }
2159
2154
  }
2160
2155
  )
2161
2156
  ));
2162
2157
  };
2163
2158
 
2164
- const useActionBoxStyles = makeStyles((theme) => ({
2165
- alignCenter: {
2166
- display: "flex",
2167
- alignItems: "center"
2168
- },
2169
- justifyEnd: {
2170
- display: "flex",
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"
2194
- }
2195
- }));
2159
+ const ActionsSection = () => {
2160
+ const { t } = useTranslationRef(pluginRiScTranslationRef);
2161
+ const { scenario, saveScenario } = useScenario();
2162
+ return /* @__PURE__ */ React.createElement(Paper$1, { sx: section }, /* @__PURE__ */ React.createElement(Typography$1, { sx: heading3 }, t("scenarioDrawer.measureTab.title")), scenario.existingActions && /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(Typography$1, { sx: label }, t("scenarioDrawer.measureTab.existingMeasure")), /* @__PURE__ */ React.createElement(Typography$1, { sx: body2 }, scenario.existingActions)), scenario.actions.map((action, index) => /* @__PURE__ */ React.createElement(Fragment, { key: action.ID }, /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(
2163
+ ActionBox,
2164
+ {
2165
+ action,
2166
+ index: index + 1,
2167
+ saveScenario
2168
+ }
2169
+ ))));
2170
+ };
2171
+
2172
+ const DeleteConfirmation = () => {
2173
+ const { t } = useTranslationRef(pluginRiScTranslationRef);
2174
+ const { deleteConfirmationIsOpen, abortDeletion, confirmDeletion } = useScenario();
2175
+ return /* @__PURE__ */ React.createElement(Dialog$1, { open: deleteConfirmationIsOpen }, /* @__PURE__ */ React.createElement(DialogTitle$1, null, t("scenarioDrawer.deleteScenarioConfirmation")), /* @__PURE__ */ React.createElement(DialogActions$1, { sx: { gap: 1, paddingX: 3, paddingY: 2 } }, /* @__PURE__ */ React.createElement(Button$1, { onClick: abortDeletion }, t("dictionary.cancel")), /* @__PURE__ */ React.createElement(Button$1, { onClick: confirmDeletion, variant: "contained", color: "error" }, t("scenarioDrawer.deleteScenarioButton"))));
2176
+ };
2177
+
2178
+ const ScopeSection = () => {
2179
+ const { t } = useTranslationRef(pluginRiScTranslationRef);
2180
+ const { scenario } = useScenario();
2181
+ return /* @__PURE__ */ React.createElement(Paper$1, { sx: section }, /* @__PURE__ */ React.createElement(Typography$1, { sx: heading3 }, t("scenarioDrawer.title")), /* @__PURE__ */ React.createElement(Typography$1, { sx: heading1 }, scenario.title), /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(Typography$1, { sx: label }, t("dictionary.description")), /* @__PURE__ */ React.createElement(Typography$1, { sx: body2 }, scenario.description)), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(Box$2, { sx: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 2 } }, /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(Typography$1, { sx: label }, t("dictionary.threatActors")), scenario.threatActors.map((threatActor) => /* @__PURE__ */ React.createElement(Typography$1, { key: threatActor, sx: body2 }, threatActor))), /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(Typography$1, { sx: label }, t("dictionary.vulnerabilities")), scenario.vulnerabilities.map((vulnerability) => /* @__PURE__ */ React.createElement(Typography$1, { key: vulnerability, sx: body2 }, vulnerability)))));
2182
+ };
2196
2183
 
2197
- const ChipDropdown = ({
2184
+ const Select = ({
2185
+ label,
2186
+ sublabel,
2187
+ error,
2188
+ helperText,
2189
+ required,
2190
+ control,
2191
+ name,
2192
+ multiple,
2193
+ labelTranslationKey,
2198
2194
  options,
2199
- selectedValue,
2200
- handleChange
2195
+ ...props
2201
2196
  }) => {
2202
- const [anchorEl, setAnchorEl] = useState(null);
2203
- const [menuAnchorEl, setMenuAnchorEl] = useState(null);
2204
- const handleClick = (item) => {
2205
- setAnchorEl(null);
2206
- handleChange(item);
2207
- };
2208
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
2209
- Chip,
2210
- {
2211
- style: {
2212
- margin: 0,
2213
- padding: 0,
2214
- backgroundColor: selectedValue === "Completed" ? "#6BC6A4" : "#D9D9D9",
2215
- color: "#000000"
2216
- },
2217
- label: selectedValue,
2218
- onClick: (e) => {
2219
- setAnchorEl(e.currentTarget);
2220
- setMenuAnchorEl(e.currentTarget);
2197
+ const { t } = useTranslationRef(pluginRiScTranslationRef);
2198
+ const { field } = useController({
2199
+ name,
2200
+ control,
2201
+ rules: { required }
2202
+ });
2203
+ const renderValue = (values) => {
2204
+ var _a;
2205
+ return multiple ? /* @__PURE__ */ React.createElement(
2206
+ Box$2,
2207
+ {
2208
+ sx: {
2209
+ display: "flex",
2210
+ flexWrap: "wrap",
2211
+ marginBottom: 0,
2212
+ paddingBottom: 0
2213
+ }
2221
2214
  },
2222
- icon: /* @__PURE__ */ React.createElement(KeyboardArrowDownIcon, { style: { color: "#000000" } })
2223
- }
2224
- ), /* @__PURE__ */ React.createElement(
2225
- Menu,
2215
+ values.map((value) => /* @__PURE__ */ React.createElement(
2216
+ Chip$1,
2217
+ {
2218
+ key: value,
2219
+ label: (
2220
+ /* @ts-ignore Because ts can't typecheck strings against our keys */
2221
+ labelTranslationKey ? t(`${labelTranslationKey}.${value}`) : value
2222
+ )
2223
+ }
2224
+ ))
2225
+ ) : (_a = options.find((option) => option.value === values)) == null ? void 0 : _a.renderedValue;
2226
+ };
2227
+ const handleChecked = (fieldValue, optionValue) => {
2228
+ if (Array.isArray(fieldValue))
2229
+ return fieldValue.includes(optionValue);
2230
+ return fieldValue === optionValue;
2231
+ };
2232
+ return /* @__PURE__ */ React.createElement(FormControl$2, { sx: { width: "100%" } }, label && /* @__PURE__ */ React.createElement(FormLabel$2, { sx: formLabel, required }, label), sublabel && /* @__PURE__ */ React.createElement(FormHelperText$1, { sx: labelSubtitle }, sublabel), /* @__PURE__ */ React.createElement(
2233
+ MUISelect,
2226
2234
  {
2227
- anchorEl: menuAnchorEl,
2228
- open: Boolean(anchorEl),
2229
- onClose: () => setAnchorEl(null),
2230
- getContentAnchorEl: null,
2231
- anchorOrigin: {
2232
- vertical: "bottom",
2233
- horizontal: "center"
2235
+ MenuProps: {
2236
+ disablePortal: true
2234
2237
  },
2235
- transformOrigin: {
2236
- vertical: "top",
2237
- horizontal: "center"
2238
- }
2238
+ variant: "outlined",
2239
+ renderValue,
2240
+ multiple,
2241
+ SelectDisplayProps: multiple ? {
2242
+ style: {
2243
+ paddingBottom: 8,
2244
+ paddingTop: 16,
2245
+ minHeight: 40
2246
+ }
2247
+ } : {},
2248
+ inputRef: field.ref,
2249
+ ...field,
2250
+ ...props
2239
2251
  },
2240
- options.map((item) => /* @__PURE__ */ React.createElement(MenuItem, { key: item, onClick: () => handleClick(item) }, item))
2241
- ));
2252
+ options.map((option) => /* @__PURE__ */ React.createElement(MenuItem$1, { key: option.value, value: option.value }, multiple && /* @__PURE__ */ React.createElement(Checkbox$1, { checked: handleChecked(field.value, option.value) }), /* @__PURE__ */ React.createElement(ListItemText$1, { primary: option.renderedValue })))
2253
+ ), error && /* @__PURE__ */ React.createElement(FormHelperText$1, { error: true }, helperText));
2242
2254
  };
2243
2255
 
2244
- const ActionBox = ({
2245
- action,
2246
- index,
2247
- updateAction,
2248
- saveScenario
2256
+ const ScenarioForm = ({
2257
+ formMethods
2249
2258
  }) => {
2250
2259
  const { t } = useTranslationRef(pluginRiScTranslationRef);
2251
- const { body2, label } = useFontStyles();
2252
- const { gridContainer } = useScenarioDrawerStyles();
2253
- const {
2254
- alignCenter,
2255
- row,
2256
- paddingTop,
2257
- actionDescription,
2258
- column,
2259
- chipDropdown
2260
- } = useActionBoxStyles();
2261
- const [previousAction, setPreviousAction] = useState(action);
2262
- useEffect(() => {
2263
- if (previousAction !== action) {
2264
- saveScenario();
2265
- setPreviousAction(action);
2266
- }
2267
- }, [action, saveScenario, previousAction]);
2268
- const setStatus = (status) => {
2269
- updateAction({
2270
- ...action,
2271
- status
2272
- });
2273
- };
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(
2275
- ChipDropdown,
2260
+ const { control } = formMethods;
2261
+ const probabilityValues = probabilityOptions.map((value, index) => ({
2262
+ value,
2263
+ /* @ts-ignore Because ts can't typecheck strings against our keys */
2264
+ renderedValue: `${index + 1}: ${t(`probabilityTable.rows.${index + 1}`)}`
2265
+ }));
2266
+ const consequenceValues = consequenceOptions.map((value, index) => ({
2267
+ value,
2268
+ /* @ts-ignore Because ts can't typecheck strings against our keys */
2269
+ renderedValue: `${index + 1}: ${t(`consequenceTable.rows.${index + 1}`)}`
2270
+ }));
2271
+ return /* @__PURE__ */ React.createElement(
2272
+ Box$2,
2276
2273
  {
2277
- options: actionStatusOptions,
2278
- selectedValue: action.status,
2279
- handleChange: setStatus
2280
- }
2281
- ))));
2274
+ sx: {
2275
+ display: "flex",
2276
+ gap: "24px"
2277
+ }
2278
+ },
2279
+ /* @__PURE__ */ React.createElement(Paper$1, { sx: section }, /* @__PURE__ */ React.createElement(Typography$1, { sx: heading3 }, t("dictionary.initialRisk")), /* @__PURE__ */ React.createElement(
2280
+ Select,
2281
+ {
2282
+ control,
2283
+ name: "risk.probability",
2284
+ label: t("dictionary.probability"),
2285
+ options: probabilityValues
2286
+ }
2287
+ ), /* @__PURE__ */ React.createElement(
2288
+ Select,
2289
+ {
2290
+ control,
2291
+ name: "risk.consequence",
2292
+ label: t("dictionary.consequence"),
2293
+ options: consequenceValues
2294
+ }
2295
+ )),
2296
+ /* @__PURE__ */ React.createElement(Paper$1, { sx: section }, /* @__PURE__ */ React.createElement(Typography$1, { sx: heading3 }, t("dictionary.restRisk")), /* @__PURE__ */ React.createElement(
2297
+ Select,
2298
+ {
2299
+ control,
2300
+ name: "remainingRisk.probability",
2301
+ label: t("dictionary.probability"),
2302
+ options: probabilityValues
2303
+ }
2304
+ ), /* @__PURE__ */ React.createElement(
2305
+ Select,
2306
+ {
2307
+ control,
2308
+ name: "remainingRisk.consequence",
2309
+ label: t("dictionary.consequence"),
2310
+ options: consequenceValues
2311
+ }
2312
+ ))
2313
+ );
2282
2314
  };
2283
2315
 
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)));
2291
- };
2316
+ const Input = forwardRef(
2317
+ ({ label, sublabel, error, helperText, required, ...props }, ref) => {
2318
+ return /* @__PURE__ */ React.createElement(FormControl$2, { sx: { width: "100%" }, error, required }, label && /* @__PURE__ */ React.createElement(FormLabel$2, { required, sx: formLabel }, label), sublabel && /* @__PURE__ */ React.createElement(FormHelperText$1, { sx: labelSubtitle }, sublabel), /* @__PURE__ */ React.createElement(
2319
+ TextField$2,
2320
+ {
2321
+ id: "filled-multiline-static",
2322
+ error,
2323
+ multiline: true,
2324
+ fullWidth: true,
2325
+ variant: "outlined",
2326
+ inputRef: ref,
2327
+ InputProps: {
2328
+ sx: (theme) => ({
2329
+ "&.Mui-disabled": {
2330
+ color: theme.palette.mode === "dark" ? "#FFFFFF80" : "#757575"
2331
+ }
2332
+ })
2333
+ },
2334
+ ...props
2335
+ }
2336
+ ), helperText && /* @__PURE__ */ React.createElement(FormHelperText$1, null, helperText));
2337
+ }
2338
+ );
2292
2339
 
2293
- const ActionsSection = () => {
2340
+ const ActionFormSection = ({
2341
+ formMethods
2342
+ }) => {
2294
2343
  const { t } = useTranslationRef(pluginRiScTranslationRef);
2295
- const { h3, button, label } = useFontStyles();
2296
- const { titleAndButton, section, editIcon } = useScenarioDrawerStyles();
2297
- const { scenario, editScenario, saveScenario, updateAction } = useContext(ScenarioContext);
2298
- return /* @__PURE__ */ React.createElement(Box$2, null, /* @__PURE__ */ React.createElement(Paper, { className: section, style: { padding: "1rem" } }, /* @__PURE__ */ React.createElement(
2299
- Grid,
2344
+ const { control, register } = formMethods;
2345
+ const { fields, append, remove } = useFieldArray({
2346
+ control,
2347
+ name: "actions"
2348
+ });
2349
+ return /* @__PURE__ */ React.createElement(Paper$1, { sx: section }, /* @__PURE__ */ React.createElement(Typography$1, { sx: heading3 }, t("dictionary.measure")), /* @__PURE__ */ React.createElement(
2350
+ Input,
2300
2351
  {
2301
- item: true,
2302
- xs: 12,
2303
- className: titleAndButton,
2304
- style: {
2305
- marginBottom: "12px"
2352
+ ...register("existingActions"),
2353
+ label: t("scenarioDrawer.measureTab.existingMeasure"),
2354
+ minRows: 3
2355
+ }
2356
+ ), fields.map((field, index) => /* @__PURE__ */ React.createElement(Fragment, { key: field.ID }, /* @__PURE__ */ React.createElement(Divider, { variant: "fullWidth" }), /* @__PURE__ */ React.createElement(
2357
+ Box$2,
2358
+ {
2359
+ sx: {
2360
+ display: "grid",
2361
+ gap: "24px",
2362
+ padding: 0
2306
2363
  }
2307
2364
  },
2308
- /* @__PURE__ */ React.createElement(Typography, { className: h3 }, t("scenarioDrawer.measureTab.actionsTitle")),
2309
2365
  /* @__PURE__ */ React.createElement(
2310
- Button,
2366
+ Box$2,
2311
2367
  {
2312
- className: button,
2313
- variant: "text",
2314
- color: "primary",
2315
- onClick: () => editScenario("measure"),
2316
- startIcon: /* @__PURE__ */ React.createElement(EditIcon, { className: editIcon, "aria-label": "Edit" })
2368
+ sx: {
2369
+ display: "flex",
2370
+ justifyContent: "space-between",
2371
+ alignItems: "center"
2372
+ }
2373
+ },
2374
+ /* @__PURE__ */ React.createElement(Typography$1, { sx: heading3 }, t("scenarioDrawer.measureTab.title"), " ", index),
2375
+ /* @__PURE__ */ React.createElement(IconButton, { onClick: () => remove(index), color: "primary" }, /* @__PURE__ */ React.createElement(DeleteIcon$1, { "aria-label": "Edit" }))
2376
+ ),
2377
+ /* @__PURE__ */ React.createElement(
2378
+ Input,
2379
+ {
2380
+ required: true,
2381
+ ...register(`actions.${index}.description`, { required: true }),
2382
+ label: t("dictionary.description")
2317
2383
  }
2384
+ ),
2385
+ /* @__PURE__ */ React.createElement(
2386
+ Box$2,
2387
+ {
2388
+ sx: {
2389
+ display: "grid",
2390
+ gridTemplateColumns: "2fr 1fr",
2391
+ gap: "24px"
2392
+ }
2393
+ },
2394
+ /* @__PURE__ */ React.createElement(
2395
+ Input,
2396
+ {
2397
+ required: true,
2398
+ ...register(`actions.${index}.url`, { required: true }),
2399
+ label: t("dictionary.url")
2400
+ }
2401
+ ),
2402
+ /* @__PURE__ */ React.createElement(
2403
+ Select,
2404
+ {
2405
+ required: true,
2406
+ control,
2407
+ name: `actions.${index}.status`,
2408
+ label: t("dictionary.status"),
2409
+ options: actionStatusOptions.map((value) => ({
2410
+ value,
2411
+ renderedValue: value
2412
+ }))
2413
+ }
2414
+ )
2318
2415
  )
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(
2320
- ActionBox,
2416
+ ))), /* @__PURE__ */ React.createElement(
2417
+ Button$1,
2321
2418
  {
2322
- action,
2323
- index: index + 1,
2324
- updateAction,
2325
- saveScenario
2326
- }
2327
- ), index !== scenario.actions.length - 1 && /* @__PURE__ */ React.createElement(Divider$1, { variant: "fullWidth" })))));
2328
- };
2329
-
2330
- const DeleteConfirmation = () => {
2331
- const { t } = useTranslationRef(pluginRiScTranslationRef);
2332
- const { deleteConfirmationIsOpen, abortDeletion, confirmDeletion } = useContext(ScenarioContext);
2333
- return /* @__PURE__ */ React.createElement(Dialog, { open: deleteConfirmationIsOpen }, /* @__PURE__ */ React.createElement(DialogTitle, null, t("scenarioDrawer.deleteScenarioConfirmation")), /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { style: { textTransform: "none" }, onClick: abortDeletion }, t("dictionary.cancel")), /* @__PURE__ */ React.createElement(Button, { style: { textTransform: "none" }, onClick: confirmDeletion }, t("scenarioDrawer.deleteScenarioButton"))));
2419
+ startIcon: /* @__PURE__ */ React.createElement(AddCircle$1, null),
2420
+ color: "primary",
2421
+ onClick: () => append(emptyAction())
2422
+ },
2423
+ t("scenarioDrawer.measureTab.addMeasureButton")
2424
+ ));
2334
2425
  };
2335
2426
 
2336
- const ScopeSection = () => {
2427
+ const ScenarioFormSection = ({
2428
+ formMethods
2429
+ }) => {
2337
2430
  const { t } = useTranslationRef(pluginRiScTranslationRef);
2338
- const { h1, h3, body2, label, button } = useFontStyles();
2339
- const { header, titleAndButton, section, editIcon } = useScenarioDrawerStyles();
2340
- const { scenario, editScenario } = useContext(ScenarioContext);
2341
- return /* @__PURE__ */ React.createElement(Paper, { className: section, style: { padding: "1rem" } }, /* @__PURE__ */ React.createElement(Box$2, { className: header }, /* @__PURE__ */ React.createElement(Grid, { container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, className: titleAndButton }, /* @__PURE__ */ React.createElement(Typography, { variant: "h3", className: h3 }, t("scenarioDrawer.title")), /* @__PURE__ */ React.createElement(
2342
- Button,
2431
+ const {
2432
+ control,
2433
+ register,
2434
+ formState: { errors }
2435
+ } = formMethods;
2436
+ const translatedThreatActors = threatActorsOptions.map((threatActor) => ({
2437
+ value: threatActor,
2438
+ /* @ts-ignore Because ts can't typecheck strings against our keys */
2439
+ renderedValue: t(`threatActors.${threatActor}`)
2440
+ }));
2441
+ const translatedVulnerabilities = vulnerabilitiesOptions.map(
2442
+ (vulnerability) => ({
2443
+ value: vulnerability,
2444
+ /* @ts-ignore Because ts can't typecheck strings against our keys */
2445
+ renderedValue: t(`vulnerabilities.${vulnerability}`)
2446
+ })
2447
+ );
2448
+ return /* @__PURE__ */ React.createElement(Paper$1, { sx: section }, /* @__PURE__ */ React.createElement(Typography$1, { sx: heading3 }, t("scenarioDrawer.title")), /* @__PURE__ */ React.createElement(
2449
+ Input,
2343
2450
  {
2344
- className: button,
2345
- variant: "text",
2346
- color: "primary",
2347
- onClick: () => editScenario("scenario"),
2348
- startIcon: /* @__PURE__ */ React.createElement(EditIcon, { className: editIcon, "aria-label": "Edit" })
2451
+ required: true,
2452
+ ...register("title", { required: true }),
2453
+ error: errors.title !== void 0,
2454
+ label: t("dictionary.title")
2455
+ }
2456
+ ), /* @__PURE__ */ React.createElement(
2457
+ Select,
2458
+ {
2459
+ multiple: true,
2460
+ control,
2461
+ name: "threatActors",
2462
+ label: t("dictionary.threatActors"),
2463
+ labelTranslationKey: "threatActors",
2464
+ options: translatedThreatActors
2465
+ }
2466
+ ), /* @__PURE__ */ React.createElement(
2467
+ Select,
2468
+ {
2469
+ multiple: true,
2470
+ control,
2471
+ name: "vulnerabilities",
2472
+ label: t("dictionary.vulnerabilities"),
2473
+ labelTranslationKey: "vulnerabilities",
2474
+ options: translatedVulnerabilities
2475
+ }
2476
+ ), /* @__PURE__ */ React.createElement(
2477
+ Input,
2478
+ {
2479
+ ...register("description"),
2480
+ label: t("dictionary.description"),
2481
+ minRows: 4
2349
2482
  }
2350
- )), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Typography, { className: h1 }, scenario.title)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.description")), /* @__PURE__ */ React.createElement(Typography, { className: body2 }, scenario.description)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Divider, { variant: "fullWidth" })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.threatActors")), scenario.threatActors.map((threatActor) => /* @__PURE__ */ React.createElement(Typography, { key: threatActor, className: body2 }, threatActor))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.vulnerabilities")), scenario.vulnerabilities.map((vulnerability) => /* @__PURE__ */ React.createElement(Typography, { key: vulnerability, className: body2 }, vulnerability))))));
2483
+ ));
2351
2484
  };
2352
2485
 
2353
2486
  const ScenarioDrawer = () => {
2354
2487
  const { t } = useTranslationRef(pluginRiScTranslationRef);
2355
- const { drawer, buttons } = useScenarioDrawerStyles();
2356
- const { button } = useFontStyles();
2357
- const { scenarioDrawerState, openDeleteConfirmation, closeScenario } = useContext(ScenarioContext);
2358
- const isOpen = scenarioDrawerState !== ScenarioDrawerState.Closed;
2488
+ const [isEditing, setIsEditing] = useState(false);
2489
+ const {
2490
+ scenario,
2491
+ isDrawerOpen,
2492
+ openDeleteConfirmation,
2493
+ closeScenario,
2494
+ submitEditedScenarioToRiSc
2495
+ } = useScenario();
2496
+ const formMethods = useForm({ defaultValues: scenario });
2497
+ const onCancel = () => {
2498
+ formMethods.reset(scenario);
2499
+ setIsEditing(false);
2500
+ };
2501
+ const onClose = () => {
2502
+ closeScenario();
2503
+ setIsEditing(false);
2504
+ };
2505
+ const onSubmit = formMethods.handleSubmit((data) => {
2506
+ submitEditedScenarioToRiSc(data);
2507
+ setIsEditing(false);
2508
+ });
2509
+ useEffect(() => {
2510
+ formMethods.reset(scenario);
2511
+ }, [scenario, formMethods]);
2359
2512
  return /* @__PURE__ */ React.createElement(
2360
2513
  Drawer,
2361
2514
  {
2362
- classes: { paper: drawer },
2515
+ PaperProps: {
2516
+ sx: (theme) => ({
2517
+ padding: theme.spacing(4),
2518
+ width: "50%",
2519
+ gap: theme.spacing(3),
2520
+ [theme.breakpoints.down("sm")]: {
2521
+ width: "90%",
2522
+ padding: theme.spacing(2)
2523
+ },
2524
+ backgroundColor: theme.palette.mode === "dark" ? "#333333" : "#f8f8f8"
2525
+ })
2526
+ },
2363
2527
  variant: "temporary",
2364
2528
  anchor: "right",
2365
- open: isOpen,
2366
- onClose: closeScenario
2529
+ open: isDrawerOpen,
2530
+ onClose
2367
2531
  },
2368
2532
  /* @__PURE__ */ React.createElement(
2369
2533
  Box$2,
2370
2534
  {
2371
- className: buttons,
2372
- style: {
2373
- marginBottom: "8px",
2374
- marginTop: "8px",
2535
+ sx: {
2375
2536
  display: "flex",
2376
- flexDirection: "row",
2377
- gap: "0.5rem",
2378
- justifyContent: "flex-end"
2537
+ gap: "16px",
2538
+ marginLeft: "auto"
2379
2539
  }
2380
2540
  },
2381
2541
  /* @__PURE__ */ React.createElement(
2382
- Button,
2542
+ Button$1,
2543
+ {
2544
+ color: "primary",
2545
+ variant: "contained",
2546
+ onClick: isEditing ? onSubmit : () => setIsEditing(true),
2547
+ disabled: isEditing && !formMethods.formState.isDirty
2548
+ },
2549
+ t(isEditing ? "dictionary.save" : "dictionary.edit")
2550
+ ),
2551
+ /* @__PURE__ */ React.createElement(
2552
+ Button$1,
2383
2553
  {
2384
- className: button,
2385
2554
  variant: "outlined",
2386
2555
  color: "primary",
2387
- onClick: closeScenario
2556
+ onClick: isEditing ? onCancel : onClose
2388
2557
  },
2389
- t("dictionary.close")
2558
+ t(isEditing ? "dictionary.cancel" : "dictionary.close")
2390
2559
  )
2391
2560
  ),
2392
- /* @__PURE__ */ React.createElement(ScopeSection, null),
2393
- /* @__PURE__ */ React.createElement(RiskSection, null),
2394
- /* @__PURE__ */ React.createElement(ActionsSection, null),
2561
+ isEditing ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ScenarioFormSection, { formMethods }), /* @__PURE__ */ React.createElement(ScenarioForm, { formMethods }), /* @__PURE__ */ React.createElement(ActionFormSection, { formMethods })) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ScopeSection, null), /* @__PURE__ */ React.createElement(RiskSection, null), /* @__PURE__ */ React.createElement(ActionsSection, null)),
2395
2562
  /* @__PURE__ */ React.createElement(
2396
- Box$2,
2563
+ Button$1,
2397
2564
  {
2398
- className: buttons,
2399
- style: {
2400
- display: "flex",
2401
- flexDirection: "row",
2402
- gap: "0.5rem",
2403
- justifyContent: "flex-start"
2404
- }
2565
+ startIcon: /* @__PURE__ */ React.createElement(DeleteIcon$1, null),
2566
+ variant: "text",
2567
+ color: "primary",
2568
+ onClick: openDeleteConfirmation,
2569
+ sx: { marginRight: "auto" }
2405
2570
  },
2406
- /* @__PURE__ */ React.createElement(
2407
- Button,
2408
- {
2409
- startIcon: /* @__PURE__ */ React.createElement(DeleteIcon$1, null),
2410
- variant: "text",
2411
- color: "primary",
2412
- onClick: openDeleteConfirmation
2413
- },
2414
- t("scenarioDrawer.deleteScenarioButton")
2415
- )
2571
+ t("scenarioDrawer.deleteScenarioButton")
2416
2572
  ),
2417
2573
  /* @__PURE__ */ React.createElement(DeleteConfirmation, null)
2418
2574
  );
@@ -2504,7 +2660,7 @@ const RiskMatrixScenarioCount = ({
2504
2660
  tooltipText
2505
2661
  } = useRiskMatrixStyles();
2506
2662
  const [tooltipOpen, setTooltipOpen] = useState(false);
2507
- const { openScenario } = useContext(ScenarioContext);
2663
+ const { openScenario } = useScenario();
2508
2664
  const handleScenarioClick = (ID) => {
2509
2665
  setTooltipOpen(false);
2510
2666
  openScenario(ID);
@@ -2519,16 +2675,27 @@ const RiskMatrixScenarioCount = ({
2519
2675
  if (scenarios.length === 0) {
2520
2676
  return null;
2521
2677
  }
2522
- const tooltipList = /* @__PURE__ */ React.createElement(List, { dense: true }, /* @__PURE__ */ React.createElement(ListSubheader, { className: tooltipText }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6", className: tooltipText }, t("riskMatrix.tooltip.title"))), scenarios.map((s) => /* @__PURE__ */ React.createElement(ListItem, { key: s.ID, button: true, disableGutters: true, className: tooltipText, onClick: () => {
2523
- handleScenarioClick(s.ID);
2524
- } }, /* @__PURE__ */ React.createElement(CircleIcon, { className: tooltipText, style: { width: "10px" } }), /* @__PURE__ */ React.createElement(
2525
- ListItemText$1,
2678
+ const tooltipList = /* @__PURE__ */ React.createElement(List, { dense: true }, /* @__PURE__ */ React.createElement(ListSubheader, { className: tooltipText }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6", className: tooltipText }, t("riskMatrix.tooltip.title"))), scenarios.map((s) => /* @__PURE__ */ React.createElement(
2679
+ ListItem,
2526
2680
  {
2681
+ key: s.ID,
2682
+ button: true,
2683
+ disableGutters: true,
2527
2684
  className: tooltipText,
2528
- style: { paddingLeft: "0.6rem" }
2685
+ onClick: () => {
2686
+ handleScenarioClick(s.ID);
2687
+ }
2529
2688
  },
2530
- /* @__PURE__ */ React.createElement("span", null, s.title)
2531
- ))));
2689
+ /* @__PURE__ */ React.createElement(CircleIcon, { className: tooltipText, style: { width: "10px" } }),
2690
+ /* @__PURE__ */ React.createElement(
2691
+ ListItemText$2,
2692
+ {
2693
+ className: tooltipText,
2694
+ style: { paddingLeft: "0.6rem" }
2695
+ },
2696
+ /* @__PURE__ */ React.createElement("span", null, s.title)
2697
+ )
2698
+ )));
2532
2699
  return /* @__PURE__ */ React.createElement(ClickAwayListener, { onClickAway: () => setTooltipOpen(false) }, /* @__PURE__ */ React.createElement(
2533
2700
  Tooltip,
2534
2701
  {
@@ -2593,7 +2760,7 @@ const AggregatedCost = ({ riSc, initialRisk }) => {
2593
2760
  const cost = riSc.scenarios.map(
2594
2761
  (scenario) => initialRisk ? scenario.risk.probability * scenario.risk.consequence : scenario.remainingRisk.probability * scenario.remainingRisk.consequence
2595
2762
  ).reduce((a, b) => a + b, 0);
2596
- return /* @__PURE__ */ React.createElement(Box$1, { className: outerBox }, /* @__PURE__ */ React.createElement(Typography, { className: label2 }, t("riskMatrix.estimatedRisk.title")), /* @__PURE__ */ React.createElement(Box$1, { className: innerBox }, /* @__PURE__ */ React.createElement(Typography, { className: body2 }, formatNumber(cost, t), " ", t("riskMatrix.estimatedRisk.unit.nokPerYear")), /* @__PURE__ */ React.createElement(IconButton, { size: "small", onClick: () => setShowDialog(true) }, /* @__PURE__ */ React.createElement(InfoIcon, null))), /* @__PURE__ */ React.createElement(
2763
+ return /* @__PURE__ */ React.createElement(Box$1, { className: outerBox }, /* @__PURE__ */ React.createElement(Typography, { className: label2 }, t("riskMatrix.estimatedRisk.title")), /* @__PURE__ */ React.createElement(Box$1, { className: innerBox }, /* @__PURE__ */ React.createElement(Typography, { className: body2 }, formatNumber(cost, t), " ", t("riskMatrix.estimatedRisk.unit.nokPerYear")), /* @__PURE__ */ React.createElement(IconButton$1, { size: "small", onClick: () => setShowDialog(true) }, /* @__PURE__ */ React.createElement(InfoIcon, null))), /* @__PURE__ */ React.createElement(
2597
2764
  EstimatedRiskInfoDialog,
2598
2765
  {
2599
2766
  isOpen: showDialog,
@@ -2661,29 +2828,36 @@ const RiskMatrix = ({ riSc }) => {
2661
2828
  centered
2662
2829
  } = useRiskMatrixStyles();
2663
2830
  const [tab, setTab] = useState("initialRisk" /* initialRisk */);
2664
- return /* @__PURE__ */ React.createElement(InfoCard, { title: t("riskMatrix.title") }, /* @__PURE__ */ React.createElement(TabContext, { value: tab }, /* @__PURE__ */ React.createElement(Tabs, { setTab }), riSc.scenarios.length > 0 && /* @__PURE__ */ React.createElement(Box$2, { className: riskSummary }, /* @__PURE__ */ React.createElement(
2665
- AggregatedCost,
2666
- {
2667
- riSc,
2668
- initialRisk: tab === "initialRisk" /* initialRisk */
2669
- }
2670
- )), /* @__PURE__ */ React.createElement(Box$2, { className: gridWrapper }, /* @__PURE__ */ React.createElement(Box$2, { className: grid }, /* @__PURE__ */ React.createElement(Box$2, { className: konsekvens }, /* @__PURE__ */ React.createElement(Typography, { className: `${centered} ${label2}` }, t("dictionary.consequence"))), riskMatrix.map((row, rowIndex) => /* @__PURE__ */ React.createElement(Fragment, { key: rowIndex }, /* @__PURE__ */ React.createElement(Box$2, { className: centered }, /* @__PURE__ */ React.createElement(Typography, { className: text }, 5 - rowIndex)), row.map((col, colIndex) => /* @__PURE__ */ React.createElement(
2671
- Box$2,
2831
+ return /* @__PURE__ */ React.createElement(
2832
+ InfoCard,
2672
2833
  {
2673
- className: `${square} ${centered}`,
2674
- style: { backgroundColor: col },
2675
- key: colIndex
2834
+ title: t("riskMatrix.title"),
2835
+ titleTypographyProps: { style: { marginBottom: 0 } }
2676
2836
  },
2677
- /* @__PURE__ */ React.createElement(
2678
- RiskMatrixScenarioCount,
2837
+ /* @__PURE__ */ React.createElement(TabContext, { value: tab }, /* @__PURE__ */ React.createElement(Tabs, { setTab }), riSc.scenarios.length > 0 && /* @__PURE__ */ React.createElement(Box$2, { className: riskSummary }, /* @__PURE__ */ React.createElement(
2838
+ AggregatedCost,
2679
2839
  {
2680
2840
  riSc,
2681
- probability: colIndex,
2682
- consequence: 4 - rowIndex,
2683
2841
  initialRisk: tab === "initialRisk" /* initialRisk */
2684
2842
  }
2685
- )
2686
- )))), /* @__PURE__ */ React.createElement(Box$2, { className: centered }), /* @__PURE__ */ React.createElement(Box$2, { className: centered }), riskMatrix.map((_, col) => /* @__PURE__ */ React.createElement(Box$2, { className: centered, key: col }, /* @__PURE__ */ React.createElement(Typography, { className: text }, col + 1))), /* @__PURE__ */ React.createElement(Box$2, { className: sannsynlighet }, /* @__PURE__ */ React.createElement(Typography, { className: `${centered} ${label2}` }, t("dictionary.probability")))))));
2843
+ )), /* @__PURE__ */ React.createElement(Box$2, { className: gridWrapper }, /* @__PURE__ */ React.createElement(Box$2, { className: grid }, /* @__PURE__ */ React.createElement(Box$2, { className: konsekvens }, /* @__PURE__ */ React.createElement(Typography, { className: `${centered} ${label2}` }, t("dictionary.consequence"))), riskMatrix.map((row, rowIndex) => /* @__PURE__ */ React.createElement(Fragment, { key: rowIndex }, /* @__PURE__ */ React.createElement(Box$2, { className: centered }, /* @__PURE__ */ React.createElement(Typography, { className: text }, 5 - rowIndex)), row.map((col, colIndex) => /* @__PURE__ */ React.createElement(
2844
+ Box$2,
2845
+ {
2846
+ className: `${square} ${centered}`,
2847
+ style: { backgroundColor: col },
2848
+ key: colIndex
2849
+ },
2850
+ /* @__PURE__ */ React.createElement(
2851
+ RiskMatrixScenarioCount,
2852
+ {
2853
+ riSc,
2854
+ probability: colIndex,
2855
+ consequence: 4 - rowIndex,
2856
+ initialRisk: tab === "initialRisk" /* initialRisk */
2857
+ }
2858
+ )
2859
+ )))), /* @__PURE__ */ React.createElement(Box$2, { className: centered }), /* @__PURE__ */ React.createElement(Box$2, { className: centered }), riskMatrix.map((_, col) => /* @__PURE__ */ React.createElement(Box$2, { className: centered, key: col }, /* @__PURE__ */ React.createElement(Typography, { className: text }, col + 1))), /* @__PURE__ */ React.createElement(Box$2, { className: sannsynlighet }, /* @__PURE__ */ React.createElement(Typography, { className: `${centered} ${label2}` }, t("dictionary.probability"))))))
2860
+ );
2687
2861
  };
2688
2862
 
2689
2863
  const useTableStyles = makeStyles((theme) => ({
@@ -2801,9 +2975,10 @@ const ScenarioTableRow = ({
2801
2975
 
2802
2976
  const ScenarioTable = ({ riSc }) => {
2803
2977
  const { t } = useTranslationRef(pluginRiScTranslationRef);
2978
+ const { label } = useFontStyles();
2804
2979
  const { titleBox, rowBorder, tableCell, tableCellTitle } = useTableStyles();
2805
- const { newScenario, openScenario } = useContext(ScenarioContext);
2806
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Paper, null, /* @__PURE__ */ React.createElement(Box$1, { className: titleBox }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5", style: { padding: "1rem" } }, t("scenarioTable.title")), riSc.scenarios.length > 0 && /* @__PURE__ */ React.createElement(
2980
+ const { newScenario, openScenario } = useScenario();
2981
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Paper, null, /* @__PURE__ */ React.createElement(Box$1, { className: titleBox }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5", style: { padding: "1rem", marginBottom: 0 } }, t("scenarioTable.title")), riSc.scenarios.length > 0 && /* @__PURE__ */ React.createElement(
2807
2982
  Box$1,
2808
2983
  {
2809
2984
  style: {
@@ -2847,38 +3022,7 @@ const ScenarioTable = ({ riSc }) => {
2847
3022
  },
2848
3023
  t("scenarioTable.addScenarioButton")
2849
3024
  )
2850
- ) : /* @__PURE__ */ React.createElement(TableContainer, { style: { overflow: "auto" }, component: Paper }, /* @__PURE__ */ React.createElement(Table, null, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, { className: rowBorder }, /* @__PURE__ */ React.createElement(TableCell, { className: tableCellTitle }, /* @__PURE__ */ React.createElement(
2851
- Typography,
2852
- {
2853
- variant: "subtitle1",
2854
- style: { fontWeight: "bold", textTransform: "uppercase" }
2855
- },
2856
- t("dictionary.title")
2857
- )), /* @__PURE__ */ React.createElement(TableCell, { className: tableCell }, /* @__PURE__ */ React.createElement(
2858
- Typography,
2859
- {
2860
- variant: "subtitle1",
2861
- style: { fontWeight: "bold", textTransform: "uppercase" }
2862
- },
2863
- t("dictionary.initialRisk")
2864
- )), /* @__PURE__ */ React.createElement(TableCell, { className: tableCell }, /* @__PURE__ */ React.createElement(
2865
- Typography,
2866
- {
2867
- variant: "subtitle1",
2868
- style: { fontWeight: "bold", textTransform: "uppercase" }
2869
- },
2870
- t("scenarioTable.columns.measuresCount")
2871
- )), /* @__PURE__ */ React.createElement(TableCell, { className: tableCell }, /* @__PURE__ */ React.createElement(
2872
- Typography,
2873
- {
2874
- variant: "subtitle1",
2875
- style: {
2876
- fontWeight: "bold",
2877
- textTransform: "uppercase"
2878
- }
2879
- },
2880
- t("dictionary.restRisk")
2881
- )))), /* @__PURE__ */ React.createElement(TableBody, null, riSc.scenarios.map((scenario, idx) => /* @__PURE__ */ React.createElement(
3025
+ ) : /* @__PURE__ */ React.createElement(TableContainer, { style: { overflow: "auto" }, component: Paper }, /* @__PURE__ */ React.createElement(Table, null, /* @__PURE__ */ React.createElement(TableHead, { style: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React.createElement(TableRow, { className: rowBorder }, /* @__PURE__ */ React.createElement(TableCell, { className: tableCellTitle }, /* @__PURE__ */ React.createElement(Typography, { className: label, style: { paddingBottom: 0 } }, t("dictionary.title"))), /* @__PURE__ */ React.createElement(TableCell, { className: tableCell }, /* @__PURE__ */ React.createElement(Typography, { className: label, style: { paddingBottom: 0 } }, t("dictionary.initialRisk"))), /* @__PURE__ */ React.createElement(TableCell, { className: tableCell }, /* @__PURE__ */ React.createElement(Typography, { className: label, style: { paddingBottom: 0 } }, t("scenarioTable.columns.measuresCount"))), /* @__PURE__ */ React.createElement(TableCell, { className: tableCell }, /* @__PURE__ */ React.createElement(Typography, { className: label, style: { paddingBottom: 0 } }, t("dictionary.restRisk"))))), /* @__PURE__ */ React.createElement(TableBody, null, riSc.scenarios.map((scenario, idx) => /* @__PURE__ */ React.createElement(
2882
3026
  ScenarioTableRow,
2883
3027
  {
2884
3028
  key: scenario.ID,
@@ -2991,7 +3135,7 @@ const RiScDialog = ({
2991
3135
  {
2992
3136
  classes: { paper },
2993
3137
  open: dialogState !== 0 /* Closed */,
2994
- onClose: handleSave,
3138
+ onClose: handleCancel,
2995
3139
  ...props
2996
3140
  },
2997
3141
  /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Typography, { className: h1 }, header), /* @__PURE__ */ React.createElement(Box$1, { className: content }, /* @__PURE__ */ React.createElement(
@@ -3031,116 +3175,6 @@ const RiScDialog = ({
3031
3175
  );
3032
3176
  };
3033
3177
 
3034
- const useStatusChipStyles = makeStyles(() => ({
3035
- statusChip: {
3036
- borderRadius: "7px",
3037
- maxHeight: "fit-content",
3038
- height: "fit-content",
3039
- margin: "0px"
3040
- },
3041
- statusChipText: {
3042
- whiteSpace: "normal"
3043
- },
3044
- statusIcon: {
3045
- maxHeight: "10px"
3046
- },
3047
- riScPublished: {
3048
- borderColor: "#88e763",
3049
- color: "#88e763"
3050
- },
3051
- riScSentForApproval: {
3052
- borderColor: "#E7A563FF",
3053
- color: "#E7A563FF"
3054
- },
3055
- riScDraft: {
3056
- borderColor: "#FBE36A",
3057
- color: "#FBE36A"
3058
- }
3059
- }));
3060
- const useStatusTextStyles = makeStyles(() => ({
3061
- prStatus: {
3062
- fontSize: "14px",
3063
- wrap: "wrap"
3064
- },
3065
- prIcon: {
3066
- margin: "0px",
3067
- padding: "0px",
3068
- marginBottom: "-1px",
3069
- marginRight: "2px",
3070
- marginLeft: "2px"
3071
- }
3072
- }));
3073
-
3074
- const getChipColor = (status, classes) => {
3075
- switch (status) {
3076
- case RiScStatus.Draft:
3077
- return classes.riScDraft;
3078
- case RiScStatus.SentForApproval:
3079
- case RiScStatus.Published:
3080
- return classes.riScPublished;
3081
- default:
3082
- return classes.riScDraft;
3083
- }
3084
- };
3085
- const useChipText = (status) => {
3086
- const { t } = useTranslationRef(pluginRiScTranslationRef);
3087
- switch (status) {
3088
- case RiScStatus.Draft:
3089
- return t("rosStatus.statusBadge.missing");
3090
- case RiScStatus.SentForApproval:
3091
- case RiScStatus.Published:
3092
- return t("rosStatus.statusBadge.approved");
3093
- default:
3094
- return t("rosStatus.statusBadge.error");
3095
- }
3096
- };
3097
- function ChipTextStatus({ status }) {
3098
- const { statusChipText } = useStatusChipStyles();
3099
- const chipText = useChipText(status);
3100
- return /* @__PURE__ */ React.createElement("span", { className: statusChipText }, chipText);
3101
- }
3102
- function PRStatus({ status, classes }) {
3103
- const { t } = useTranslationRef(pluginRiScTranslationRef);
3104
- switch (status) {
3105
- case RiScStatus.SentForApproval:
3106
- return /* @__PURE__ */ React.createElement(Typography, { className: classes.prStatus }, /* @__PURE__ */ React.createElement(GitHubIcon, { fontSize: "inherit", className: classes.prIcon }), t("rosStatus.prStatus"));
3107
- case RiScStatus.Draft:
3108
- case RiScStatus.Published:
3109
- default:
3110
- return null;
3111
- }
3112
- }
3113
- const StatusChip = ({ currentRiScStatus }) => {
3114
- const chipClasses = useStatusChipStyles();
3115
- const textClasses = useStatusTextStyles();
3116
- const [chipColorClass, setChipColorClass] = useState(null);
3117
- useEffect(() => {
3118
- const chipColor = getChipColor(currentRiScStatus, chipClasses);
3119
- setChipColorClass(chipColor);
3120
- }, [currentRiScStatus, chipClasses]);
3121
- return /* @__PURE__ */ React.createElement(
3122
- Grid,
3123
- {
3124
- container: true,
3125
- direction: "column",
3126
- spacing: 0,
3127
- style: { alignItems: "end" }
3128
- },
3129
- /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(
3130
- Chip,
3131
- {
3132
- color: "primary",
3133
- size: "medium",
3134
- variant: "outlined",
3135
- label: /* @__PURE__ */ React.createElement(ChipTextStatus, { status: currentRiScStatus }),
3136
- icon: /* @__PURE__ */ React.createElement(CircleIcon$1, { className: chipClasses.statusIcon }),
3137
- className: [chipColorClass, chipClasses.statusChip].join(" ")
3138
- }
3139
- )),
3140
- /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(PRStatus, { status: currentRiScStatus, classes: textClasses }))
3141
- );
3142
- };
3143
-
3144
3178
  const useButtonStyles = makeStyles(() => ({
3145
3179
  approveButton: {
3146
3180
  borderRadius: "2px",
@@ -3190,6 +3224,49 @@ const RiScPublishDialog = ({
3190
3224
  ))
3191
3225
  ));
3192
3226
  };
3227
+ function PullRequestSvg() {
3228
+ const theme = useTheme();
3229
+ return /* @__PURE__ */ React.createElement(
3230
+ "svg",
3231
+ {
3232
+ width: "18",
3233
+ height: "16",
3234
+ viewBox: "0 0 16 16",
3235
+ fill: "none",
3236
+ style: {
3237
+ transform: "translateY(2px)"
3238
+ },
3239
+ xmlns: "http://www.w3.org/2000/svg"
3240
+ },
3241
+ /* @__PURE__ */ React.createElement(
3242
+ "path",
3243
+ {
3244
+ d: "M9.55625 0.065625C9.825 0.184375 10 0.453125 10 0.75V2.25H10.5C12.5719 2.25 14.25 3.92812 14.25 6V11.1156C15.2656 11.4344 16 12.3813 16 13.5C16 14.8813 14.8813 16 13.5 16C12.1187 16 11 14.8813 11 13.5C11 12.3813 11.7344 11.4344 12.75 11.1156V6C12.75 4.75625 11.7437 3.75 10.5 3.75H10V5.25C10 5.54688 9.825 5.81562 9.55625 5.93437C9.2875 6.05312 8.96875 6.00625 8.75 5.80625L6.25 3.55625C6.09062 3.4125 6.00313 3.2125 6.00313 3C6.00313 2.7875 6.09375 2.58437 6.25 2.44375L8.75 0.19375C8.96875 -0.00312501 9.2875 -0.053125 9.55625 0.065625ZM3.5 2.5C3.5 2.23478 3.39464 1.98043 3.20711 1.79289C3.01957 1.60536 2.76522 1.5 2.5 1.5C2.23478 1.5 1.98043 1.60536 1.79289 1.79289C1.60536 1.98043 1.5 2.23478 1.5 2.5C1.5 2.76522 1.60536 3.01957 1.79289 3.20711C1.98043 3.39464 2.23478 3.5 2.5 3.5C2.76522 3.5 3.01957 3.39464 3.20711 3.20711C3.39464 3.01957 3.5 2.76522 3.5 2.5ZM3.25 4.88438V11.1156C4.26562 11.4344 5 12.3813 5 13.5C5 14.8813 3.88125 16 2.5 16C1.11875 16 0 14.8813 0 13.5C0 12.3813 0.734375 11.4344 1.75 11.1156V4.88438C0.734375 4.56563 0 3.61875 0 2.5C0 1.11875 1.11875 0 2.5 0C3.88125 0 5 1.11875 5 2.5C5 3.61875 4.26562 4.56563 3.25 4.88438ZM3.5 13.5C3.5 13.2348 3.39464 12.9804 3.20711 12.7929C3.01957 12.6054 2.76522 12.5 2.5 12.5C2.23478 12.5 1.98043 12.6054 1.79289 12.7929C1.60536 12.9804 1.5 13.2348 1.5 13.5C1.5 13.7652 1.60536 14.0196 1.79289 14.2071C1.98043 14.3946 2.23478 14.5 2.5 14.5C2.76522 14.5 3.01957 14.3946 3.20711 14.2071C3.39464 14.0196 3.5 13.7652 3.5 13.5ZM13.5 14.5C13.7652 14.5 14.0196 14.3946 14.2071 14.2071C14.3946 14.0196 14.5 13.7652 14.5 13.5C14.5 13.2348 14.3946 12.9804 14.2071 12.7929C14.0196 12.6054 13.7652 12.5 13.5 12.5C13.2348 12.5 12.9804 12.6054 12.7929 12.7929C12.6054 12.9804 12.5 13.2348 12.5 13.5C12.5 13.7652 12.6054 14.0196 12.7929 14.2071C12.9804 14.3946 13.2348 14.5 13.5 14.5Z",
3245
+ fill: theme.palette.type === "dark" ? "white" : "rgba(0, 0, 0, 0.87)"
3246
+ }
3247
+ )
3248
+ );
3249
+ }
3250
+ const RosAcceptance = ({
3251
+ status
3252
+ }) => {
3253
+ const { t } = useTranslationRef(pluginRiScTranslationRef);
3254
+ switch (status) {
3255
+ case RiScStatus.Draft:
3256
+ return /* @__PURE__ */ React.createElement(Typography, { paragraph: true, style: { fontWeight: 700 }, variant: "subtitle1" }, t("rosStatus.statusBadge.missing"));
3257
+ case RiScStatus.SentForApproval:
3258
+ case RiScStatus.Published:
3259
+ return /* @__PURE__ */ React.createElement(Typography, { paragraph: true, style: { fontWeight: 700 }, variant: "subtitle1" }, /* @__PURE__ */ React.createElement(
3260
+ CheckIcon,
3261
+ {
3262
+ fontSize: "medium",
3263
+ style: { transform: "translateY(5px)" }
3264
+ }
3265
+ ), " ", t("rosStatus.statusBadge.approved"));
3266
+ default:
3267
+ return /* @__PURE__ */ React.createElement(Typography, { paragraph: true, variant: "subtitle1" }, t("rosStatus.statusBadge.error"));
3268
+ }
3269
+ };
3193
3270
  const RiScStatusComponent = ({
3194
3271
  selectedRiSc,
3195
3272
  publishRiScFn
@@ -3201,50 +3278,66 @@ const RiScStatusComponent = ({
3201
3278
  publishRiScFn();
3202
3279
  setPublishRiScDialogIsOpen(false);
3203
3280
  };
3204
- return /* @__PURE__ */ React.createElement(Grid, { item: true, container: true, xs: true, direction: "column", alignItems: "flex-end" }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true }, /* @__PURE__ */ React.createElement(StatusChip, { currentRiScStatus: selectedRiSc.status })), /* @__PURE__ */ React.createElement(Grid, { item: true, container: true, spacing: 1, justifyContent: "flex-end" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(
3205
- Button,
3281
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, "Status"), /* @__PURE__ */ React.createElement(RosAcceptance, { status: selectedRiSc.status }), selectedRiSc.status === RiScStatus.SentForApproval && /* @__PURE__ */ React.createElement(Typography, { style: { fontWeight: 700 }, paragraph: true, variant: "subtitle1" }, /* @__PURE__ */ React.createElement(PullRequestSvg, null), t("rosStatus.prStatus"), /* @__PURE__ */ React.createElement(Link$1, { target: "_blank", href: selectedRiSc.pullRequestUrl }, "Github")), /* @__PURE__ */ React.createElement(
3282
+ Grid,
3206
3283
  {
3207
- color: "primary",
3208
- variant: "contained",
3209
- onClick: () => setPublishRiScDialogIsOpen(!publishRiScDialogIsOpen),
3210
- className: approveButton,
3211
- fullWidth: true,
3212
- disabled: selectedRiSc.status !== RiScStatus.Draft
3284
+ item: true,
3285
+ container: true,
3286
+ xs: true,
3287
+ direction: "row",
3288
+ alignItems: "flex-end",
3289
+ justifyContent: "space-between"
3213
3290
  },
3214
- /* @__PURE__ */ React.createElement(Typography, { variant: "button" }, t("rosStatus.approveButton"))
3215
- ))), /* @__PURE__ */ React.createElement(
3216
- RiScPublishDialog,
3217
- {
3218
- openDialog: publishRiScDialogIsOpen,
3219
- handlePublish: handleApproveAndPublish,
3220
- handleCancel: () => setPublishRiScDialogIsOpen(false)
3221
- }
3291
+ /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" })),
3292
+ /* @__PURE__ */ React.createElement(Grid, { item: true }, selectedRiSc.status === RiScStatus.Draft && /* @__PURE__ */ React.createElement(
3293
+ Button,
3294
+ {
3295
+ color: "primary",
3296
+ variant: "contained",
3297
+ onClick: () => setPublishRiScDialogIsOpen(!publishRiScDialogIsOpen),
3298
+ className: approveButton,
3299
+ fullWidth: true,
3300
+ disabled: selectedRiSc.status !== RiScStatus.Draft
3301
+ },
3302
+ /* @__PURE__ */ React.createElement(Typography, { variant: "button" }, t("rosStatus.approveButton"))
3303
+ )),
3304
+ /* @__PURE__ */ React.createElement(
3305
+ RiScPublishDialog,
3306
+ {
3307
+ openDialog: publishRiScDialogIsOpen,
3308
+ handlePublish: handleApproveAndPublish,
3309
+ handleCancel: () => setPublishRiScDialogIsOpen(false)
3310
+ }
3311
+ )
3222
3312
  ));
3223
3313
  };
3224
3314
 
3225
- const useRiScInfoStyles = makeStyles(() => ({
3226
- infoCard: {
3227
- maxWidth: "600px"
3315
+ const useEditButtonStyle = makeStyles((theme) => ({
3316
+ icon: {
3317
+ color: theme.palette.type === "dark" ? "#fff" : "rgba(0, 0, 0, 0.54)"
3228
3318
  }
3229
3319
  }));
3320
+ const EditButton = (props) => {
3321
+ const { icon } = useEditButtonStyle();
3322
+ return /* @__PURE__ */ React.createElement(IconButton$1, { size: "small", ...props }, /* @__PURE__ */ React.createElement(EditIcon, { className: icon, "aria-label": "Edit" }));
3323
+ };
3230
3324
 
3231
3325
  const RiScInfo = ({ riSc, approveRiSc, edit }) => {
3232
3326
  const { t } = useTranslationRef(pluginRiScTranslationRef);
3233
3327
  const { label, body2 } = useFontStyles();
3234
- const { infoCard } = useRiScInfoStyles();
3235
3328
  return /* @__PURE__ */ React.createElement(Grid, { container: true }, /* @__PURE__ */ React.createElement(
3236
3329
  Grid,
3237
3330
  {
3238
3331
  item: true,
3239
3332
  xs: 12,
3240
3333
  sm: 6,
3241
- md: 8,
3334
+ md: 6,
3242
3335
  style: {
3243
3336
  display: "flex",
3244
3337
  flexDirection: "column"
3245
3338
  }
3246
3339
  },
3247
- /* @__PURE__ */ React.createElement(InfoCard, { className: infoCard }, /* @__PURE__ */ React.createElement(
3340
+ /* @__PURE__ */ React.createElement(InfoCard, null, /* @__PURE__ */ React.createElement(
3248
3341
  Box$1,
3249
3342
  {
3250
3343
  style: {
@@ -3254,9 +3347,15 @@ const RiScInfo = ({ riSc, approveRiSc, edit }) => {
3254
3347
  }
3255
3348
  },
3256
3349
  /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, riSc.content.title),
3257
- /* @__PURE__ */ React.createElement(IconButton, { onClick: edit }, /* @__PURE__ */ React.createElement(EditIcon, null))
3350
+ /* @__PURE__ */ React.createElement(EditButton, { onClick: edit })
3258
3351
  ), /* @__PURE__ */ React.createElement(Typography, { className: label }, t("dictionary.scope")), /* @__PURE__ */ React.createElement(Typography, { className: body2 }, riSc.content.scope))
3259
- ), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, sm: 6, md: 4 }, /* @__PURE__ */ React.createElement(RiScStatusComponent, { selectedRiSc: riSc, publishRiScFn: approveRiSc })));
3352
+ ), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, sm: 6, md: 6 }, /* @__PURE__ */ React.createElement(InfoCard, null, /* @__PURE__ */ React.createElement(
3353
+ RiScStatusComponent,
3354
+ {
3355
+ selectedRiSc: riSc,
3356
+ publishRiScFn: approveRiSc
3357
+ }
3358
+ ))));
3260
3359
  };
3261
3360
 
3262
3361
  const useLinearProgressStyle = makeStyles((theme) => ({
@@ -3267,8 +3366,16 @@ const useLinearProgressStyle = makeStyles((theme) => ({
3267
3366
  }
3268
3367
  }));
3269
3368
 
3369
+ var _a;
3370
+ const emotionInsertionPoint = document.createElement("meta");
3371
+ emotionInsertionPoint.setAttribute("name", "emotion-insertion-point");
3372
+ (_a = document.querySelector("head")) == null ? void 0 : _a.appendChild(emotionInsertionPoint);
3373
+ const cache = createCache({
3374
+ key: "css",
3375
+ insertionPoint: emotionInsertionPoint
3376
+ });
3270
3377
  const Plugin = () => {
3271
- var _a, _b;
3378
+ var _a2, _b;
3272
3379
  const params = useParams();
3273
3380
  const { t } = useTranslationRef(pluginRiScTranslationRef);
3274
3381
  const { linearProgress } = useLinearProgressStyle();
@@ -3291,77 +3398,84 @@ const Plugin = () => {
3291
3398
  response,
3292
3399
  isRequesting
3293
3400
  } = useFetchRiScs(params.riScId);
3294
- const scenario = useScenarioDrawer(
3295
- selectedRiSc != null ? selectedRiSc : null,
3296
- updateRiSc,
3297
- params.scenarioId
3298
- );
3299
3401
  const [searchParams] = useSearchParams();
3300
3402
  const scenarioWizardStep = searchParams.get("step") || null;
3301
3403
  useEffect(() => {
3302
3404
  if (scenarioWizardStep !== null)
3303
3405
  resetRiScStatus();
3304
3406
  }, [resetRiScStatus, scenarioWizardStep]);
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(
3306
- ScenarioWizard,
3307
- {
3308
- step: scenarioWizardStep,
3309
- isFetching,
3310
- updateStatus: updateRiScStatus
3311
- }
3312
- ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: t("contentHeader.title") }, /* @__PURE__ */ React.createElement(SupportButton, null)), isFetching && /* @__PURE__ */ React.createElement(Spinner, { size: 80 }), /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 4 }, riScs !== null && riScs.length !== 0 && /* @__PURE__ */ React.createElement(
3313
- Grid,
3407
+ return /* @__PURE__ */ React.createElement(CacheProvider, { value: cache }, /* @__PURE__ */ React.createElement(
3408
+ ScenarioProvider,
3314
3409
  {
3315
- item: true,
3316
- xs: 12,
3317
- sm: 6,
3318
- style: {
3319
- maxWidth: "600px",
3320
- minWidth: "300px"
3321
- }
3410
+ riSc: selectedRiSc != null ? selectedRiSc : null,
3411
+ updateRiSc,
3412
+ scenarioIdFromParams: params.scenarioId
3322
3413
  },
3323
- /* @__PURE__ */ React.createElement(
3324
- Dropdown,
3414
+ response && /* @__PURE__ */ React.createElement(Alert, { severity: getAlertSeverity(response.status) }, /* @__PURE__ */ React.createElement(Typography, null, response.statusMessage)),
3415
+ isRequesting && /* @__PURE__ */ React.createElement(LinearProgress, { className: linearProgress }),
3416
+ scenarioWizardStep !== null ? /* @__PURE__ */ React.createElement(
3417
+ ScenarioWizard,
3325
3418
  {
3326
- options: (_a = riScs.map((riSc) => riSc.content.title)) != null ? _a : [],
3327
- selectedValues: (_b = selectedRiSc == null ? void 0 : selectedRiSc.content.title) != null ? _b : "",
3328
- handleChange: (title) => selectRiSc(title),
3329
- variant: "standard"
3419
+ step: scenarioWizardStep,
3420
+ isFetching,
3421
+ updateStatus: updateRiScStatus
3330
3422
  }
3331
- )
3332
- ), !isFetching && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true }, /* @__PURE__ */ React.createElement(
3333
- Button,
3334
- {
3335
- startIcon: /* @__PURE__ */ React.createElement(AddCircle, null),
3336
- variant: "text",
3337
- color: "primary",
3338
- onClick: openCreateRiScDialog,
3339
- style: {
3340
- minWidth: "205px"
3423
+ ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: t("contentHeader.title") }, /* @__PURE__ */ React.createElement(SupportButton, null)), isFetching && /* @__PURE__ */ React.createElement(Spinner, { size: 80 }), /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 4 }, riScs !== null && riScs.length !== 0 && /* @__PURE__ */ React.createElement(
3424
+ Grid,
3425
+ {
3426
+ item: true,
3427
+ xs: 12,
3428
+ sm: 6,
3429
+ style: {
3430
+ maxWidth: "600px",
3431
+ minWidth: "300px"
3432
+ }
3433
+ },
3434
+ /* @__PURE__ */ React.createElement(
3435
+ Dropdown,
3436
+ {
3437
+ options: (_a2 = riScs.map((riSc) => riSc.content.title)) != null ? _a2 : [],
3438
+ selectedValues: (_b = selectedRiSc == null ? void 0 : selectedRiSc.content.title) != null ? _b : "",
3439
+ handleChange: (title) => selectRiSc(title),
3440
+ variant: "standard"
3441
+ }
3442
+ )
3443
+ ), !isFetching && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true }, /* @__PURE__ */ React.createElement(
3444
+ Button,
3445
+ {
3446
+ startIcon: /* @__PURE__ */ React.createElement(AddCircle, null),
3447
+ variant: "text",
3448
+ color: "primary",
3449
+ onClick: openCreateRiScDialog,
3450
+ style: {
3451
+ minWidth: "205px"
3452
+ }
3453
+ },
3454
+ t("contentHeader.createNewButton")
3455
+ )), selectedRiSc && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
3456
+ RiScInfo,
3457
+ {
3458
+ riSc: selectedRiSc,
3459
+ approveRiSc,
3460
+ edit: openEditRiScDialog
3341
3461
  }
3342
- },
3343
- t("contentHeader.createNewButton")
3344
- )), selectedRiSc && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
3345
- RiScInfo,
3346
- {
3347
- riSc: selectedRiSc,
3348
- approveRiSc,
3349
- edit: openEditRiScDialog
3350
- }
3351
- )), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true, md: 7, lg: 8 }, /* @__PURE__ */ React.createElement(ScenarioTable, { riSc: selectedRiSc.content })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true, md: 5, lg: 4 }, /* @__PURE__ */ React.createElement(RiskMatrix, { riSc: selectedRiSc.content }))))), RiScDialogState !== RiScDialogStates.Closed && /* @__PURE__ */ React.createElement(
3352
- RiScDialog,
3353
- {
3354
- onClose: closeRiScDialog,
3355
- createNewRiSc,
3356
- updateRiSc,
3357
- riSc: selectedRiSc,
3358
- dialogState: RiScDialogState
3359
- }
3360
- ), !scenarioWizardStep && /* @__PURE__ */ React.createElement(ScenarioDrawer, null)));
3462
+ )), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true, md: 7, lg: 8 }, /* @__PURE__ */ React.createElement(ScenarioTable, { riSc: selectedRiSc.content })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true, md: 5, lg: 4 }, /* @__PURE__ */ React.createElement(RiskMatrix, { riSc: selectedRiSc.content }))))),
3463
+ RiScDialogState !== RiScDialogStates.Closed && /* @__PURE__ */ React.createElement(
3464
+ RiScDialog,
3465
+ {
3466
+ onClose: closeRiScDialog,
3467
+ createNewRiSc,
3468
+ updateRiSc,
3469
+ riSc: selectedRiSc,
3470
+ dialogState: RiScDialogState
3471
+ }
3472
+ ),
3473
+ !scenarioWizardStep && /* @__PURE__ */ React.createElement(ScenarioDrawer, null)
3474
+ ));
3361
3475
  };
3362
3476
  const RiScPlugin = () => {
3363
3477
  return /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, { path: "/", element: /* @__PURE__ */ React.createElement(Plugin, null) }), /* @__PURE__ */ React.createElement(Route, { path: riScRouteRef.path, element: /* @__PURE__ */ React.createElement(Plugin, null) }), /* @__PURE__ */ React.createElement(Route, { path: scenarioRouteRef.path, element: /* @__PURE__ */ React.createElement(Plugin, null) }));
3364
3478
  };
3365
3479
 
3366
3480
  export { RiScPlugin };
3367
- //# sourceMappingURL=index-b5d09407.esm.js.map
3481
+ //# sourceMappingURL=index-99d0fef8.esm.js.map