diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2021-07-14 22:23:40 +0200 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2021-07-15 15:55:56 +0200 |
| commit | 803e13b32cf0ff8b496649fb0a4d6e32400e98a4 (patch) | |
| tree | 263a6f9741c5ca0dd64ecf3f7f07b580331aec9d /opendc-web/opendc-web-ui/src/components/app/results/PortfolioResults.js | |
| parent | e200dbfdc076ac6263c9ac6f9dabdcc475f01d6e (diff) | |
feat(ui): Migrate to PatternFly 4 design framework
This change is a rewrite of the existing OpenDC frontend in order to
migrate to the PatternFly 4 design framework.
PatternFly is used by Red Hat for various computing related services such
as OpenShift, Red Hat Virtualization and Cockpit. Since their design
requirements are very similar to those of OpenDC (modeling computing
services), migrating to PatternFly 4 allows us to re-use design choices
from these services.
See https://www.patternfly.org/v4/ for more information about
PatternFly.
Diffstat (limited to 'opendc-web/opendc-web-ui/src/components/app/results/PortfolioResults.js')
| -rw-r--r-- | opendc-web/opendc-web-ui/src/components/app/results/PortfolioResults.js | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResults.js b/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResults.js new file mode 100644 index 00000000..6a96c70c --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResults.js @@ -0,0 +1,134 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { Bar, CartesianGrid, ComposedChart, ErrorBar, ResponsiveContainer, Scatter, XAxis, YAxis } from 'recharts' +import { AVAILABLE_METRICS, METRIC_NAMES, METRIC_NAMES_SHORT, METRIC_UNITS } from '../../../util/available-metrics' +import { mean, std } from 'mathjs' +import approx from 'approximate-number' +import { + Bullseye, + Card, + CardActions, + CardBody, + CardHeader, + CardTitle, + EmptyState, + EmptyStateBody, + EmptyStateIcon, + Grid, + GridItem, + Spinner, + Title, +} from '@patternfly/react-core' +import { ErrorCircleOIcon, CubesIcon } from '@patternfly/react-icons' +import { usePortfolioScenarios } from '../../../data/project' +import NewScenario from '../../projects/NewScenario' +import PortfolioResultInfo from './PortfolioResultInfo' + +const PortfolioResults = ({ portfolioId }) => { + const { status, data: scenarios = [] } = usePortfolioScenarios(portfolioId) + + if (status === 'loading') { + return ( + <Bullseye> + <EmptyState> + <EmptyStateIcon variant="container" component={Spinner} /> + <Title size="lg" headingLevel="h4"> + Loading Results + </Title> + </EmptyState> + </Bullseye> + ) + } else if (status === 'error') { + return ( + <Bullseye> + <EmptyState> + <EmptyStateIcon variant="container" component={ErrorCircleOIcon} /> + <Title size="lg" headingLevel="h4"> + Unable to connect + </Title> + <EmptyStateBody> + There was an error retrieving data. Check your connection and try again. + </EmptyStateBody> + </EmptyState> + </Bullseye> + ) + } else if (scenarios.length === 0) { + return ( + <Bullseye> + <EmptyState> + <EmptyStateIcon variant="container" component={CubesIcon} /> + <Title size="lg" headingLevel="h4"> + No results + </Title> + <EmptyStateBody> + No results are currently available for this portfolio. Run a scenario to obtain simulation + results. + </EmptyStateBody> + <NewScenario portfolioId={portfolioId} /> + </EmptyState> + </Bullseye> + ) + } + + const dataPerMetric = {} + + AVAILABLE_METRICS.forEach((metric) => { + dataPerMetric[metric] = scenarios + .filter((scenario) => scenario.results) + .map((scenario) => ({ + name: scenario.name, + value: mean(scenario.results[metric]), + errorX: std(scenario.results[metric]), + })) + }) + + return ( + <Grid hasGutter> + {AVAILABLE_METRICS.map((metric) => ( + <GridItem xl={6} lg={12} key={metric}> + <Card> + <CardHeader> + <CardActions> + <PortfolioResultInfo metric={metric} /> + </CardActions> + <CardTitle>{METRIC_NAMES[metric]}</CardTitle> + </CardHeader> + <CardBody> + <ResponsiveContainer aspect={16 / 9} width="100%"> + <ComposedChart + data={dataPerMetric[metric]} + margin={{ left: 35, 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> + </CardBody> + </Card> + </GridItem> + ))} + </Grid> + ) +} + +PortfolioResults.propTypes = { + portfolioId: PropTypes.string, +} + +export default PortfolioResults |
