From 75034f37f6ffc187f429246a8e0d868fa4cf2ed5 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 21 Apr 2022 17:41:03 +0300 Subject: [PATCH] UI: Replace startOverPage with an improved Island reset modal New modal allows to save clicks, explains the situation better, offers to export the config and allows deleting agent data without deleting config --- .../cc/ui/src/components/Main.tsx | 4 - .../cc/ui/src/components/SideNavComponent.tsx | 58 +++++--- .../layouts/SidebarLayoutComponent.tsx | 6 +- .../ui/src/components/pages/LandingPage.tsx | 5 +- .../cc/ui/src/components/pages/ResetPage.js | 104 -------------- .../ui-components/IslandResetModal.js | 76 ---------- .../ui-components/IslandResetModal.tsx | 130 ++++++++++++++++++ .../styles/components/IslandResetModal.scss | 9 ++ .../cc/ui/src/styles/components/SideNav.scss | 7 + .../cc/ui/src/styles/pages/StartOverPage.scss | 9 -- 10 files changed, 192 insertions(+), 216 deletions(-) delete mode 100644 monkey/monkey_island/cc/ui/src/components/pages/ResetPage.js delete mode 100644 monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.js create mode 100644 monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.tsx create mode 100644 monkey/monkey_island/cc/ui/src/styles/components/IslandResetModal.scss delete mode 100644 monkey/monkey_island/cc/ui/src/styles/pages/StartOverPage.scss diff --git a/monkey/monkey_island/cc/ui/src/components/Main.tsx b/monkey/monkey_island/cc/ui/src/components/Main.tsx index 2b31ae37b..e28013df2 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.tsx +++ b/monkey/monkey_island/cc/ui/src/components/Main.tsx @@ -6,7 +6,6 @@ import ConfigurePage from './pages/ConfigurePage.js'; import RunMonkeyPage from './pages/RunMonkeyPage/RunMonkeyPage'; import MapPage from './pages/MapPage'; import TelemetryPage from './pages/TelemetryPage'; -import StartOverPage from './pages/ResetPage'; import ReportPage from './pages/ReportPage'; import LicensePage from './pages/LicensePage'; import AuthComponent from './AuthComponent'; @@ -47,7 +46,6 @@ export const Routes = { RunMonkeyPage: '/run-monkey', MapPage: '/infection/map', TelemetryPage: '/infection/telemetry', - StartOverPage: '/start-over', LicensePage: '/license' } @@ -232,8 +230,6 @@ class AppComponent extends AuthComponent { )} {this.renderRoute(Routes.TelemetryPage, )} - {this.renderRoute(Routes.StartOverPage, - )} {this.redirectToReport()} {this.renderRoute(Routes.SecurityReport, void } -const SideNavComponent = ({disabled, - completedSteps, - defaultReport, - header=null}: Props) => { +const SideNavComponent = ({ + disabled, + completedSteps, + defaultReport, + header = null, + onStatusChange, + }: Props) => { + + const [showResetModal, setShowResetModal] = useState(false); return ( <> @@ -37,12 +44,12 @@ const SideNavComponent = ({disabled,
    {(header !== null) && - <> -
  • - {header} -
  • -
    - } + <> +
  • + {header} +
  • +
    + }
  • @@ -76,10 +83,17 @@ const SideNavComponent = ({disabled,
  • - + + { + setShowResetModal(false); + onStatusChange(); + }}/>
@@ -90,7 +104,7 @@ const SideNavComponent = ({disabled, Configuration
  • + className={getNavLinkClass()}> Telemetries
  • @@ -98,8 +112,12 @@ const SideNavComponent = ({disabled, ); + function areMonkeysDead() { + return (!completedSteps['runMonkey']) || (completedSteps['infectionDone']) + } + function getNavLinkClass() { - if(disabled){ + if (disabled) { return `nav-link disabled` } else { return '' diff --git a/monkey/monkey_island/cc/ui/src/components/layouts/SidebarLayoutComponent.tsx b/monkey/monkey_island/cc/ui/src/components/layouts/SidebarLayoutComponent.tsx index c8bac4396..55f22d8b6 100644 --- a/monkey/monkey_island/cc/ui/src/components/layouts/SidebarLayoutComponent.tsx +++ b/monkey/monkey_island/cc/ui/src/components/layouts/SidebarLayoutComponent.tsx @@ -9,6 +9,7 @@ const SidebarLayoutComponent = ({component: Component, completedSteps = null, defaultReport = '', sideNavHeader = (<>), + onStatusChange = () => {}, ...other }) => ( { @@ -18,9 +19,10 @@ const SidebarLayoutComponent = ({component: Component, + header={sideNavHeader} + onStatusChange={onStatusChange}/> } - + ) }}/> ) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx b/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx index cc1741b20..d95a84dfe 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx +++ b/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx @@ -12,8 +12,11 @@ import Logo from "../logo/LogoComponent"; const monkeyIcon = require('../../images/monkey-icon.svg') const infectionMonkey = require('../../images/infection-monkey.svg') -const LandingPageComponent = (props) => { +type Props = { + onStatusChange: () => void +} +const LandingPageComponent = (props: Props) => { return ( <> diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ResetPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ResetPage.js deleted file mode 100644 index 6d0c5bb98..000000000 --- a/monkey/monkey_island/cc/ui/src/components/pages/ResetPage.js +++ /dev/null @@ -1,104 +0,0 @@ -import React from 'react'; -import {Col, Button} from 'react-bootstrap'; -import {Link} from 'react-router-dom'; -import AuthComponent from '../AuthComponent'; -import IslandResetModal from '../ui-components/IslandResetModal'; -import '../../styles/pages/StartOverPage.scss'; -import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; -import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle'; -import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'; - -class ResetPageComponent extends AuthComponent { - constructor(props) { - super(props); - - this.state = { - cleaned: false, - showCleanDialog: false, - allMonkeysAreDead: false - }; - - this.cleanup = this.cleanup.bind(this); - this.closeModal = this.closeModal.bind(this); - } - - updateMonkeysRunning = () => { - this.authFetch('/api') - .then(res => res.json()) - .then(res => { - // This check is used to prevent unnecessary re-rendering - this.setState({ - allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']) - }); - }); - }; - - render() { - return ( - - -

    Reset

    -
    -

    - If you are finished and want to start over with a fresh configuration, erase the logs and clear the map - you can go ahead and -

    -

    - -

    -
    - - You don't have to reset the environment to keep running monkeys. - You can continue and Run More Monkeys as you wish, - and see the results on the Infection Map without deleting anything. -
    - {this.state.cleaned ? -
    - - Environment was reset successfully -
    - : ''} -
    - - ); - } - - cleanup = () => { - this.setState({ - cleaned: false - }); - return this.authFetch('/api?action=reset') - .then(res => res.json()) - .then(res => { - if (res['status'] === 'OK') { - this.setState({ - cleaned: true - }); - } - }).then(() => { - this.updateMonkeysRunning(); - this.props.onStatusChange(); - }); - }; - - closeModal = () => { - this.setState({ - showCleanDialog: false - }) - }; -} - -export default ResetPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.js b/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.js deleted file mode 100644 index 8639e46a3..000000000 --- a/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.js +++ /dev/null @@ -1,76 +0,0 @@ -import {Modal} from 'react-bootstrap'; -import React from 'react'; -import {GridLoader} from 'react-spinners'; -import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; -import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle'; - - -class IslandResetModal extends React.PureComponent { - - constructor(props) { - super(props); - - this.state = { - showCleanDialog: this.props.showCleanDialog, - allMonkeysAreDead: this.props.allMonkeysAreDead, - loading: false - }; - } - - componentDidUpdate(prevProps) { - if (this.props !== prevProps) { - this.setState({ showCleanDialog: this.props.showCleanDialog, - allMonkeysAreDead: this.props.allMonkeysAreDead}) - } - } - - render = () => { - return ( - this.props.onClose()}> - -

    -
    Reset environment
    -

    -

    - Are you sure you want to reset the environment? -

    - { - !this.state.allMonkeysAreDead ? -
    - - Some monkeys are still running. It's advised to kill all monkeys before resetting. -
    - : -
    - } - { - this.state.loading ?
    : this.showModalButtons() - } - - - ) - }; - - showModalButtons() { - return (
    - - -
    ) - } - - modalVerificationOnClick = async () => { - this.setState({loading: true}); - this.props.onVerify() - .then(() => {this.setState({loading: false}); - this.props.onClose();}) - - } -} - -export default IslandResetModal; diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.tsx b/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.tsx new file mode 100644 index 000000000..c31d5c401 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.tsx @@ -0,0 +1,130 @@ +import {Button, Col, Container, Modal, NavLink, Row} from 'react-bootstrap'; +import React, {useState} from 'react'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle'; + +import '../../styles/components/IslandResetModal.scss'; +import {Routes} from '../Main'; +import LoadingIcon from './LoadingIcon'; +import {faCheck} from '@fortawesome/free-solid-svg-icons'; +import AuthService from '../../services/AuthService'; + +type Props = { + show: boolean, + allMonkeysAreDead: boolean, + onClose: () => void +} + +// Button statuses +const Idle = 1; +const Loading = 2; +const Done = 3; + +const IslandResetModal = (props: Props) => { + + const [resetAllStatus, setResetAll] = useState(Idle); + const [deleteStatus, setDeleteStatus] = useState(Idle); + const auth = new AuthService(); + + return ( + { + setDeleteStatus(Idle); + props.onClose() + }} size={'lg'}> + + Reset the Island + + + { + !props.allMonkeysAreDead ? +
    + + Can't reset the Island while Monkey agents are still running! +
    + : + showModalButtons() + } +
    +
    + ) + + function displayDeleteData() { + if (deleteStatus === Idle) { + return ( + + ) + } else if (deleteStatus === Loading) { + return () + } else if (deleteStatus === Done) { + return () + } + } + + function displayResetAll() { + if (resetAllStatus === Idle) { + return ( + + ) + } else if (resetAllStatus === Loading) { + return () + } else if (resetAllStatus === Done) { + return () + } + } + + function resetIsland(url: string, callback: () => void) { + auth.authFetch(url) + .then(res => res.json()) + .then(res => { + if (res['status'] === 'OK') { + callback() + } + }) + } + + function showModalButtons() { + return ( + + +

    Delete data gathered by Monkey agents.

    +

    This will reset the Map and reports.

    + + + {displayDeleteData()} + +
    +
    + + +

    Reset everything.

    +

    You might want to before doing this.

    + + + {displayResetAll()} + +
    +
    ) + } +} + +export default IslandResetModal; diff --git a/monkey/monkey_island/cc/ui/src/styles/components/IslandResetModal.scss b/monkey/monkey_island/cc/ui/src/styles/components/IslandResetModal.scss new file mode 100644 index 000000000..2c1c71110 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/components/IslandResetModal.scss @@ -0,0 +1,9 @@ +.island-reset-modal p { + font-size: 16.5px; + margin: 0; + padding-left: 5px; +} + +.island-reset-modal a { + padding: 0 0 4px 0; +} diff --git a/monkey/monkey_island/cc/ui/src/styles/components/SideNav.scss b/monkey/monkey_island/cc/ui/src/styles/components/SideNav.scss index 949191d13..078b7daa2 100644 --- a/monkey/monkey_island/cc/ui/src/styles/components/SideNav.scss +++ b/monkey/monkey_island/cc/ui/src/styles/components/SideNav.scss @@ -1,6 +1,13 @@ .sidebar .version-text { position: relative; } + .sidebar .license-link { position: relative; } + +.navigation .island-reset-button { + width: 100%; + text-align: left; + padding-left: 18px; +} diff --git a/monkey/monkey_island/cc/ui/src/styles/pages/StartOverPage.scss b/monkey/monkey_island/cc/ui/src/styles/pages/StartOverPage.scss deleted file mode 100644 index ee4ab65ea..000000000 --- a/monkey/monkey_island/cc/ui/src/styles/pages/StartOverPage.scss +++ /dev/null @@ -1,9 +0,0 @@ -$yellow: #ffcc00; - -.modalLoader div{ - margin-left: auto; - margin-right: auto; -} -.modalLoader div>div{ - background-color: $yellow; -}