diff options
8 files changed, 66 insertions, 30 deletions
diff --git a/frontend/public/index.html b/frontend/public/index.html index 0254eff4..b19fdbda 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -37,8 +37,8 @@ <meta name="google-site-verification" content="YIR4LkQTv6WmOdWv8MkeiUKni-0Yu3WHylLp4VvUMig"/> <!-- CDN dependencies --> - <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" - integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> + <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" + integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet"> <script src="https://use.fontawesome.com/ece66a2e7c.js"></script> @@ -61,14 +61,14 @@ </noscript> <div id="root"></div> -<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" - integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" +<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" + integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> -<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" - integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" +<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" + integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> -<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" - integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" +<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" + integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> </body> </html> diff --git a/frontend/src/components/app/results/PortfolioResultsComponent.js b/frontend/src/components/app/results/PortfolioResultsComponent.js index e9b33777..286dd48c 100644 --- a/frontend/src/components/app/results/PortfolioResultsComponent.js +++ b/frontend/src/components/app/results/PortfolioResultsComponent.js @@ -54,18 +54,24 @@ const PortfolioResultsComponent = ({ portfolio, scenarios }) => { <div className="col-6 mb-2" key={metric}> <h4>{METRIC_NAMES[metric]}</h4> <ResponsiveContainer aspect={16 / 9} width="100%"> - <ComposedChart data={dataPerMetric[metric]} margin={{ bottom: 15 }} layout="vertical" animationDUration={0}> - <CartesianGrid strokeDasharray="3 3"/> - <XAxis tickFormatter={(tick) => approx(tick)} - label={{ value: METRIC_UNITS[metric], position: 'bottom', offset: 0 }} - type="number"/> - <YAxis dataKey="name" type="category"/> - <Bar dataKey="value" fill="#3399FF"/> - <Scatter dataKey="value" opacity={0}> - <ErrorBar dataKey="errorX" width={10} strokeWidth={3} stroke="#FF6600" - direction="x"/> + <ComposedChart data={dataPerMetric[metric]} margin={{ bottom: 15 }} layout="vertical"> + <CartesianGrid strokeDasharray="3 3" /> + <XAxis + tickFormatter={(tick) => approx(tick)} + label={{ value: METRIC_UNITS[metric], position: 'bottom', offset: 0 }} + type="number" + /> + <YAxis dataKey="name" type="category" /> + <Bar dataKey="value" fill="#3399FF" isAnimationActive={false} /> + <Scatter dataKey="value" opacity={0} isAnimationActive={false}> + <ErrorBar + dataKey="errorX" + width={10} + strokeWidth={3} + stroke="#FF6600" + direction="x" + /> </Scatter> - </ComposedChart> </ResponsiveContainer> </div> diff --git a/frontend/src/components/app/sidebars/project/PortfolioListComponent.js b/frontend/src/components/app/sidebars/project/PortfolioListComponent.js index 837666af..b000b9e2 100644 --- a/frontend/src/components/app/sidebars/project/PortfolioListComponent.js +++ b/frontend/src/components/app/sidebars/project/PortfolioListComponent.js @@ -37,13 +37,13 @@ class PortfolioListComponent extends React.Component { <div className="row mb-1"> <div className={ - 'col-8 align-self-center ' + + 'col-7 align-self-center ' + (portfolio._id === this.props.currentPortfolioId ? 'font-weight-bold' : '') } > {portfolio.name} </div> - <div className="col-4 text-right"> + <div className="col-5 text-right"> <Link className="btn btn-outline-primary mr-1 fa fa-play" to={`/projects/${this.props.currentProjectId}/portfolios/${portfolio._id}`} diff --git a/frontend/src/components/app/sidebars/project/ProjectSidebarComponent.js b/frontend/src/components/app/sidebars/project/ProjectSidebarComponent.js index aadc451e..4789315e 100644 --- a/frontend/src/components/app/sidebars/project/ProjectSidebarComponent.js +++ b/frontend/src/components/app/sidebars/project/ProjectSidebarComponent.js @@ -5,8 +5,10 @@ import PortfolioListContainer from '../../../../containers/app/sidebars/project/ const ProjectSidebarComponent = ({ collapsible }) => ( <Sidebar isRight={false} collapsible={collapsible}> - <TopologyListContainer /> - <PortfolioListContainer /> + <div className="h-100 overflow-auto container-fluid"> + <TopologyListContainer /> + <PortfolioListContainer /> + </div> </Sidebar> ) diff --git a/frontend/src/components/app/sidebars/project/ScenarioListComponent.js b/frontend/src/components/app/sidebars/project/ScenarioListComponent.js index 8b50eff0..e775a663 100644 --- a/frontend/src/components/app/sidebars/project/ScenarioListComponent.js +++ b/frontend/src/components/app/sidebars/project/ScenarioListComponent.js @@ -26,13 +26,13 @@ class ScenarioListComponent extends React.Component { <div key={scenario._id} className="row mb-1"> <div className={ - 'col-8 pl-5 align-self-center ' + + 'col-7 pl-5 align-self-center ' + (scenario._id === this.props.currentScenarioId ? 'font-weight-bold' : '') } > {scenario.name} </div> - <div className="col-4 text-right"> + <div className="col-5 text-right"> <Link className="btn btn-outline-primary mr-1 fa fa-play disabled" to={`/projects/${this.props.currentProjectId}/portfolios/${scenario.portfolioId}/scenarios/${scenario._id}`} diff --git a/frontend/src/components/app/sidebars/project/TopologyListComponent.js b/frontend/src/components/app/sidebars/project/TopologyListComponent.js index 498ee26d..2f42f7e4 100644 --- a/frontend/src/components/app/sidebars/project/TopologyListComponent.js +++ b/frontend/src/components/app/sidebars/project/TopologyListComponent.js @@ -34,13 +34,13 @@ class TopologyListComponent extends React.Component { <div key={topology._id} className="row mb-1"> <div className={ - 'col-8 align-self-center ' + + 'col-7 align-self-center ' + (topology._id === this.props.currentTopologyId ? 'font-weight-bold' : '') } > {topology.name} </div> - <div className="col-4 text-right"> + <div className="col-5 text-right"> <span className="btn btn-outline-primary mr-1 fa fa-play" onClick={() => this.onChoose(topology._id)} diff --git a/frontend/src/sagas/portfolios.js b/frontend/src/sagas/portfolios.js index 3c004282..ed9bfd29 100644 --- a/frontend/src/sagas/portfolios.js +++ b/frontend/src/sagas/portfolios.js @@ -1,4 +1,4 @@ -import { call, put, select } from 'redux-saga/effects' +import { call, put, select, delay } from 'redux-saga/effects' import { addPropToStoreObject, addToStore } from '../actions/objects' import { addPortfolio, deletePortfolio, getPortfolio, updatePortfolio } from '../api/routes/portfolios' import { getProject } from '../api/routes/projects' @@ -15,7 +15,34 @@ export function* onOpenPortfolioSucceeded(action) { yield fetchAndStoreAllSchedulers() yield fetchAndStoreAllTraces() - // TODO Fetch portfolio-specific metrics + yield watchForPortfolioResults() + } catch (error) { + console.error(error) + } +} + +export function* watchForPortfolioResults() { + try { + const currentPortfolioId = yield select((state) => state.currentPortfolioId) + let unfinishedScenarios = yield getCurrentUnfinishedScenarios() + + while (unfinishedScenarios.length > 0) { + yield delay(3000) + yield fetchPortfolioWithScenarios(currentPortfolioId) + unfinishedScenarios = yield getCurrentUnfinishedScenarios() + } + } catch (error) { + console.error(error) + } +} + +export function* getCurrentUnfinishedScenarios() { + try { + const currentPortfolioId = yield select((state) => state.currentPortfolioId) + const scenarioIds = yield select((state) => state.objects.portfolio[currentPortfolioId].scenarioIds) + const scenarioObjects = yield select((state) => state.objects.scenario) + const scenarios = scenarioIds.map((s) => scenarioObjects[s]) + return scenarios.filter((s) => !s || s.simulation.state === 'QUEUED' || s.simulation.state === 'RUNNING') } catch (error) { console.error(error) } diff --git a/frontend/src/sagas/scenarios.js b/frontend/src/sagas/scenarios.js index 3ecc3fc4..720c0c97 100644 --- a/frontend/src/sagas/scenarios.js +++ b/frontend/src/sagas/scenarios.js @@ -4,7 +4,7 @@ import { getProject } from '../api/routes/projects' import { fetchAndStoreAllSchedulers, fetchAndStoreAllTraces } from './objects' import { fetchAndStoreAllTopologiesOfProject } from './topology' import { addScenario, deleteScenario, updateScenario } from '../api/routes/scenarios' -import { fetchPortfolioWithScenarios } from './portfolios' +import { fetchPortfolioWithScenarios, watchForPortfolioResults } from './portfolios' export function* onOpenScenarioSucceeded(action) { try { @@ -32,6 +32,7 @@ export function* onAddScenario(action) { scenarioIds: scenarioIds.concat([scenario._id]), }) ) + yield watchForPortfolioResults() } catch (error) { console.error(error) } |
