From 829be3c973f5ca837431c3ca10909412b675e668 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Thu, 15 Jul 2021 15:10:04 +0200 Subject: refactor(ui): Extract OpenDC landing page from web interface This change extracts the landing page from the web interface in order to separate the development of the two. This allows the landing page to be developed independently of the actual OpenDC web application. --- opendc-web/opendc-web-ui/next.config.js | 9 +++ opendc-web/opendc-web-ui/package.json | 4 +- opendc-web/opendc-web-ui/public/index.html | 70 ------------------ .../src/components/home/ContactSection.js | 66 ----------------- .../src/components/home/ContactSection.module.scss | 20 ------ .../src/components/home/ContentSection.js | 23 ------ .../src/components/home/ContentSection.module.scss | 11 --- .../src/components/home/IntroSection.js | 50 ------------- .../src/components/home/JumbotronHeader.js | 34 --------- .../components/home/JumbotronHeader.module.scss | 31 -------- .../src/components/home/ModelingSection.js | 28 -------- .../src/components/home/ScreenshotSection.js | 39 ---------- .../components/home/ScreenshotSection.module.scss | 5 -- .../src/components/home/SimulationSection.js | 66 ----------------- .../src/components/home/StakeholderSection.js | 47 ------------- .../src/components/home/TeamSection.js | 82 ---------------------- .../src/components/home/TechnologiesSection.js | 47 ------------- .../src/components/navigation/HomeNavbar.js | 16 ----- opendc-web/opendc-web-ui/src/pages/index.js | 42 ----------- .../opendc-web-ui/src/pages/index.module.scss | 16 ----- 20 files changed, 11 insertions(+), 695 deletions(-) delete mode 100644 opendc-web/opendc-web-ui/public/index.html delete mode 100644 opendc-web/opendc-web-ui/src/components/home/ContactSection.js delete mode 100644 opendc-web/opendc-web-ui/src/components/home/ContactSection.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/home/ContentSection.js delete mode 100644 opendc-web/opendc-web-ui/src/components/home/ContentSection.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/home/IntroSection.js delete mode 100644 opendc-web/opendc-web-ui/src/components/home/JumbotronHeader.js delete mode 100644 opendc-web/opendc-web-ui/src/components/home/JumbotronHeader.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/home/ModelingSection.js delete mode 100644 opendc-web/opendc-web-ui/src/components/home/ScreenshotSection.js delete mode 100644 opendc-web/opendc-web-ui/src/components/home/ScreenshotSection.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/home/SimulationSection.js delete mode 100644 opendc-web/opendc-web-ui/src/components/home/StakeholderSection.js delete mode 100644 opendc-web/opendc-web-ui/src/components/home/TeamSection.js delete mode 100644 opendc-web/opendc-web-ui/src/components/home/TechnologiesSection.js delete mode 100644 opendc-web/opendc-web-ui/src/components/navigation/HomeNavbar.js delete mode 100644 opendc-web/opendc-web-ui/src/pages/index.js delete mode 100644 opendc-web/opendc-web-ui/src/pages/index.module.scss (limited to 'opendc-web/opendc-web-ui') diff --git a/opendc-web/opendc-web-ui/next.config.js b/opendc-web/opendc-web-ui/next.config.js index 44ebfff6..97580fa0 100644 --- a/opendc-web/opendc-web-ui/next.config.js +++ b/opendc-web/opendc-web-ui/next.config.js @@ -25,4 +25,13 @@ module.exports = { experimental: { eslint: true, }, + async redirects() { + return [ + { + source: '/', + destination: '/projects', + permanent: true, + }, + ] + }, } diff --git a/opendc-web/opendc-web-ui/package.json b/opendc-web/opendc-web-ui/package.json index 1a906acd..2dfd137c 100644 --- a/opendc-web/opendc-web-ui/package.json +++ b/opendc-web/opendc-web-ui/package.json @@ -10,10 +10,10 @@ ], "homepage": "http://opendc.org", "bugs": { - "url": "https://github.com/atlarge-research/opendc-frontend/issues", + "url": "https://github.com/atlarge-research/opendc/issues", "email": "opendc@atlarge-research.com" }, - "author": "Georgios Andreadis (https://gandreadis.com/)", + "author": "OpenDC Maintainers ", "license": "MIT", "private": true, "dependencies": { diff --git a/opendc-web/opendc-web-ui/public/index.html b/opendc-web/opendc-web-ui/public/index.html deleted file mode 100644 index 19b80e29..00000000 --- a/opendc-web/opendc-web-ui/public/index.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - OpenDC - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - diff --git a/opendc-web/opendc-web-ui/src/components/home/ContactSection.js b/opendc-web/opendc-web-ui/src/components/home/ContactSection.js deleted file mode 100644 index 60a7e6a3..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/ContactSection.js +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react' -import Image from 'next/image' -import { Row, Col } from 'reactstrap' -import ContentSection from './ContentSection' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faExclamationTriangle, faEnvelope } from '@fortawesome/free-solid-svg-icons' -import { faGithub } from '@fortawesome/free-brands-svg-icons' -import { contactSection, tudelftIcon } from './ContactSection.module.scss' - -const ContactSection = () => ( - - - - - -
- atlarge-research/opendc - - - - - -
- opendc@atlarge-research.com - - - - - - TU Delft - - - - - A project by the   - - @Large Research Group - - . - - - - - -
- Disclaimer: - OpenDC is an experimental tool. Your data may get lost, overwritten, or otherwise become unavailable. -
- The OpenDC authors should in no way be liable in the event this happens (see our{' '} - - license - - ). Sorry for the inconvenience. - -
- -) - -export default ContactSection diff --git a/opendc-web/opendc-web-ui/src/components/home/ContactSection.module.scss b/opendc-web/opendc-web-ui/src/components/home/ContactSection.module.scss deleted file mode 100644 index 9ab4fcb1..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/ContactSection.module.scss +++ /dev/null @@ -1,20 +0,0 @@ -.contactSection { - background-color: #444; - color: #ddd; - - a { - color: #ddd; - - &:hover { - color: #fff; - } - } - - .tudelftIcon { - height: 100px; - } - - .disclaimer { - color: #cccccc; - } -} diff --git a/opendc-web/opendc-web-ui/src/components/home/ContentSection.js b/opendc-web/opendc-web-ui/src/components/home/ContentSection.js deleted file mode 100644 index abaa565c..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/ContentSection.js +++ /dev/null @@ -1,23 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import classNames from 'classnames' -import { Container } from 'reactstrap' -import { contentSection } from './ContentSection.module.scss' - -const ContentSection = ({ name, title, children, className }) => ( -
- -

{title}

- {children} -
-
-) - -ContentSection.propTypes = { - name: PropTypes.string.isRequired, - title: PropTypes.string.isRequired, - children: PropTypes.node, - className: PropTypes.string, -} - -export default ContentSection diff --git a/opendc-web/opendc-web-ui/src/components/home/ContentSection.module.scss b/opendc-web/opendc-web-ui/src/components/home/ContentSection.module.scss deleted file mode 100644 index d27a0ce0..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/ContentSection.module.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import 'src/style/_variables.scss'; - -.contentSection { - padding-top: 50px; - padding-bottom: 50px; - text-align: center; - - h1 { - margin-bottom: 30px; - } -} diff --git a/opendc-web/opendc-web-ui/src/components/home/IntroSection.js b/opendc-web/opendc-web-ui/src/components/home/IntroSection.js deleted file mode 100644 index f9000d32..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/IntroSection.js +++ /dev/null @@ -1,50 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import Image from 'next/image' -import { Container, Row, Col } from 'reactstrap' - -const IntroSection = ({ className }) => ( -
- - - -

The datacenter (DC) industry...

-
    -
  • Is worth over $15 bn, and growing
  • -
  • Has many hard-to-grasp concepts
  • -
  • Needs to become accessible to many
  • -
- - - Schematic top-down view of a datacenter -

- - Image source - -

- - -

OpenDC provides...

-
    -
  • Collaborative online DC modeling
  • -
  • Diverse and effective DC simulation
  • -
  • Exploratory DC performance feedback
  • -
- -
-
-
-) - -IntroSection.propTypes = { - className: PropTypes.string, -} - -export default IntroSection diff --git a/opendc-web/opendc-web-ui/src/components/home/JumbotronHeader.js b/opendc-web/opendc-web-ui/src/components/home/JumbotronHeader.js deleted file mode 100644 index 98aa0af2..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/JumbotronHeader.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' -import Image from 'next/image' -import { Container, Jumbotron, Button } from 'reactstrap' -import { jumbotronHeader, jumbotron, dc } from './JumbotronHeader.module.scss' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons' - -const JumbotronHeader = () => ( -
- - -

- OpenDC -

-

Collaborative Datacenter Simulation and Exploration for Everybody

-
- OpenDC -
-

- -

-
-
-
-) - -export default JumbotronHeader diff --git a/opendc-web/opendc-web-ui/src/components/home/JumbotronHeader.module.scss b/opendc-web/opendc-web-ui/src/components/home/JumbotronHeader.module.scss deleted file mode 100644 index 567b3e73..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/JumbotronHeader.module.scss +++ /dev/null @@ -1,31 +0,0 @@ -.jumbotronHeader { - background: #00a6d6; -} - -.jumbotron { - background-color: inherit; - margin-bottom: 0; - text-align: center; - - padding-top: 120px; - padding-bottom: 120px; - - img { - max-width: 110px; - } - - h1 { - color: #fff; - font-size: 4.5em; - - .dc { - color: #fff; - font-weight: bold; - } - } - - :global(.lead) { - color: #fff; - font-size: 1.4em; - } -} diff --git a/opendc-web/opendc-web-ui/src/components/home/ModelingSection.js b/opendc-web/opendc-web-ui/src/components/home/ModelingSection.js deleted file mode 100644 index 8959663a..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/ModelingSection.js +++ /dev/null @@ -1,28 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import ScreenshotSection from './ScreenshotSection' - -const ModelingSection = ({ className }) => ( - -

Collaboratively...

-
    -
  • Model DC layout, and room locations and types
  • -
  • Place racks in rooms and nodes in racks
  • -
  • Add real-world CPU, GPU, memory, storage and network units to each node
  • -
  • Select from diverse scheduling policies
  • -
-
-) - -ModelingSection.propTypes = { - className: PropTypes.string, -} - -export default ModelingSection diff --git a/opendc-web/opendc-web-ui/src/components/home/ScreenshotSection.js b/opendc-web/opendc-web-ui/src/components/home/ScreenshotSection.js deleted file mode 100644 index 58fe1710..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/ScreenshotSection.js +++ /dev/null @@ -1,39 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import Image from 'next/image' -import { Row, Col } from 'reactstrap' -import ContentSection from './ContentSection' -import { screenshot } from './ScreenshotSection.module.scss' - -const ScreenshotSection = ({ className, name, title, imageUrl, caption, imageIsRight, children }) => ( - - - - {children} - - - {caption} -
{caption}
- -
-
-) - -ScreenshotSection.propTypes = { - className: PropTypes.string, - name: PropTypes.string, - title: PropTypes.string, - imageUrl: PropTypes.string, - caption: PropTypes.string, - imageIsRight: PropTypes.bool, - children: PropTypes.node, -} - -export default ScreenshotSection diff --git a/opendc-web/opendc-web-ui/src/components/home/ScreenshotSection.module.scss b/opendc-web/opendc-web-ui/src/components/home/ScreenshotSection.module.scss deleted file mode 100644 index 7e22de32..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/ScreenshotSection.module.scss +++ /dev/null @@ -1,5 +0,0 @@ -.screenshot { - padding-left: 0; - padding-right: 0; - margin-bottom: 5px; -} diff --git a/opendc-web/opendc-web-ui/src/components/home/SimulationSection.js b/opendc-web/opendc-web-ui/src/components/home/SimulationSection.js deleted file mode 100644 index 46ce6a35..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/SimulationSection.js +++ /dev/null @@ -1,66 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import Image from 'next/image' -import { Col, Row } from 'reactstrap' -import ContentSection from './ContentSection' - -const SimulationSection = ({ className }) => { - return ( - - - -

Working with OpenDC:

-
    -
  • Seamlessly switch between construction and simulation modes
  • -
  • - Choose one of several predefined workloads (Business Critical, Workflows, Machine Learning, - Serverless, etc.) -
  • -
  • Compare datacenter topologies using automated plots and visual summaries
  • -
- - - Running an experiment in OpenDC - Running an experiment in OpenDC - -
- - -

OpenDC's Simulator:

-
    -
  • Includes a detailed operational model of modern datacenters
  • -
  • - Support for emerging datacenter technologies around cloud computing,{' '} - serverless computing, big data, and machine learning -
  • -
- - - - OpenDC's Architecture - OpenDC's Architecture - -
-
- ) -} - -SimulationSection.propTypes = { - className: PropTypes.string, -} - -export default SimulationSection diff --git a/opendc-web/opendc-web-ui/src/components/home/StakeholderSection.js b/opendc-web/opendc-web-ui/src/components/home/StakeholderSection.js deleted file mode 100644 index aa689ba4..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/StakeholderSection.js +++ /dev/null @@ -1,47 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Row, Col } from 'reactstrap' -import ContentSection from './ContentSection' - -const Stakeholder = ({ name, title, subtitle }) => ( - - -
-

{title}

-

{subtitle}

-
- -) - -Stakeholder.propTypes = { - name: PropTypes.string, - title: PropTypes.string, - subtitle: PropTypes.string, -} - -const StakeholderSection = ({ className }) => ( - - - - - - - - - -) - -StakeholderSection.propTypes = { - className: PropTypes.string, -} - -export default StakeholderSection diff --git a/opendc-web/opendc-web-ui/src/components/home/TeamSection.js b/opendc-web/opendc-web-ui/src/components/home/TeamSection.js deleted file mode 100644 index dd057a93..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/TeamSection.js +++ /dev/null @@ -1,82 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import Image from 'next/image' -import { Row, Col } from 'reactstrap' -import ContentSection from './ContentSection' - -const TeamLead = ({ photoId, name, description }) => ( - - - - {name} - - -

{name}

-
{description}
- -
- -) - -TeamLead.propTypes = { - photoId: PropTypes.string, - name: PropTypes.string, - description: PropTypes.string, -} - -const TeamMember = ({ photoId, name }) => ( - - - - {name} - - -
{name}
- -
- -) - -TeamMember.propTypes = { - photoId: PropTypes.string, - name: PropTypes.string, -} - -const TeamSection = ({ className }) => ( - - - - - - - - - - - - - - - - - - -) - -TeamSection.propTypes = { - className: PropTypes.string, -} - -export default TeamSection diff --git a/opendc-web/opendc-web-ui/src/components/home/TechnologiesSection.js b/opendc-web/opendc-web-ui/src/components/home/TechnologiesSection.js deleted file mode 100644 index e5131c2a..00000000 --- a/opendc-web/opendc-web-ui/src/components/home/TechnologiesSection.js +++ /dev/null @@ -1,47 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { ListGroup, ListGroupItem } from 'reactstrap' -import ContentSection from './ContentSection' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faWindowMaximize, faTv, faDatabase, faCogs } from '@fortawesome/free-solid-svg-icons' - -const TechnologiesSection = ({ className }) => ( - - - - - - Browser - - JavaScript, React, Redux, Konva - - - - - Server - - Python, Flask, FlaskSocketIO, OpenAPI - - - - - Database - - MongoDB - - - - - Simulator - - Kotlin - - - -) - -TechnologiesSection.propTypes = { - className: PropTypes.string, -} - -export default TechnologiesSection diff --git a/opendc-web/opendc-web-ui/src/components/navigation/HomeNavbar.js b/opendc-web/opendc-web-ui/src/components/navigation/HomeNavbar.js deleted file mode 100644 index 57cb7a53..00000000 --- a/opendc-web/opendc-web-ui/src/components/navigation/HomeNavbar.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' -import { NavItem, NavLink } from 'reactstrap' -import Navbar from './Navbar' - -const HomeNavbar = () => ( - - Stakeholders - Modeling - Project - Technologies - Team - Contact - -) - -export default HomeNavbar diff --git a/opendc-web/opendc-web-ui/src/pages/index.js b/opendc-web/opendc-web-ui/src/pages/index.js deleted file mode 100644 index bb904eb6..00000000 --- a/opendc-web/opendc-web-ui/src/pages/index.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react' -import Head from 'next/head' -import ContactSection from '../components/home/ContactSection' -import IntroSection from '../components/home/IntroSection' -import JumbotronHeader from '../components/home/JumbotronHeader' -import ModelingSection from '../components/home/ModelingSection' -import SimulationSection from '../components/home/SimulationSection' -import StakeholderSection from '../components/home/StakeholderSection' -import TeamSection from '../components/home/TeamSection' -import TechnologiesSection from '../components/home/TechnologiesSection' -import HomeNavbar from '../components/navigation/HomeNavbar' -import { - introSection, - stakeholderSection, - modelingSection, - simulationSection, - technologiesSection, - teamSection, -} from './index.module.scss' - -function Home() { - return ( - <> - - OpenDC - - -
- - - - - - - - -
- - ) -} - -export default Home diff --git a/opendc-web/opendc-web-ui/src/pages/index.module.scss b/opendc-web/opendc-web-ui/src/pages/index.module.scss deleted file mode 100644 index aed1d88f..00000000 --- a/opendc-web/opendc-web-ui/src/pages/index.module.scss +++ /dev/null @@ -1,16 +0,0 @@ -.bodyWrapper { - position: relative; - overflow-y: hidden; -} - -.introSection, -.modelingSection, -.technologiesSection { - background-color: #fff; -} - -.stakeholderSection, -.simulationSection, -.teamSection { - background-color: #f2f2f2; -} -- cgit v1.2.3 From 1ab168fb80fbf3958fbadda00d1c6714cad66c86 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Thu, 3 Jun 2021 17:57:46 +0200 Subject: feat(ui): Add initial support for PatternFly 4 framework This change adds support in our Next.js application for the PatternFly 4 design framework. This framework is built by RedHat and provides several components that are useful for the space in which OpenDC operates. --- opendc-web/opendc-web-ui/next.config.js | 11 ++- opendc-web/opendc-web-ui/package.json | 2 + opendc-web/opendc-web-ui/src/pages/_app.js | 1 + opendc-web/opendc-web-ui/yarn.lock | 123 ++++++++++++++++++++++++++++- 4 files changed, 131 insertions(+), 6 deletions(-) (limited to 'opendc-web/opendc-web-ui') diff --git a/opendc-web/opendc-web-ui/next.config.js b/opendc-web/opendc-web-ui/next.config.js index 97580fa0..56263c86 100644 --- a/opendc-web/opendc-web-ui/next.config.js +++ b/opendc-web/opendc-web-ui/next.config.js @@ -20,7 +20,14 @@ * SOFTWARE. */ -module.exports = { +// PatternFly 4 uses global CSS imports in its distribution files. Therefore, +// we need to transpile the modules before we can use them. +const withTM = require('next-transpile-modules')([ + '@patternfly/react-core', + '@patternfly/react-styles', +]) + +module.exports = withTM({ reactStrictMode: true, experimental: { eslint: true, @@ -34,4 +41,4 @@ module.exports = { }, ] }, -} +}) diff --git a/opendc-web/opendc-web-ui/package.json b/opendc-web/opendc-web-ui/package.json index 2dfd137c..442d63a5 100644 --- a/opendc-web/opendc-web-ui/package.json +++ b/opendc-web/opendc-web-ui/package.json @@ -22,6 +22,7 @@ "@fortawesome/free-brands-svg-icons": "^5.15.3", "@fortawesome/free-solid-svg-icons": "^5.15.3", "@fortawesome/react-fontawesome": "^0.1.14", + "@patternfly/react-core": "^4.135.0", "@sentry/react": "^5.30.0", "@sentry/tracing": "^5.30.0", "approximate-number": "~2.0.0", @@ -32,6 +33,7 @@ "lint-staged": "~10.2.2", "mathjs": "~7.6.0", "next": "^11.0.1", + "next-transpile-modules": "^8.0.0", "normalizr": "^3.6.1", "prettier": "~2.0.5", "prop-types": "~15.7.2", diff --git a/opendc-web/opendc-web-ui/src/pages/_app.js b/opendc-web/opendc-web-ui/src/pages/_app.js index 6a7200d5..78402323 100644 --- a/opendc-web/opendc-web-ui/src/pages/_app.js +++ b/opendc-web/opendc-web-ui/src/pages/_app.js @@ -25,6 +25,7 @@ import Head from 'next/head' import { Provider } from 'react-redux' import { useStore } from '../redux' import '../index.scss' +import '@patternfly/react-core/dist/styles/base.css' import { AuthProvider, useAuth } from '../auth' import * as Sentry from '@sentry/react' import { Integrations } from '@sentry/tracing' diff --git a/opendc-web/opendc-web-ui/yarn.lock b/opendc-web/opendc-web-ui/yarn.lock index bf4c419e..abd2613e 100644 --- a/opendc-web/opendc-web-ui/yarn.lock +++ b/opendc-web/opendc-web-ui/yarn.lock @@ -214,6 +214,34 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@patternfly/react-core@^4.135.0": + version "4.135.0" + resolved "https://registry.yarnpkg.com/@patternfly/react-core/-/react-core-4.135.0.tgz#b64ad4da10a8814926e28fad727bc7690cd60e66" + integrity sha512-DZcONUGOR7Znd6BsUJ4L+KrrnIpyjUvh3JNcYiHW3loytxShCGcx+a04QjOOcZm+MtFhkgs/t51yiC5IP12abA== + dependencies: + "@patternfly/react-icons" "^4.11.0" + "@patternfly/react-styles" "^4.11.0" + "@patternfly/react-tokens" "^4.12.0" + focus-trap "6.2.2" + react-dropzone "9.0.0" + tippy.js "5.1.2" + tslib "1.13.0" + +"@patternfly/react-icons@^4.11.0": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@patternfly/react-icons/-/react-icons-4.11.0.tgz#26790eeff22dc3204aa8cd094470f0a2f915634a" + integrity sha512-WsIX34bO9rhVRmPG0jlV3GoFGfYgPC64TscNV0lxQosiVRnYIA6Z3nBSArtJsxo5Yn6c63glIefC/YTy6D/ZYg== + +"@patternfly/react-styles@^4.11.0": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@patternfly/react-styles/-/react-styles-4.11.0.tgz#0068dcb18e1343242f93fa6024dcc077acd57fb9" + integrity sha512-4eIqTwGI4mjt9DMqX6hnan4eRS+3LUWNaneTEJdmk+flKxtAE/O/OmQHvH4GetDnlSbyfATcA0VFbVtR0aRJAg== + +"@patternfly/react-tokens@^4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@patternfly/react-tokens/-/react-tokens-4.12.0.tgz#2973c7f08a2f35997a0054bbf3c886b3c5c68822" + integrity sha512-Oj+GxqTtx0Yu9IDCTibZLQnpcKp58JneNKEFQkJ29WJydhPG4j6oFFElkK+ub+Ft/f9B1Ky1SsfR9eabo6IykQ== + "@redux-saga/core@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@redux-saga/core/-/core-1.1.3.tgz#3085097b57a4ea8db5528d58673f20ce0950f6a4" @@ -645,6 +673,13 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +attr-accept@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.3.tgz#48230c79f93790ef2775fcec4f0db0f5db41ca52" + integrity sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ== + dependencies: + core-js "^2.5.0" + available-typed-arrays@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" @@ -1024,6 +1059,11 @@ convert-source-map@1.7.0: dependencies: safe-buffer "~5.1.1" +core-js@^2.5.0: + version "2.6.12" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== + core-js@^3.11.0: version "3.12.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.1.tgz#6b5af4ff55616c08a44d386f1f510917ff204112" @@ -1380,6 +1420,14 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" +enhanced-resolve@^5.7.0: + version "5.8.2" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b" + integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -1764,6 +1812,13 @@ file-saver@^1.3.3: resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-1.3.8.tgz#e68a30c7cb044e2fb362b428469feb291c2e09d8" integrity sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg== +file-selector@^0.1.8: + version "0.1.19" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.19.tgz#8ecc9d069a6f544f2e4a096b64a8052e70ec8abf" + integrity sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ== + dependencies: + tslib "^2.0.1" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1815,6 +1870,13 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== +focus-trap@6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-6.2.2.tgz#0e6f391415b0697c99da932702dedd13084fa131" + integrity sha512-qWovH9+LGoKqREvJaTCzJyO0hphQYGz+ap5Hc4NqXHNhZBdxCi5uBPPcaOUw66fHmzXLVwvETLvFgpwPILqKpg== + dependencies: + tabbable "^5.1.4" + foreach@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" @@ -1923,7 +1985,7 @@ globby@^11.0.3: merge2 "^1.3.0" slash "^3.0.0" -graceful-fs@^4.1.2: +graceful-fs@^4.1.2, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== @@ -2646,6 +2708,14 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +next-transpile-modules@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/next-transpile-modules/-/next-transpile-modules-8.0.0.tgz#56375cdc25ae5d23a834195f277fc2737b26cb97" + integrity sha512-Q2f2yB0zMJ8KJbIYAeZoIxG6cSfVk813zr6B5HzsLMBVcJ3FaF8lKr7WG66n0KlHCwjLSmf/6EkgI6QQVWHrDw== + dependencies: + enhanced-resolve "^5.7.0" + escalade "^3.1.1" + next@^11.0.1: version "11.0.1" resolved "https://registry.yarnpkg.com/next/-/next-11.0.1.tgz#b8e3914d153aaf7143cb98c09bcd3c8230eeb17a" @@ -3083,7 +3153,7 @@ pnp-webpack-plugin@1.6.4: dependencies: ts-pnp "^1.1.6" -popper.js@^1.14.4: +popper.js@^1.14.4, popper.js@^1.16.0: version "1.16.1" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== @@ -3132,6 +3202,14 @@ promise-polyfill@^8.2.0: resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.0.tgz#367394726da7561457aba2133c9ceefbd6267da0" integrity sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g== +prop-types-extra@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b" + integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew== + dependencies: + react-is "^16.3.2" + warning "^4.0.0" + prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@~15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -3244,6 +3322,16 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +react-dropzone@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-9.0.0.tgz#4f5223cdcb4d3bd8a66e3298c4041eb0c75c4634" + integrity sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw== + dependencies: + attr-accept "^1.1.3" + file-selector "^0.1.8" + prop-types "^15.6.2" + prop-types-extra "^1.1.0" + react-hotkeys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/react-hotkeys/-/react-hotkeys-2.0.0.tgz#a7719c7340cbba888b0e9184f806a9ec0ac2c53f" @@ -3261,7 +3349,7 @@ react-is@17.0.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -3975,6 +4063,11 @@ symbol-observable@^1.2.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== +tabbable@^5.1.4: + version "5.2.0" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.2.0.tgz#4fba60991d8bb89d06e5d9455c92b453acf88fb2" + integrity sha512-0uyt8wbP0P3T4rrsfYg/5Rg3cIJ8Shl1RJ54QMqYxm1TLdWqJD1u6+RQjr2Lor3wmfT7JRHkirIwy99ydBsyPg== + table@^6.0.9: version "6.7.1" resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" @@ -3987,6 +4080,11 @@ table@^6.0.9: string-width "^4.2.0" strip-ansi "^6.0.0" +tapable@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" + integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -4009,6 +4107,13 @@ tiny-emitter@^2.1.0: resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== +tippy.js@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-5.1.2.tgz#5ac91233c59ab482ef5988cffe6e08bd26528e66" + integrity sha512-Qtrv2wqbRbaKMUb6bWWBQWPayvcDKNrGlvihxtsyowhT7RLGEh1STWuy6EMXC6QLkfKPB2MLnf8W2mzql9VDAw== + dependencies: + popper.js "^1.16.0" + to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -4053,11 +4158,21 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" +tslib@1.13.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" + integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -4243,7 +4358,7 @@ vm-browserify@1.1.2, vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -warning@^4.0.2, warning@^4.0.3: +warning@^4.0.0, warning@^4.0.2, warning@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== -- cgit v1.2.3 From e200dbfdc076ac6263c9ac6f9dabdcc475f01d6e Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Sat, 10 Jul 2021 12:41:00 +0200 Subject: fix(ui): Relax topology schema requirements This change fixes an issue where the topology generated by the frontend was not accepted by the API server. --- opendc-web/opendc-web-ui/src/redux/sagas/topology.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'opendc-web/opendc-web-ui') diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/topology.js b/opendc-web/opendc-web-ui/src/redux/sagas/topology.js index 5d9154fd..333c1485 100644 --- a/opendc-web/opendc-web-ui/src/redux/sagas/topology.js +++ b/opendc-web/opendc-web-ui/src/redux/sagas/topology.js @@ -123,8 +123,6 @@ export function* onEditRoomName(action) { try { const topologyId = yield select((state) => state.currentTopologyId) const roomId = yield select((state) => state.interactionLevel.roomId) - const room = Object.assign({}, yield select((state) => state.objects.room[roomId])) - room.name = action.name yield put(addPropToStoreObject('room', roomId, { name: action.name })) yield updateTopologyOnServer(topologyId) } catch (error) { @@ -148,8 +146,6 @@ export function* onEditRackName(action) { try { const topologyId = yield select((state) => state.currentTopologyId) const rackId = yield select((state) => state.objects.tile[state.interactionLevel.tileId].rack) - const rack = Object.assign({}, yield select((state) => state.objects.rack[rackId])) - rack.name = action.name yield put(addPropToStoreObject('rack', rackId, { name: action.name })) yield updateTopologyOnServer(topologyId) } catch (error) { @@ -175,6 +171,7 @@ export function* onAddRackToTile(action) { const rack = { _id: uuid(), name: 'Rack', + tileId: action.tileId, capacity: DEFAULT_RACK_SLOT_CAPACITY, powerCapacityW: DEFAULT_RACK_POWER_CAPACITY, machines: [], @@ -195,6 +192,7 @@ export function* onAddMachine(action) { const machine = { _id: uuid(), + rackId, position: action.position, cpus: [], gpus: [], -- cgit v1.2.3 From 803e13b32cf0ff8b496649fb0a4d6e32400e98a4 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 14 Jul 2021 22:23:40 +0200 Subject: 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. --- opendc-web/opendc-web-ui/next.config.js | 2 + opendc-web/opendc-web-ui/package.json | 7 +- opendc-web/opendc-web-ui/src/auth.js | 8 +- .../opendc-web-ui/src/components/AppHeader.js | 44 ++++++ .../opendc-web-ui/src/components/AppHeaderTools.js | 133 ++++++++++++++++ opendc-web/opendc-web-ui/src/components/AppLogo.js | 46 ++++++ .../src/components/AppLogo.module.scss | 33 ++++ .../opendc-web-ui/src/components/AppNavigation.js | 75 +++++++++ opendc-web/opendc-web-ui/src/components/AppPage.js | 41 +++++ .../src/components/app/map/GrayContainer.js | 34 +++++ .../src/components/app/map/LoadingScreen.js | 12 -- .../src/components/app/map/MapConstants.js | 4 +- .../src/components/app/map/MapStage.js | 66 ++++++++ .../src/components/app/map/MapStage.module.scss | 31 ++++ .../src/components/app/map/MapStageComponent.js | 97 ------------ .../src/components/app/map/RackContainer.js | 37 +++++ .../components/app/map/RackEnergyFillContainer.js | 37 +++++ .../components/app/map/RackSpaceFillContainer.js | 43 ++++++ .../src/components/app/map/RoomContainer.js | 45 ++++++ .../src/components/app/map/TileContainer.js | 46 ++++++ .../src/components/app/map/TopologyContainer.js | 35 +++++ .../src/components/app/map/WallContainer.js | 39 +++++ .../src/components/app/map/controls/Collapse.js | 42 +++++ .../app/map/controls/Collapse.module.scss | 55 +++++++ .../app/map/controls/ExportCanvasComponent.js | 15 -- .../components/app/map/controls/ScaleIndicator.js | 16 ++ .../app/map/controls/ScaleIndicator.module.scss | 10 ++ .../app/map/controls/ScaleIndicatorComponent.js | 16 -- .../controls/ScaleIndicatorComponent.module.scss | 10 -- .../app/map/controls/ToolPanelComponent.js | 13 -- .../map/controls/ToolPanelComponent.module.scss | 6 - .../src/components/app/map/controls/Toolbar.js | 28 ++++ .../app/map/controls/Toolbar.module.scss | 29 ++++ .../app/map/controls/ZoomControlComponent.js | 31 ---- .../src/components/app/map/groups/RackGroup.js | 4 +- .../src/components/app/map/groups/RoomGroup.js | 6 +- .../src/components/app/map/groups/TileGroup.js | 2 +- .../src/components/app/map/groups/TopologyGroup.js | 4 +- .../src/components/app/map/layers/MapLayer.js | 33 ++++ .../components/app/map/layers/MapLayerComponent.js | 2 +- .../components/app/map/layers/ObjectHoverLayer.js | 54 +++++++ .../components/app/map/layers/RoomHoverLayer.js | 67 ++++++++ .../components/app/results/PortfolioResultInfo.js | 40 +++++ .../src/components/app/results/PortfolioResults.js | 134 ++++++++++++++++ .../app/results/PortfolioResultsComponent.js | 93 ----------- .../src/components/app/sidebars/Sidebar.js | 48 ------ .../components/app/sidebars/Sidebar.module.scss | 57 ------- .../app/sidebars/project/PortfolioListComponent.js | 71 --------- .../sidebars/project/ProjectSidebarComponent.js | 21 --- .../app/sidebars/project/ScenarioListComponent.js | 45 ------ .../app/sidebars/project/TopologyListComponent.js | 56 ------- .../app/sidebars/topology/NameComponent.js | 73 +++++++-- .../app/sidebars/topology/TopologySidebar.js | 83 ++++++++++ .../sidebars/topology/TopologySidebar.module.scss | 37 +++++ .../sidebars/topology/TopologySidebarComponent.js | 36 ----- .../topology/building/BuildingSidebarComponent.js | 9 +- .../building/NewRoomConstructionComponent.js | 41 +++-- .../building/NewRoomConstructionContainer.js | 46 ++++++ .../topology/machine/BackToRackComponent.js | 17 --- .../app/sidebars/topology/machine/DeleteMachine.js | 54 +++++++ .../topology/machine/MachineSidebarComponent.js | 39 +++-- .../topology/machine/MachineSidebarContainer.js | 37 +++++ .../sidebars/topology/machine/UnitAddComponent.js | 48 +++--- .../sidebars/topology/machine/UnitAddContainer.js | 42 +++++ .../app/sidebars/topology/machine/UnitComponent.js | 67 -------- .../sidebars/topology/machine/UnitListComponent.js | 122 ++++++++++++--- .../sidebars/topology/machine/UnitListContainer.js | 56 +++++++ .../sidebars/topology/machine/UnitTabsComponent.js | 97 +++--------- .../sidebars/topology/rack/AddPrefabComponent.js | 8 +- .../sidebars/topology/rack/AddPrefabContainer.js | 33 ++++ .../sidebars/topology/rack/BackToRoomComponent.js | 18 --- .../sidebars/topology/rack/DeleteRackContainer.js | 54 +++++++ .../sidebars/topology/rack/EmptySlotComponent.js | 24 --- .../app/sidebars/topology/rack/MachineComponent.js | 51 +++---- .../sidebars/topology/rack/MachineListComponent.js | 69 +++++++-- .../topology/rack/MachineListComponent.module.scss | 3 - .../sidebars/topology/rack/MachineListContainer.js | 56 +++++++ .../sidebars/topology/rack/RackNameContainer.js | 22 +++ .../sidebars/topology/rack/RackSidebarComponent.js | 61 ++++++-- .../topology/rack/RackSidebarComponent.module.scss | 6 +- .../sidebars/topology/rack/RackSidebarContainer.js | 32 ++++ .../topology/room/BackToBuildingComponent.js | 17 --- .../sidebars/topology/room/DeleteRoomComponent.js | 18 --- .../sidebars/topology/room/DeleteRoomContainer.js | 54 +++++++ .../sidebars/topology/room/EditRoomContainer.js | 56 +++++++ .../topology/room/RackConstructionComponent.js | 18 +-- .../topology/room/RackConstructionContainer.js | 46 ++++++ .../app/sidebars/topology/room/RoomName.js | 44 ++++++ .../sidebars/topology/room/RoomSidebarComponent.js | 43 ++++-- .../sidebars/topology/room/RoomSidebarContainer.js | 32 ++++ .../src/components/modals/ConfirmationModal.js | 6 +- .../opendc-web-ui/src/components/modals/Modal.js | 48 ++---- .../src/components/modals/TextInputModal.js | 65 +++++--- .../modals/custom-components/NewPortfolioModal.js | 139 +++++++++++++++++ .../NewPortfolioModalComponent.js | 78 ---------- .../modals/custom-components/NewScenarioModal.js | 159 +++++++++++++++++++ .../custom-components/NewScenarioModalComponent.js | 144 ----------------- .../modals/custom-components/NewTopologyModal.js | 81 ++++++++++ .../custom-components/NewTopologyModalComponent.js | 71 --------- .../components/navigation/AppNavbarComponent.js | 37 ----- .../src/components/navigation/LogoutButton.js | 17 --- .../src/components/navigation/Navbar.js | 120 --------------- .../src/components/navigation/Navbar.module.scss | 36 ----- .../src/components/not-found/BlinkingCursor.js | 6 - .../not-found/BlinkingCursor.module.scss | 13 -- .../src/components/not-found/CodeBlock.js | 28 ---- .../src/components/not-found/CodeBlock.module.scss | 4 - .../src/components/not-found/TerminalWindow.js | 37 ----- .../not-found/TerminalWindow.module.scss | 61 -------- .../src/components/projects/FilterPanel.js | 18 +-- .../src/components/projects/NewPortfolio.js | 53 +++++++ .../src/components/projects/NewProject.js | 39 +++++ .../src/components/projects/NewProject.module.scss | 26 ++++ .../src/components/projects/NewScenario.js | 64 ++++++++ .../src/components/projects/NewTopology.js | 58 +++++++ .../src/components/projects/PortfolioTable.js | 97 ++++++++++++ .../components/projects/ProjectActionButtons.js | 38 ----- .../src/components/projects/ProjectList.js | 41 ----- .../src/components/projects/ProjectRow.js | 29 ---- .../src/components/projects/ProjectTable.js | 76 +++++++++ .../src/components/projects/ScenarioState.js | 62 ++++++++ .../src/components/projects/ScenarioTable.js | 108 +++++++++++++ .../src/components/projects/TopologyTable.js | 95 ++++++++++++ .../src/components/util/BreadcrumbLink.js | 37 +++++ .../src/components/util/NavItemLink.js | 37 +++++ .../src/components/util/TableEmptyState.js | 103 +++++++++++++ .../src/containers/app/map/GrayContainer.js | 12 -- .../src/containers/app/map/MapStage.js | 26 ---- .../src/containers/app/map/RackContainer.js | 10 -- .../containers/app/map/RackEnergyFillContainer.js | 32 ---- .../containers/app/map/RackSpaceFillContainer.js | 16 -- .../src/containers/app/map/RoomContainer.js | 23 --- .../src/containers/app/map/TileContainer.js | 19 --- .../src/containers/app/map/TopologyContainer.js | 13 -- .../src/containers/app/map/WallContainer.js | 12 -- .../app/map/controls/ScaleIndicatorContainer.js | 10 -- .../app/map/controls/ZoomControlContainer.js | 13 -- .../src/containers/app/map/layers/MapLayer.js | 11 -- .../containers/app/map/layers/ObjectHoverLayer.js | 32 ---- .../containers/app/map/layers/RoomHoverLayer.js | 45 ------ .../app/results/PortfolioResultsContainer.js | 14 -- .../app/sidebars/project/PortfolioListContainer.js | 48 ------ .../sidebars/project/ProjectSidebarContainer.js | 11 -- .../app/sidebars/project/ScenarioListContainer.js | 67 -------- .../app/sidebars/project/TopologyListContainer.js | 67 -------- .../sidebars/topology/TopologySidebarContainer.js | 10 -- .../topology/building/BuildingSidebarContainer.js | 5 - .../building/NewRoomConstructionContainer.js | 28 ---- .../topology/machine/BackToRackContainer.js | 11 -- .../topology/machine/DeleteMachineContainer.js | 34 ----- .../topology/machine/MachineNameContainer.js | 9 -- .../topology/machine/MachineSidebarContainer.js | 15 -- .../sidebars/topology/machine/UnitAddContainer.js | 15 -- .../sidebars/topology/machine/UnitListContainer.js | 34 ----- .../sidebars/topology/machine/UnitTabsContainer.js | 5 - .../sidebars/topology/rack/AddPrefabContainer.js | 11 -- .../sidebars/topology/rack/BackToRoomContainer.js | 11 -- .../sidebars/topology/rack/DeleteRackContainer.js | 34 ----- .../sidebars/topology/rack/MachineListContainer.js | 29 ---- .../sidebars/topology/rack/RackNameContainer.js | 33 ---- .../sidebars/topology/rack/RackSidebarContainer.js | 10 -- .../topology/room/BackToBuildingContainer.js | 12 -- .../sidebars/topology/room/DeleteRoomContainer.js | 34 ----- .../sidebars/topology/room/EditRoomContainer.js | 35 ----- .../topology/room/RackConstructionContainer.js | 24 --- .../sidebars/topology/room/RoomNameContainer.js | 31 ---- .../sidebars/topology/room/RoomSidebarContainer.js | 10 -- .../opendc-web-ui/src/containers/auth/Login.js | 21 --- .../opendc-web-ui/src/containers/auth/Logout.js | 10 -- .../src/containers/auth/ProfileName.js | 9 -- .../containers/navigation/AppNavbarContainer.js | 11 -- .../src/containers/projects/NewProjectContainer.js | 34 ----- .../src/containers/projects/ProjectActions.js | 14 -- .../containers/projects/ProjectListContainer.js | 34 ----- opendc-web/opendc-web-ui/src/data/map.js | 4 - opendc-web/opendc-web-ui/src/data/project.js | 52 +++---- opendc-web/opendc-web-ui/src/data/topology.js | 10 +- opendc-web/opendc-web-ui/src/index.scss | 68 --------- opendc-web/opendc-web-ui/src/pages/404.js | 33 +++- opendc-web/opendc-web-ui/src/pages/404.module.scss | 8 - opendc-web/opendc-web-ui/src/pages/_app.js | 15 +- opendc-web/opendc-web-ui/src/pages/logout.js | 10 +- .../src/pages/projects/[project]/index.js | 111 +++++++++++++- .../projects/[project]/portfolios/[portfolio].js | 170 ++++++++++++++++++--- .../projects/[project]/topologies/[topology].js | 73 ++++++--- .../opendc-web-ui/src/pages/projects/index.js | 90 ++++++++--- opendc-web/opendc-web-ui/src/shapes.js | 2 + opendc-web/opendc-web-ui/src/style/_mixins.scss | 5 - opendc-web/opendc-web-ui/src/style/_variables.scss | 31 ---- opendc-web/opendc-web-ui/src/style/index.scss | 36 +++++ .../opendc-web-ui/src/util/authorizations.js | 10 +- .../opendc-web-ui/src/util/available-metrics.js | 45 +++++- opendc-web/opendc-web-ui/src/util/date-time.js | 14 +- .../opendc-web-ui/src/util/date-time.test.js | 16 +- opendc-web/opendc-web-ui/yarn.lock | 132 ++++++---------- 195 files changed, 4429 insertions(+), 3283 deletions(-) create mode 100644 opendc-web/opendc-web-ui/src/components/AppHeader.js create mode 100644 opendc-web/opendc-web-ui/src/components/AppHeaderTools.js create mode 100644 opendc-web/opendc-web-ui/src/components/AppLogo.js create mode 100644 opendc-web/opendc-web-ui/src/components/AppLogo.module.scss create mode 100644 opendc-web/opendc-web-ui/src/components/AppNavigation.js create mode 100644 opendc-web/opendc-web-ui/src/components/AppPage.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/GrayContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/map/LoadingScreen.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/MapStage.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/MapStage.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/app/map/MapStageComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/RackContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/RackEnergyFillContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/RackSpaceFillContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/RoomContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/TileContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/TopologyContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/WallContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/Collapse.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/Collapse.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/ExportCanvasComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicator.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicator.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.module.scss create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/Toolbar.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/Toolbar.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/app/map/controls/ZoomControlComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/layers/MapLayer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/layers/ObjectHoverLayer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/map/layers/RoomHoverLayer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultInfo.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/results/PortfolioResults.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultsComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/project/ProjectSidebarComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/project/ScenarioListComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/project/TopologyListComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebar.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebar.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebarComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachine.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.module.scss create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomName.js create mode 100644 opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/modals/custom-components/NewPortfolioModal.js delete mode 100644 opendc-web/opendc-web-ui/src/components/modals/custom-components/NewPortfolioModalComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/modals/custom-components/NewScenarioModal.js delete mode 100644 opendc-web/opendc-web-ui/src/components/modals/custom-components/NewScenarioModalComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/modals/custom-components/NewTopologyModal.js delete mode 100644 opendc-web/opendc-web-ui/src/components/modals/custom-components/NewTopologyModalComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/navigation/AppNavbarComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/navigation/LogoutButton.js delete mode 100644 opendc-web/opendc-web-ui/src/components/navigation/Navbar.js delete mode 100644 opendc-web/opendc-web-ui/src/components/navigation/Navbar.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/not-found/BlinkingCursor.js delete mode 100644 opendc-web/opendc-web-ui/src/components/not-found/BlinkingCursor.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/not-found/CodeBlock.js delete mode 100644 opendc-web/opendc-web-ui/src/components/not-found/CodeBlock.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/components/not-found/TerminalWindow.js delete mode 100644 opendc-web/opendc-web-ui/src/components/not-found/TerminalWindow.module.scss create mode 100644 opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js create mode 100644 opendc-web/opendc-web-ui/src/components/projects/NewProject.js create mode 100644 opendc-web/opendc-web-ui/src/components/projects/NewProject.module.scss create mode 100644 opendc-web/opendc-web-ui/src/components/projects/NewScenario.js create mode 100644 opendc-web/opendc-web-ui/src/components/projects/NewTopology.js create mode 100644 opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js delete mode 100644 opendc-web/opendc-web-ui/src/components/projects/ProjectActionButtons.js delete mode 100644 opendc-web/opendc-web-ui/src/components/projects/ProjectList.js delete mode 100644 opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js create mode 100644 opendc-web/opendc-web-ui/src/components/projects/ProjectTable.js create mode 100644 opendc-web/opendc-web-ui/src/components/projects/ScenarioState.js create mode 100644 opendc-web/opendc-web-ui/src/components/projects/ScenarioTable.js create mode 100644 opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js create mode 100644 opendc-web/opendc-web-ui/src/components/util/BreadcrumbLink.js create mode 100644 opendc-web/opendc-web-ui/src/components/util/NavItemLink.js create mode 100644 opendc-web/opendc-web-ui/src/components/util/TableEmptyState.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/GrayContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/RackContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/RackEnergyFillContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/RackSpaceFillContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/RoomContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/TileContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/TopologyContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/WallContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/controls/ScaleIndicatorContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/controls/ZoomControlContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/layers/MapLayer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/layers/ObjectHoverLayer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/map/layers/RoomHoverLayer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/results/PortfolioResultsContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/TopologySidebarContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/BuildingSidebarContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/NewRoomConstructionContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/BackToRackContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/DeleteMachineContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineNameContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineSidebarContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitAddContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitListContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitTabsContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/AddPrefabContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/BackToRoomContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/DeleteRackContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineListContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackNameContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackSidebarContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/BackToBuildingContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/DeleteRoomContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/EditRoomContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RackConstructionContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomNameContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomSidebarContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/auth/Login.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/auth/Logout.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/auth/ProfileName.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/navigation/AppNavbarContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/projects/ProjectActions.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js delete mode 100644 opendc-web/opendc-web-ui/src/index.scss delete mode 100644 opendc-web/opendc-web-ui/src/pages/404.module.scss delete mode 100644 opendc-web/opendc-web-ui/src/style/_mixins.scss delete mode 100644 opendc-web/opendc-web-ui/src/style/_variables.scss create mode 100644 opendc-web/opendc-web-ui/src/style/index.scss (limited to 'opendc-web/opendc-web-ui') diff --git a/opendc-web/opendc-web-ui/next.config.js b/opendc-web/opendc-web-ui/next.config.js index 56263c86..9092adc4 100644 --- a/opendc-web/opendc-web-ui/next.config.js +++ b/opendc-web/opendc-web-ui/next.config.js @@ -25,6 +25,8 @@ const withTM = require('next-transpile-modules')([ '@patternfly/react-core', '@patternfly/react-styles', + '@patternfly/react-table', + '@patternfly/react-tokens', ]) module.exports = withTM({ diff --git a/opendc-web/opendc-web-ui/package.json b/opendc-web/opendc-web-ui/package.json index 442d63a5..84db4277 100644 --- a/opendc-web/opendc-web-ui/package.json +++ b/opendc-web/opendc-web-ui/package.json @@ -22,11 +22,12 @@ "@fortawesome/free-brands-svg-icons": "^5.15.3", "@fortawesome/free-solid-svg-icons": "^5.15.3", "@fortawesome/react-fontawesome": "^0.1.14", - "@patternfly/react-core": "^4.135.0", + "@patternfly/react-core": "^4.135.7", + "@patternfly/react-icons": "^4.11.2", + "@patternfly/react-table": "^4.29.8", "@sentry/react": "^5.30.0", "@sentry/tracing": "^5.30.0", "approximate-number": "~2.0.0", - "bootstrap": "~4.6.0", "classnames": "~2.2.5", "husky": "~4.2.5", "konva": "~7.2.5", @@ -43,7 +44,6 @@ "react-konva": "~17.0.2-0", "react-query": "^3.18.1", "react-redux": "~7.2.0", - "reactstrap": "^8.9.0", "recharts": "~2.0.9", "redux": "~4.0.5", "redux-logger": "~3.0.6", @@ -51,6 +51,7 @@ "redux-thunk": "~2.3.0", "sass": "^1.32.12", "svgsaver": "~0.9.0", + "use-resize-observer": "^7.0.0", "uuidv4": "~6.1.1" }, "devDependencies": { diff --git a/opendc-web/opendc-web-ui/src/auth.js b/opendc-web/opendc-web-ui/src/auth.js index 96417e67..e670476c 100644 --- a/opendc-web/opendc-web-ui/src/auth.js +++ b/opendc-web/opendc-web-ui/src/auth.js @@ -23,7 +23,6 @@ import PropTypes from 'prop-types' import { Auth0Provider, useAuth0 } from '@auth0/auth0-react' import { useEffect } from 'react' -import { useRouter } from 'next/router' /** * Obtain the authentication context. @@ -37,14 +36,13 @@ export function useAuth() { */ export function useRequireAuth() { const auth = useAuth() - const router = useRouter() - const { isLoading, isAuthenticated } = auth + const { loginWithRedirect, isLoading, isAuthenticated } = auth useEffect(() => { if (!isLoading && !isAuthenticated) { - router.replace('/') + loginWithRedirect() } - }, [router, isLoading, isAuthenticated]) + }, [loginWithRedirect, isLoading, isAuthenticated]) return auth } diff --git a/opendc-web/opendc-web-ui/src/components/AppHeader.js b/opendc-web/opendc-web-ui/src/components/AppHeader.js new file mode 100644 index 00000000..b33212c4 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/AppHeader.js @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { PageHeader } from '@patternfly/react-core' +import React from 'react' +import Image from 'next/image' +import AppHeaderTools from './AppHeaderTools' +import { AppNavigation } from './AppNavigation' +import AppLogo from './AppLogo' + +export function AppHeader() { + const logo = OpenDC + + return ( + } + topNav={} + /> + ) +} + +AppHeader.propTypes = {} diff --git a/opendc-web/opendc-web-ui/src/components/AppHeaderTools.js b/opendc-web/opendc-web-ui/src/components/AppHeaderTools.js new file mode 100644 index 00000000..02e5d265 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/AppHeaderTools.js @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { + Avatar, + Button, + ButtonVariant, + Dropdown, + DropdownGroup, + DropdownItem, + DropdownToggle, + KebabToggle, + PageHeaderTools, + PageHeaderToolsGroup, + PageHeaderToolsItem, + Skeleton, +} from '@patternfly/react-core' +import { useState } from 'react' +import { useAuth } from '../auth' +import { GithubIcon, HelpIcon } from '@patternfly/react-icons' + +function AppHeaderTools() { + const auth = useAuth() + + const [isKebabDropdownOpen, setKebabDropdownOpen] = useState(false) + const kebabDropdownItems = [ + + Help + + } + />, + ] + + const [isDropdownOpen, setDropdownOpen] = useState(false) + const userDropdownItems = [ + + auth.logout({ returnTo: window.location.origin })}> + Logout + + , + ] + + return ( + + + + + + + + + + + + setKebabDropdownOpen(!isKebabDropdownOpen)} />} + isOpen={isKebabDropdownOpen} + dropdownItems={kebabDropdownItems} + /> + + + setDropdownOpen(!isDropdownOpen)}> + {auth?.user?.name ?? ( + + )} + + } + dropdownItems={userDropdownItems} + /> + + + {auth?.user?.picture ? ( + + ) : ( + + )} + + ) +} + +AppHeaderTools.propTypes = {} + +export default AppHeaderTools diff --git a/opendc-web/opendc-web-ui/src/components/AppLogo.js b/opendc-web/opendc-web-ui/src/components/AppLogo.js new file mode 100644 index 00000000..92663295 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/AppLogo.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import Link from 'next/link' +import { appLogo } from './AppLogo.module.scss' + +function AppLogo({ href, children, className, ...props }) { + return ( + <> + + + {children} + OpenDC + + + + ) +} + +AppLogo.propTypes = { + href: PropTypes.string.isRequired, + children: PropTypes.node, + className: PropTypes.string, +} + +export default AppLogo diff --git a/opendc-web/opendc-web-ui/src/components/AppLogo.module.scss b/opendc-web/opendc-web-ui/src/components/AppLogo.module.scss new file mode 100644 index 00000000..3d228cb6 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/AppLogo.module.scss @@ -0,0 +1,33 @@ +/*! + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +.appLogo { + span { + margin-left: 4px; + color: #fff; + } + + &:hover, + &:focus { + text-decoration: none; + } +} diff --git a/opendc-web/opendc-web-ui/src/components/AppNavigation.js b/opendc-web/opendc-web-ui/src/components/AppNavigation.js new file mode 100644 index 00000000..b3f11f34 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/AppNavigation.js @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { Dropdown, DropdownItem, DropdownToggle, Nav, NavItem, NavList } from '@patternfly/react-core' +import { useRouter } from 'next/router' +import NavItemLink from './util/NavItemLink' +import { useProject } from '../data/project' + +export function AppNavigation() { + const { pathname, query } = useRouter() + const { project: projectId } = query + const { data: project } = useProject(projectId) + + const nextTopologyId = project?.topologyIds?.[0] + const nextPortfolioId = project?.portfolioIds?.[0] + + return ( + + ) +} + +AppNavigation.propTypes = {} diff --git a/opendc-web/opendc-web-ui/src/components/AppPage.js b/opendc-web/opendc-web-ui/src/components/AppPage.js new file mode 100644 index 00000000..7cf9cc15 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/AppPage.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import { AppHeader } from './AppHeader' +import { AppNavigation } from './AppNavigation' +import React, { useState } from 'react' +import { Page } from '@patternfly/react-core' + +export function AppPage({ children, breadcrumb, tertiaryNav }) { + return ( + }> + {children} + + ) +} + +AppPage.propTypes = { + breadcrumb: PropTypes.node, + tertiaryNav: PropTypes.node, + children: PropTypes.node, +} diff --git a/opendc-web/opendc-web-ui/src/components/app/map/GrayContainer.js b/opendc-web/opendc-web-ui/src/components/app/map/GrayContainer.js new file mode 100644 index 00000000..4791940f --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/GrayContainer.js @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useDispatch } from 'react-redux' +import { goDownOneInteractionLevel } from '../../../redux/actions/interaction-level' +import GrayLayer from '../../../components/app/map/elements/GrayLayer' + +const GrayContainer = () => { + const dispatch = useDispatch() + const onClick = () => dispatch(goDownOneInteractionLevel()) + return +} + +export default GrayContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/map/LoadingScreen.js b/opendc-web/opendc-web-ui/src/components/app/map/LoadingScreen.js deleted file mode 100644 index ddb94990..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/map/LoadingScreen.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faSpinner } from '@fortawesome/free-solid-svg-icons' - -const LoadingScreen = () => ( -
- - Loading your project... -
-) - -export default LoadingScreen diff --git a/opendc-web/opendc-web-ui/src/components/app/map/MapConstants.js b/opendc-web/opendc-web-ui/src/components/app/map/MapConstants.js index d6ea1f84..45799f70 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/MapConstants.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/MapConstants.js @@ -8,8 +8,8 @@ export const TILE_PLUS_MARGIN_IN_PIXELS = TILE_SIZE_IN_PIXELS / 3 export const OBJECT_SIZE_IN_PIXELS = TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2 export const GRID_LINE_WIDTH_IN_PIXELS = 2 -export const WALL_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 8 -export const OBJECT_BORDER_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 12 +export const WALL_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 16 +export const OBJECT_BORDER_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 16 export const TILE_PLUS_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 10 export const SIDEBAR_WIDTH = 350 diff --git a/opendc-web/opendc-web-ui/src/components/app/map/MapStage.js b/opendc-web/opendc-web-ui/src/components/app/map/MapStage.js new file mode 100644 index 00000000..73accf3f --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/MapStage.js @@ -0,0 +1,66 @@ +import React, { useEffect, useRef, useState } from 'react' +import { HotKeys } from 'react-hotkeys' +import { Stage } from 'react-konva' +import { MAP_MOVE_PIXELS_PER_EVENT } from './MapConstants' +import { Provider, useDispatch, useStore } from 'react-redux' +import useResizeObserver from 'use-resize-observer' +import { mapContainer } from './MapStage.module.scss' +import { useMapPosition } from '../../../data/map' +import { setMapDimensions, setMapPositionWithBoundsCheck, zoomInOnPosition } from '../../../redux/actions/map' +import MapLayer from './layers/MapLayer' +import RoomHoverLayer from './layers/RoomHoverLayer' +import ObjectHoverLayer from './layers/ObjectHoverLayer' + +function MapStage() { + const store = useStore() + const dispatch = useDispatch() + + const stage = useRef(null) + const [pos, setPos] = useState([0, 0]) + const [x, y] = pos + const handlers = { + MOVE_LEFT: () => moveWithDelta(MAP_MOVE_PIXELS_PER_EVENT, 0), + MOVE_RIGHT: () => moveWithDelta(-MAP_MOVE_PIXELS_PER_EVENT, 0), + MOVE_UP: () => moveWithDelta(0, MAP_MOVE_PIXELS_PER_EVENT), + MOVE_DOWN: () => moveWithDelta(0, -MAP_MOVE_PIXELS_PER_EVENT), + } + const mapPosition = useMapPosition() + const { ref, width = 100, height = 100 } = useResizeObserver() + + const moveWithDelta = (deltaX, deltaY) => + dispatch(setMapPositionWithBoundsCheck(mapPosition.x + deltaX, mapPosition.y + deltaY)) + const updateMousePosition = () => { + if (!stage.current) { + return + } + + const mousePos = stage.current.getStage().getPointerPosition() + setPos([mousePos.x, mousePos.y]) + } + const updateScale = ({ evt }) => dispatch(zoomInOnPosition(evt.deltaY < 0, x, y)) + + useEffect(() => { + window['exportCanvasToImage'] = () => { + const download = document.createElement('a') + download.href = stage.current.getStage().toDataURL() + download.download = 'opendc-canvas-export-' + Date.now() + '.png' + download.click() + } + }, [stage]) + + useEffect(() => dispatch(setMapDimensions(width, height)), [width, height]) // eslint-disable-line react-hooks/exhaustive-deps + + return ( + + + + + + + + + + ) +} + +export default MapStage diff --git a/opendc-web/opendc-web-ui/src/components/app/map/MapStage.module.scss b/opendc-web/opendc-web-ui/src/components/app/map/MapStage.module.scss new file mode 100644 index 00000000..d879b4c8 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/MapStage.module.scss @@ -0,0 +1,31 @@ +/*! + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +.mapContainer { + background-color: var(--pf-global--Color--light-200); + position: relative; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; +} diff --git a/opendc-web/opendc-web-ui/src/components/app/map/MapStageComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/MapStageComponent.js deleted file mode 100644 index c3177fe1..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/map/MapStageComponent.js +++ /dev/null @@ -1,97 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useEffect, useRef, useState } from 'react' -import { HotKeys } from 'react-hotkeys' -import { Stage } from 'react-konva' -import MapLayer from '../../../containers/app/map/layers/MapLayer' -import ObjectHoverLayer from '../../../containers/app/map/layers/ObjectHoverLayer' -import RoomHoverLayer from '../../../containers/app/map/layers/RoomHoverLayer' -import { NAVBAR_HEIGHT } from '../../navigation/Navbar' -import { MAP_MOVE_PIXELS_PER_EVENT } from './MapConstants' -import { Provider, useStore } from 'react-redux' - -function MapStageComponent({ - mapDimensions, - mapPosition, - setMapDimensions, - setMapPositionWithBoundsCheck, - zoomInOnPosition, -}) { - const [pos, setPos] = useState([0, 0]) - const stage = useRef(null) - const [x, y] = pos - const handlers = { - MOVE_LEFT: () => moveWithDelta(MAP_MOVE_PIXELS_PER_EVENT, 0), - MOVE_RIGHT: () => moveWithDelta(-MAP_MOVE_PIXELS_PER_EVENT, 0), - MOVE_UP: () => moveWithDelta(0, MAP_MOVE_PIXELS_PER_EVENT), - MOVE_DOWN: () => moveWithDelta(0, -MAP_MOVE_PIXELS_PER_EVENT), - } - - const moveWithDelta = (deltaX, deltaY) => - setMapPositionWithBoundsCheck(mapPosition.x + deltaX, mapPosition.y + deltaY) - const updateMousePosition = () => { - if (!stage.current) { - return - } - - const mousePos = stage.current.getStage().getPointerPosition() - setPos([mousePos.x, mousePos.y]) - } - - const updateDimensions = () => setMapDimensions(window.innerWidth, window.innerHeight - NAVBAR_HEIGHT) - const updateScale = (e) => zoomInOnPosition(e.deltaY < 0, x, y) - - // We explicitly do not specify any dependencies to prevent infinitely dispatching updateDimensions commands - useEffect(() => { - updateDimensions() - - window.addEventListener('resize', updateDimensions) - window.addEventListener('wheel', updateScale) - - window['exportCanvasToImage'] = () => { - const download = document.createElement('a') - download.href = stage.current.getStage().toDataURL() - download.download = 'opendc-canvas-export-' + Date.now() + '.png' - download.click() - } - - return () => { - window.removeEventListener('resize', updateDimensions) - window.removeEventListener('wheel', updateScale) - } - }, []) // eslint-disable-line react-hooks/exhaustive-deps - - const store = useStore() - - return ( - - - - - - - - - - ) -} - -MapStageComponent.propTypes = { - mapDimensions: PropTypes.shape({ - width: PropTypes.number.isRequired, - height: PropTypes.number.isRequired, - }).isRequired, - mapPosition: PropTypes.shape({ - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - }).isRequired, - setMapDimensions: PropTypes.func, - setMapPositionWithBoundsCheck: PropTypes.func, - zoomInOnPosition: PropTypes.func, -} - -export default MapStageComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/map/RackContainer.js b/opendc-web/opendc-web-ui/src/components/app/map/RackContainer.js new file mode 100644 index 00000000..3c75d3a7 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/RackContainer.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useSelector } from 'react-redux' +import RackGroup from '../../../components/app/map/groups/RackGroup' +import { Tile } from '../../../shapes' + +const RackContainer = ({ tile }) => { + const interactionLevel = useSelector((state) => state.interactionLevel) + return +} + +RackContainer.propTypes = { + tile: Tile, +} + +export default RackContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/map/RackEnergyFillContainer.js b/opendc-web/opendc-web-ui/src/components/app/map/RackEnergyFillContainer.js new file mode 100644 index 00000000..838aea5a --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/RackEnergyFillContainer.js @@ -0,0 +1,37 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { useSelector } from 'react-redux' +import RackFillBar from '../../../components/app/map/elements/RackFillBar' + +const RackSpaceFillContainer = (props) => { + const state = useSelector((state) => { + let energyConsumptionTotal = 0 + const rack = state.objects.rack[state.objects.tile[props.tileId].rack] + const machineIds = rack.machines + machineIds.forEach((machineId) => { + if (machineId !== null) { + const machine = state.objects.machine[machineId] + machine.cpus.forEach((id) => (energyConsumptionTotal += state.objects.cpu[id].energyConsumptionW)) + machine.gpus.forEach((id) => (energyConsumptionTotal += state.objects.gpu[id].energyConsumptionW)) + machine.memories.forEach( + (id) => (energyConsumptionTotal += state.objects.memory[id].energyConsumptionW) + ) + machine.storages.forEach( + (id) => (energyConsumptionTotal += state.objects.storage[id].energyConsumptionW) + ) + } + }) + + return { + type: 'energy', + fillFraction: Math.min(1, energyConsumptionTotal / rack.powerCapacityW), + } + }) + return +} + +RackSpaceFillContainer.propTypes = { + tileId: PropTypes.string.isRequired, +} + +export default RackSpaceFillContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/map/RackSpaceFillContainer.js b/opendc-web/opendc-web-ui/src/components/app/map/RackSpaceFillContainer.js new file mode 100644 index 00000000..6791120e --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/RackSpaceFillContainer.js @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import PropTypes from 'prop-types' +import { useSelector } from 'react-redux' +import RackFillBar from '../../../components/app/map/elements/RackFillBar' + +const RackSpaceFillContainer = (props) => { + const state = useSelector((state) => { + const machineIds = state.objects.rack[state.objects.tile[props.tileId].rack].machines + return { + type: 'space', + fillFraction: machineIds.filter((id) => id !== null).length / machineIds.length, + } + }) + return +} + +RackSpaceFillContainer.propTypes = { + tileId: PropTypes.string.isRequired, +} + +export default RackSpaceFillContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/map/RoomContainer.js b/opendc-web/opendc-web-ui/src/components/app/map/RoomContainer.js new file mode 100644 index 00000000..26fbcd7a --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/RoomContainer.js @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { goFromBuildingToRoom } from '../../../redux/actions/interaction-level' +import RoomGroup from '../../../components/app/map/groups/RoomGroup' + +const RoomContainer = (props) => { + const state = useSelector((state) => { + return { + interactionLevel: state.interactionLevel, + currentRoomInConstruction: state.construction.currentRoomInConstruction, + room: state.objects.room[props.roomId], + } + }) + const dispatch = useDispatch() + return dispatch(goFromBuildingToRoom(props.roomId))} /> +} + +RoomContainer.propTypes = { + roomId: PropTypes.string, +} + +export default RoomContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/map/TileContainer.js b/opendc-web/opendc-web-ui/src/components/app/map/TileContainer.js new file mode 100644 index 00000000..bfcbf735 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/TileContainer.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import PropTypes from 'prop-types' +import { useDispatch, useSelector } from 'react-redux' +import { goFromRoomToRack } from '../../../redux/actions/interaction-level' +import TileGroup from '../../../components/app/map/groups/TileGroup' + +const TileContainer = (props) => { + const interactionLevel = useSelector((state) => state.interactionLevel) + const tile = useSelector((state) => state.objects.tile[props.tileId]) + + const dispatch = useDispatch() + const onClick = (tile) => { + if (tile.rack) { + dispatch(goFromRoomToRack(tile._id)) + } + } + return +} + +TileContainer.propTypes = { + tileId: PropTypes.string.isRequired, +} + +export default TileContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/map/TopologyContainer.js b/opendc-web/opendc-web-ui/src/components/app/map/TopologyContainer.js new file mode 100644 index 00000000..78e75d0f --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/TopologyContainer.js @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useSelector } from 'react-redux' +import TopologyGroup from '../../../components/app/map/groups/TopologyGroup' +import { useActiveTopology } from '../../../data/topology' + +const TopologyContainer = () => { + const topology = useActiveTopology() + const interactionLevel = useSelector((state) => state.interactionLevel) + + return +} + +export default TopologyContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/map/WallContainer.js b/opendc-web/opendc-web-ui/src/components/app/map/WallContainer.js new file mode 100644 index 00000000..51dffe4b --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/WallContainer.js @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import PropTypes from 'prop-types' +import { useSelector } from 'react-redux' +import WallGroup from '../../../components/app/map/groups/WallGroup' + +const WallContainer = (props) => { + const tiles = useSelector((state) => + state.objects.room[props.roomId].tiles.map((tileId) => state.objects.tile[tileId]) + ) + return +} + +WallContainer.propTypes = { + roomId: PropTypes.string.isRequired, +} + +export default WallContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/Collapse.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/Collapse.js new file mode 100644 index 00000000..f54b7c84 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/Collapse.js @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import { ChevronLeftIcon } from '@patternfly/react-icons' +import { collapseContainer } from './Collapse.module.scss' +import { Button } from '@patternfly/react-core' + +function Collapse({ onClick }) { + return ( +
+ +
+ ) +} + +Collapse.propTypes = { + onClick: PropTypes.func, +} + +export default Collapse diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/Collapse.module.scss b/opendc-web/opendc-web-ui/src/components/app/map/controls/Collapse.module.scss new file mode 100644 index 00000000..0c1fac94 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/Collapse.module.scss @@ -0,0 +1,55 @@ +/*! + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +.collapseContainer { + position: absolute; + right: var(--pf-global--spacer--xs); + top: 0; + bottom: 10%; + margin: auto 0; + height: 50px; + + button:global(.pf-m-tertiary) { + height: 100%; + padding: 2px; + + margin-right: var(--pf-global--spacer--xs); + margin-top: var(--pf-global--spacer--xs); + background-color: var(--pf-global--BackgroundColor--100); + border: none; + border-radius: var(--pf-global--BorderRadius--sm); + box-shadow: var(--pf-global--BoxShadow--sm); + + &:not(:global(.pf-m-disabled)) { + background-color: var(--pf-global--BackgroundColor--100); + } + + &:after { + display: none; + } + + &:hover { + border: none; + box-shadow: var(--pf-global--BoxShadow--md); + } + } +} diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ExportCanvasComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/ExportCanvasComponent.js deleted file mode 100644 index 9e8cb36a..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ExportCanvasComponent.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faCamera } from '@fortawesome/free-solid-svg-icons' - -const ExportCanvasComponent = () => ( - -) - -export default ExportCanvasComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicator.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicator.js new file mode 100644 index 00000000..11c2f2d3 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicator.js @@ -0,0 +1,16 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { TILE_SIZE_IN_METERS, TILE_SIZE_IN_PIXELS } from '../MapConstants' +import { scaleIndicator } from './ScaleIndicator.module.scss' + +const ScaleIndicator = ({ scale }) => ( +
+ {TILE_SIZE_IN_METERS}m +
+) + +ScaleIndicator.propTypes = { + scale: PropTypes.number.isRequired, +} + +export default ScaleIndicator diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicator.module.scss b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicator.module.scss new file mode 100644 index 00000000..f19e0ff2 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicator.module.scss @@ -0,0 +1,10 @@ +.scaleIndicator { + position: absolute; + right: 10px; + bottom: 10px; + z-index: 50; + + border: solid 2px #212529; + border-top: none; + border-left: none; +} diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.js deleted file mode 100644 index ef633764..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.js +++ /dev/null @@ -1,16 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { TILE_SIZE_IN_METERS, TILE_SIZE_IN_PIXELS } from '../MapConstants' -import { scaleIndicator } from './ScaleIndicatorComponent.module.scss' - -const ScaleIndicatorComponent = ({ scale }) => ( -
- {TILE_SIZE_IN_METERS}m -
-) - -ScaleIndicatorComponent.propTypes = { - scale: PropTypes.number.isRequired, -} - -export default ScaleIndicatorComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.module.scss b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.module.scss deleted file mode 100644 index f19e0ff2..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.module.scss +++ /dev/null @@ -1,10 +0,0 @@ -.scaleIndicator { - position: absolute; - right: 10px; - bottom: 10px; - z-index: 50; - - border: solid 2px #212529; - border-top: none; - border-left: none; -} diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.js deleted file mode 100644 index d2f70953..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react' -import ZoomControlContainer from '../../../../containers/app/map/controls/ZoomControlContainer' -import ExportCanvasComponent from './ExportCanvasComponent' -import { toolPanel } from './ToolPanelComponent.module.scss' - -const ToolPanelComponent = () => ( -
- - -
-) - -export default ToolPanelComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.module.scss b/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.module.scss deleted file mode 100644 index 970b1ce2..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.module.scss +++ /dev/null @@ -1,6 +0,0 @@ -.toolPanel { - position: absolute; - left: 10px; - bottom: 10px; - z-index: 50; -} diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/Toolbar.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/Toolbar.js new file mode 100644 index 00000000..4c60bfb2 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/Toolbar.js @@ -0,0 +1,28 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { control, toolBar } from './Toolbar.module.scss' +import { Button } from '@patternfly/react-core' +import { SearchPlusIcon, SearchMinusIcon } from '@patternfly/react-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCamera } from '@fortawesome/free-solid-svg-icons' + +const Toolbar = ({ onZoom, onExport }) => ( +
+ + + +
+) + +Toolbar.propTypes = { + onZoom: PropTypes.func, + onExport: PropTypes.func, +} + +export default Toolbar diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/Toolbar.module.scss b/opendc-web/opendc-web-ui/src/components/app/map/controls/Toolbar.module.scss new file mode 100644 index 00000000..0d505acc --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/Toolbar.module.scss @@ -0,0 +1,29 @@ +.toolBar { + position: absolute; + bottom: var(--pf-global--spacer--md); + left: var(--pf-global--spacer--xl); +} + +.control { + &:global(.pf-m-tertiary) { + margin-right: var(--pf-global--spacer--xs); + margin-top: var(--pf-global--spacer--xs); + background-color: var(--pf-global--BackgroundColor--100); + border: none; + border-radius: var(--pf-global--BorderRadius--sm); + box-shadow: var(--pf-global--BoxShadow--sm); + + &:not(:global(.pf-m-disabled)) { + background-color: var(--pf-global--BackgroundColor--100); + } + + &:after { + display: none; + } + + &:hover { + border: none; + box-shadow: var(--pf-global--BoxShadow--md); + } + } +} diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ZoomControlComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/ZoomControlComponent.js deleted file mode 100644 index 6c3c6cb7..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ZoomControlComponent.js +++ /dev/null @@ -1,31 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faPlus, faMinus } from '@fortawesome/free-solid-svg-icons' - -const ZoomControlComponent = ({ zoomInOnCenter }) => { - return ( - - - - - ) -} - -ZoomControlComponent.propTypes = { - zoomInOnCenter: PropTypes.func.isRequired, -} - -export default ZoomControlComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/map/groups/RackGroup.js b/opendc-web/opendc-web-ui/src/components/app/map/groups/RackGroup.js index 40e28f01..9c4abc4a 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/groups/RackGroup.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/groups/RackGroup.js @@ -1,10 +1,10 @@ import React from 'react' import { Group } from 'react-konva' -import RackEnergyFillContainer from '../../../../containers/app/map/RackEnergyFillContainer' -import RackSpaceFillContainer from '../../../../containers/app/map/RackSpaceFillContainer' import { Tile } from '../../../../shapes' import { RACK_BACKGROUND_COLOR } from '../../../../util/colors' import TileObject from '../elements/TileObject' +import RackSpaceFillContainer from '../RackSpaceFillContainer' +import RackEnergyFillContainer from '../RackEnergyFillContainer' const RackGroup = ({ tile }) => { return ( diff --git a/opendc-web/opendc-web-ui/src/components/app/map/groups/RoomGroup.js b/opendc-web/opendc-web-ui/src/components/app/map/groups/RoomGroup.js index 42d20ff1..a14f3676 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/groups/RoomGroup.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/groups/RoomGroup.js @@ -1,10 +1,10 @@ import PropTypes from 'prop-types' import React from 'react' import { Group } from 'react-konva' -import GrayContainer from '../../../../containers/app/map/GrayContainer' -import TileContainer from '../../../../containers/app/map/TileContainer' -import WallContainer from '../../../../containers/app/map/WallContainer' import { InteractionLevel, Room } from '../../../../shapes' +import GrayContainer from '../GrayContainer' +import TileContainer from '../TileContainer' +import WallContainer from '../WallContainer' const RoomGroup = ({ room, interactionLevel, currentRoomInConstruction, onClick }) => { if (currentRoomInConstruction === room._id) { diff --git a/opendc-web/opendc-web-ui/src/components/app/map/groups/TileGroup.js b/opendc-web/opendc-web-ui/src/components/app/map/groups/TileGroup.js index ce5e4a6b..cd36c7e5 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/groups/TileGroup.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/groups/TileGroup.js @@ -1,10 +1,10 @@ import PropTypes from 'prop-types' import React from 'react' import { Group } from 'react-konva' -import RackContainer from '../../../../containers/app/map/RackContainer' import { Tile } from '../../../../shapes' import { ROOM_DEFAULT_COLOR, ROOM_IN_CONSTRUCTION_COLOR } from '../../../../util/colors' import RoomTile from '../elements/RoomTile' +import RackContainer from '../RackContainer' const TileGroup = ({ tile, newTile, onClick }) => { let tileObject diff --git a/opendc-web/opendc-web-ui/src/components/app/map/groups/TopologyGroup.js b/opendc-web/opendc-web-ui/src/components/app/map/groups/TopologyGroup.js index d4c6db7d..d3bcb279 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/groups/TopologyGroup.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/groups/TopologyGroup.js @@ -1,8 +1,8 @@ import React from 'react' import { Group } from 'react-konva' -import GrayContainer from '../../../../containers/app/map/GrayContainer' -import RoomContainer from '../../../../containers/app/map/RoomContainer' import { InteractionLevel, Topology } from '../../../../shapes' +import RoomContainer from '../RoomContainer' +import GrayContainer from '../GrayContainer' const TopologyGroup = ({ topology, interactionLevel }) => { if (!topology) { diff --git a/opendc-web/opendc-web-ui/src/components/app/map/layers/MapLayer.js b/opendc-web/opendc-web-ui/src/components/app/map/layers/MapLayer.js new file mode 100644 index 00000000..badb9f68 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/layers/MapLayer.js @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import MapLayerComponent from '../../../../components/app/map/layers/MapLayerComponent' +import { useMapPosition, useMapScale } from '../../../../data/map' + +const MapLayer = (props) => { + const position = useMapPosition() + const scale = useMapScale() + return +} + +export default MapLayer diff --git a/opendc-web/opendc-web-ui/src/components/app/map/layers/MapLayerComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/layers/MapLayerComponent.js index 96e6867c..efe5b4e5 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/layers/MapLayerComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/layers/MapLayerComponent.js @@ -1,9 +1,9 @@ import PropTypes from 'prop-types' import React from 'react' import { Group, Layer } from 'react-konva' -import TopologyContainer from '../../../../containers/app/map/TopologyContainer' import Backdrop from '../elements/Backdrop' import GridGroup from '../groups/GridGroup' +import TopologyContainer from '../TopologyContainer' const MapLayerComponent = ({ mapPosition, mapScale }) => ( diff --git a/opendc-web/opendc-web-ui/src/components/app/map/layers/ObjectHoverLayer.js b/opendc-web/opendc-web-ui/src/components/app/map/layers/ObjectHoverLayer.js new file mode 100644 index 00000000..9a087bd5 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/layers/ObjectHoverLayer.js @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { addRackToTile } from '../../../../redux/actions/topology/room' +import ObjectHoverLayerComponent from '../../../../components/app/map/layers/ObjectHoverLayerComponent' +import { findTileWithPosition } from '../../../../util/tile-calculations' + +const ObjectHoverLayer = (props) => { + const state = useSelector((state) => { + return { + mapPosition: state.map.position, + mapScale: state.map.scale, + isEnabled: () => state.construction.inRackConstructionMode, + isValid: (x, y) => { + if (state.interactionLevel.mode !== 'ROOM') { + return false + } + + const currentRoom = state.objects.room[state.interactionLevel.roomId] + const tiles = currentRoom.tiles.map((tileId) => state.objects.tile[tileId]) + const tile = findTileWithPosition(tiles, x, y) + + return !(tile === null || tile.rack) + }, + } + }) + + const dispatch = useDispatch() + const onClick = (x, y) => dispatch(addRackToTile(x, y)) + return +} + +export default ObjectHoverLayer diff --git a/opendc-web/opendc-web-ui/src/components/app/map/layers/RoomHoverLayer.js b/opendc-web/opendc-web-ui/src/components/app/map/layers/RoomHoverLayer.js new file mode 100644 index 00000000..87240813 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/layers/RoomHoverLayer.js @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { toggleTileAtLocation } from '../../../../redux/actions/topology/building' +import RoomHoverLayerComponent from '../../../../components/app/map/layers/RoomHoverLayerComponent' +import { + deriveValidNextTilePositions, + findPositionInPositions, + findPositionInRooms, +} from '../../../../util/tile-calculations' + +const RoomHoverLayer = (props) => { + const dispatch = useDispatch() + const onClick = (x, y) => dispatch(toggleTileAtLocation(x, y)) + + const state = useSelector((state) => { + return { + mapPosition: state.map.position, + mapScale: state.map.scale, + isEnabled: () => state.construction.currentRoomInConstruction !== '-1', + isValid: (x, y) => { + const newRoom = Object.assign({}, state.objects.room[state.construction.currentRoomInConstruction]) + const oldRooms = Object.keys(state.objects.room) + .map((id) => Object.assign({}, state.objects.room[id])) + .filter( + (room) => + state.objects.topology[state.currentTopologyId].rooms.indexOf(room._id) !== -1 && + room._id !== state.construction.currentRoomInConstruction + ) + + ;[...oldRooms, newRoom].forEach((room) => { + room.tiles = room.tiles.map((tileId) => state.objects.tile[tileId]) + }) + if (newRoom.tiles.length === 0) { + return findPositionInRooms(oldRooms, x, y) === -1 + } + + const validNextPositions = deriveValidNextTilePositions(oldRooms, newRoom.tiles) + return findPositionInPositions(validNextPositions, x, y) !== -1 + }, + } + }) + return +} + +export default RoomHoverLayer diff --git a/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultInfo.js b/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultInfo.js new file mode 100644 index 00000000..09348e60 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultInfo.js @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import { Tooltip } from '@patternfly/react-core' +import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons' +import { METRIC_DESCRIPTIONS } from '../../../util/available-metrics' + +function PortfolioResultInfo({ metric }) { + return ( + {METRIC_DESCRIPTIONS[metric]}
}> + + + ) +} + +PortfolioResultInfo.propTypes = { + metric: PropTypes.string.isRequired, +} + +export default PortfolioResultInfo 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 ( + + + + + Loading Results + + + + ) + } else if (status === 'error') { + return ( + + + + + Unable to connect + + + There was an error retrieving data. Check your connection and try again. + + + + ) + } else if (scenarios.length === 0) { + return ( + + + + + No results + + + No results are currently available for this portfolio. Run a scenario to obtain simulation + results. + + + + + ) + } + + 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 ( + + {AVAILABLE_METRICS.map((metric) => ( + + + + + + + {METRIC_NAMES[metric]} + + + + + + approx(tick)} + label={{ value: METRIC_UNITS[metric], position: 'bottom', offset: 0 }} + type="number" + /> + + + + + + + + + + + ))} + + ) +} + +PortfolioResults.propTypes = { + portfolioId: PropTypes.string, +} + +export default PortfolioResults diff --git a/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultsComponent.js b/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultsComponent.js deleted file mode 100644 index 983a5c1d..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultsComponent.js +++ /dev/null @@ -1,93 +0,0 @@ -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_SHORT, METRIC_UNITS } from '../../../util/available-metrics' -import { mean, std } from 'mathjs' -import { Portfolio, Scenario } from '../../../shapes' -import approx from 'approximate-number' - -const PortfolioResultsComponent = ({ portfolio, scenarios }) => { - if (!portfolio) { - return
Loading...
- } - - const nonFinishedScenarios = scenarios.filter((s) => s.simulation.state !== 'FINISHED') - - if (nonFinishedScenarios.length > 0) { - if (nonFinishedScenarios.every((s) => s.simulation.state === 'QUEUED' || s.simulation.state === 'RUNNING')) { - return ( -
-

Simulation running...

-

{nonFinishedScenarios.length} of the scenarios are still being simulated

-
- ) - } - if (nonFinishedScenarios.some((s) => s.simulation.state === 'FAILED')) { - return ( -
-

Simulation failed.

-

- Try again by creating a new scenario. Please contact the OpenDC team for support, if issues - persist. -

-
- ) - } - } - - const dataPerMetric = {} - - AVAILABLE_METRICS.forEach((metric) => { - dataPerMetric[metric] = scenarios.map((scenario) => ({ - name: scenario.name, - value: mean(scenario.results[metric]), - errorX: std(scenario.results[metric]), - })) - }) - - return ( -
-

Portfolio: {portfolio.name}

-

Repeats per Scenario: {portfolio.targets.repeatsPerScenario}

-
- {AVAILABLE_METRICS.map((metric) => ( -
-

{METRIC_NAMES_SHORT[metric]}

- - - - approx(tick)} - label={{ value: METRIC_UNITS[metric], position: 'bottom', offset: 0 }} - type="number" - /> - - - - - - - -
- ))} -
-
- ) -} - -PortfolioResultsComponent.propTypes = { - portfolio: Portfolio, - scenarios: PropTypes.arrayOf(Scenario), -} - -export default PortfolioResultsComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.js deleted file mode 100644 index 56fa799f..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.js +++ /dev/null @@ -1,48 +0,0 @@ -import PropTypes from 'prop-types' -import classNames from 'classnames' -import React, { useState } from 'react' -import { collapseButton, collapseButtonRight, sidebar, sidebarRight } from './Sidebar.module.scss' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faAngleLeft, faAngleRight } from '@fortawesome/free-solid-svg-icons' - -function Sidebar({ isRight, collapsible = true, children }) { - const [isCollapsed, setCollapsed] = useState(false) - - const button = ( -
setCollapsed(!isCollapsed)} - > - {(isCollapsed && isRight) || (!isCollapsed && !isRight) ? ( - - ) : ( - - )} -
- ) - - if (isCollapsed) { - return button - } - return ( -
e.stopPropagation()} - > - {children} - {collapsible && button} -
- ) -} - -Sidebar.propTypes = { - isRight: PropTypes.bool.isRequired, - collapsible: PropTypes.bool, - children: PropTypes.node, -} - -export default Sidebar diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.module.scss b/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.module.scss deleted file mode 100644 index 19c6a97f..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.module.scss +++ /dev/null @@ -1,57 +0,0 @@ -@import 'src/style/_variables.scss'; -@import 'src/style/_mixins.scss'; - -.collapseButton { - position: absolute; - left: 5px; - top: 5px; - padding: 5px 7px; - - background: white; - border: solid 1px $gray-semi-light; - z-index: 99; - - @include clickable; - border-radius: 5px; - transition: background 200ms; - - &.collapseButtonRight { - left: auto; - right: 5px; - top: 5px; - } - - &:hover { - background: #eeeeee; - } -} - -.sidebar { - position: absolute; - top: 0; - left: 0; - width: $side-bar-width; - - z-index: 100; - background: white; - - border-right: $gray-semi-dark 1px solid; - - .collapseButton { - left: auto; - right: -25px; - } -} - -.sidebarRight { - left: auto; - right: 0; - - border-left: $gray-semi-dark 1px solid; - border-right: none; - - .collapseButtonRight { - left: -25px; - right: auto; - } -} diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js deleted file mode 100644 index d61ff24e..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js +++ /dev/null @@ -1,71 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Portfolio } from '../../../../shapes' -import Link from 'next/link' -import ScenarioListContainer from '../../../../containers/app/sidebars/project/ScenarioListContainer' -import { Button, Col, Row } from 'reactstrap' -import classNames from 'classnames' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faPlus, faPlay, faTrash } from '@fortawesome/free-solid-svg-icons' - -function PortfolioListComponent({ - portfolios, - currentProjectId, - currentPortfolioId, - onNewPortfolio, - onChoosePortfolio, - onDeletePortfolio, -}) { - return ( -
-

- Portfolios - -

- - {portfolios.map((portfolio) => ( -
- - - {portfolio.name} - - - - - - - - - -
- ))} -
- ) -} - -PortfolioListComponent.propTypes = { - portfolios: PropTypes.arrayOf(Portfolio), - currentProjectId: PropTypes.string, - currentPortfolioId: PropTypes.string, - onNewPortfolio: PropTypes.func.isRequired, - onChoosePortfolio: PropTypes.func.isRequired, - onDeletePortfolio: PropTypes.func.isRequired, -} - -export default PortfolioListComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ProjectSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ProjectSidebarComponent.js deleted file mode 100644 index 10d22e5b..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ProjectSidebarComponent.js +++ /dev/null @@ -1,21 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import Sidebar from '../Sidebar' -import TopologyListContainer from '../../../../containers/app/sidebars/project/TopologyListContainer' -import PortfolioListContainer from '../../../../containers/app/sidebars/project/PortfolioListContainer' -import { Container } from 'reactstrap' - -const ProjectSidebarComponent = ({ collapsible }) => ( - - - - - - -) - -ProjectSidebarComponent.propTypes = { - collapsible: PropTypes.bool, -} - -export default ProjectSidebarComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ScenarioListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ScenarioListComponent.js deleted file mode 100644 index e81d2b78..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ScenarioListComponent.js +++ /dev/null @@ -1,45 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Scenario } from '../../../../shapes' -import { Button, Col, Row } from 'reactstrap' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faPlus, faTrash } from '@fortawesome/free-solid-svg-icons' - -function ScenarioListComponent({ scenarios, portfolioId, onNewScenario, onDeleteScenario }) { - return ( - <> - {scenarios.map((scenario, idx) => ( - - - {scenario.name} - - - - - - ))} -
- -
- - ) -} - -ScenarioListComponent.propTypes = { - scenarios: PropTypes.arrayOf(Scenario), - portfolioId: PropTypes.string, - onNewScenario: PropTypes.func.isRequired, - onDeleteScenario: PropTypes.func.isRequired, -} - -export default ScenarioListComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/TopologyListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/TopologyListComponent.js deleted file mode 100644 index ac58669b..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/TopologyListComponent.js +++ /dev/null @@ -1,56 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Topology } from '../../../../shapes' -import { Button, Col, Row } from 'reactstrap' -import classNames from 'classnames' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faPlus, faPlay, faTrash } from '@fortawesome/free-solid-svg-icons' - -function TopologyListComponent({ topologies, currentTopologyId, onChooseTopology, onNewTopology, onDeleteTopology }) { - return ( -
-

- Topologies - -

- - {topologies.map((topology, idx) => ( - - - {topology.name} - - - - - - - ))} -
- ) -} - -TopologyListComponent.propTypes = { - topologies: PropTypes.arrayOf(Topology), - currentTopologyId: PropTypes.string, - onChooseTopology: PropTypes.func.isRequired, - onNewTopology: PropTypes.func.isRequired, - onDeleteTopology: PropTypes.func.isRequired, -} - -export default TopologyListComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js index b8c88003..ececd07b 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js @@ -1,16 +1,65 @@ import PropTypes from 'prop-types' -import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faPencilAlt } from '@fortawesome/free-solid-svg-icons' - -const NameComponent = ({ name, onEdit }) => ( -

- {name} - -

-) +import React, { useRef, useState } from 'react' +import { Button, TextInput } from '@patternfly/react-core' +import { PencilAltIcon, CheckIcon, TimesIcon } from '@patternfly/react-icons' + +function NameComponent({ name, onEdit }) { + const [isEditing, setEditing] = useState(false) + const nameInput = useRef(null) + + const onCancel = () => { + nameInput.current.value = name + setEditing(false) + } + + const onSubmit = (event) => { + if (event) { + event.preventDefault() + } + + const name = nameInput.current.value + if (name) { + onEdit(name) + } + + setEditing(false) + } + + return ( +
+
+
+ {name} +
+
+ +
+
+
+
+ +
+
+
+ +
+
+ +
+
+
+
+ ) +} NameComponent.propTypes = { name: PropTypes.string, diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebar.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebar.js new file mode 100644 index 00000000..c4a880b1 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebar.js @@ -0,0 +1,83 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { InteractionLevel } from '../../../../shapes' +import BuildingSidebarComponent from './building/BuildingSidebarComponent' +import { + Button, + DrawerActions, + DrawerCloseButton, + DrawerHead, + DrawerPanelBody, + DrawerPanelContent, + Flex, + Title, +} from '@patternfly/react-core' +import { AngleLeftIcon } from '@patternfly/react-icons' +import { useDispatch } from 'react-redux' +import { goDownOneInteractionLevel } from '../../../../redux/actions/interaction-level' +import { backButton } from './TopologySidebar.module.scss' +import RoomSidebarContainer from './room/RoomSidebarContainer' +import RackSidebarContainer from './rack/RackSidebarContainer' +import MachineSidebarContainer from './machine/MachineSidebarContainer' + +const name = { + BUILDING: 'Building', + ROOM: 'Room', + RACK: 'Rack', + MACHINE: 'Machine', +} + +const TopologySidebar = ({ interactionLevel, onClose }) => { + let sidebarContent + + switch (interactionLevel.mode) { + case 'BUILDING': + sidebarContent = + break + case 'ROOM': + sidebarContent = + break + case 'RACK': + sidebarContent = + break + case 'MACHINE': + sidebarContent = + break + default: + sidebarContent = 'Missing Content' + } + + const dispatch = useDispatch() + const onClick = () => dispatch(goDownOneInteractionLevel()) + + return ( + + + + + + {name[interactionLevel.mode]} + + + + + + + {sidebarContent} + + ) +} + +TopologySidebar.propTypes = { + interactionLevel: InteractionLevel, + onClose: PropTypes.func, +} + +export default TopologySidebar diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebar.module.scss b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebar.module.scss new file mode 100644 index 00000000..45dc98da --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebar.module.scss @@ -0,0 +1,37 @@ +/*! + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +.backButton { + &:global(.pf-c-button) { + align-self: center; + --pf-c-button--after--BorderColor: var(--pf-global--BorderColor--light-100); + color: var(--pf-global--Color--400); + + --pf-c-button--PaddingRight: var(--pf-global--spacer--sm); + --pf-c-button--PaddingLeft: var(--pf-global--spacer--sm); + + &:hover, + &:focus { + --pf-c-button--after--BorderColor: var(--pf-global--BorderColor--100); + } + } +} diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebarComponent.js deleted file mode 100644 index 450df6cd..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebarComponent.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' -import BuildingSidebarContainer from '../../../../containers/app/sidebars/topology/building/BuildingSidebarContainer' -import MachineSidebarContainer from '../../../../containers/app/sidebars/topology/machine/MachineSidebarContainer' -import RackSidebarContainer from '../../../../containers/app/sidebars/topology/rack/RackSidebarContainer' -import RoomSidebarContainer from '../../../../containers/app/sidebars/topology/room/RoomSidebarContainer' -import Sidebar from '../Sidebar' -import { InteractionLevel } from '../../../../shapes' - -const TopologySidebarComponent = ({ interactionLevel }) => { - let sidebarContent - - switch (interactionLevel.mode) { - case 'BUILDING': - sidebarContent = - break - case 'ROOM': - sidebarContent = - break - case 'RACK': - sidebarContent = - break - case 'MACHINE': - sidebarContent = - break - default: - sidebarContent = 'Missing Content' - } - - return {sidebarContent} -} - -TopologySidebarComponent.propTypes = { - interactionLevel: InteractionLevel, -} - -export default TopologySidebarComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js index eea62f84..6c2556d3 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js @@ -1,13 +1,8 @@ import React from 'react' -import NewRoomConstructionContainer from '../../../../../containers/app/sidebars/topology/building/NewRoomConstructionContainer' +import NewRoomConstructionContainer from './NewRoomConstructionContainer' const BuildingSidebarComponent = () => { - return ( -
-

Building

- -
- ) + return } export default BuildingSidebarComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js index e8c81735..656b2515 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js @@ -1,29 +1,38 @@ import PropTypes from 'prop-types' import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faPlus, faCheck, faTimes } from '@fortawesome/free-solid-svg-icons' -import { Button } from 'reactstrap' +import { Button, Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem } from '@patternfly/react-core' +import PlusIcon from '@patternfly/react-icons/dist/js/icons/plus-icon' +import CheckIcon from '@patternfly/react-icons/dist/js/icons/check-icon' const NewRoomConstructionComponent = ({ onStart, onFinish, onCancel, currentRoomInConstruction }) => { if (currentRoomInConstruction === '-1') { return ( -
- +
+ ) } return ( -
- - -
+ + + + + + + + + + + + ) } diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionContainer.js new file mode 100644 index 00000000..0836263c --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionContainer.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { + cancelNewRoomConstruction, + finishNewRoomConstruction, + startNewRoomConstruction, +} from '../../../../../redux/actions/topology/building' +import NewRoomConstructionComponent from './NewRoomConstructionComponent' + +const NewRoomConstructionButton = (props) => { + const currentRoomInConstruction = useSelector((state) => state.construction.currentRoomInConstruction) + + const dispatch = useDispatch() + const actions = { + onStart: () => dispatch(startNewRoomConstruction()), + onFinish: () => dispatch(finishNewRoomConstruction()), + onCancel: () => dispatch(cancelNewRoomConstruction()), + } + return ( + + ) +} + +export default NewRoomConstructionButton diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js deleted file mode 100644 index 829bf265..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js +++ /dev/null @@ -1,17 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faAngleLeft } from '@fortawesome/free-solid-svg-icons' - -const BackToRackComponent = ({ onClick }) => ( -
- - Back to rack -
-) - -BackToRackComponent.propTypes = { - onClick: PropTypes.func, -} - -export default BackToRackComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachine.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachine.js new file mode 100644 index 00000000..a7bf3719 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachine.js @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' +import { deleteMachine } from '../../../../../redux/actions/topology/machine' +import { Button } from '@patternfly/react-core' +import TrashIcon from '@patternfly/react-icons/dist/js/icons/trash-icon' +import ConfirmationModal from '../../../../modals/ConfirmationModal' + +const DeleteMachine = () => { + const dispatch = useDispatch() + const [isVisible, setVisible] = useState(false) + const callback = (isConfirmed) => { + if (isConfirmed) { + dispatch(deleteMachine()) + } + setVisible(false) + } + return ( + <> + + + + ) +} + +export default DeleteMachine diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js index 88a99e0f..d3d4a8cf 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js @@ -1,17 +1,38 @@ import PropTypes from 'prop-types' import React from 'react' -import BackToRackContainer from '../../../../../containers/app/sidebars/topology/machine/BackToRackContainer' -import DeleteMachineContainer from '../../../../../containers/app/sidebars/topology/machine/DeleteMachineContainer' -import MachineNameContainer from '../../../../../containers/app/sidebars/topology/machine/MachineNameContainer' -import UnitTabsContainer from '../../../../../containers/app/sidebars/topology/machine/UnitTabsContainer' +import UnitTabsComponent from './UnitTabsComponent' +import DeleteMachine from './DeleteMachine' +import { + TextContent, + TextList, + TextListItem, + TextListItemVariants, + TextListVariants, + Title, +} from '@patternfly/react-core' +import { useSelector } from 'react-redux' const MachineSidebarComponent = ({ machineId }) => { + const machine = useSelector((state) => state.objects.machine[machineId]) return ( -
- - - - +
+ + Details + + Name + + Machine at position {machine.position} + + + + Actions + + + Units + +
+ +
) } diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarContainer.js new file mode 100644 index 00000000..94d9f2c3 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarContainer.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useSelector } from 'react-redux' +import MachineSidebarComponent from './MachineSidebarComponent' + +const MachineSidebarContainer = (props) => { + const machineId = useSelector( + (state) => + state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rack].machines[ + state.interactionLevel.position - 1 + ] + ) + return +} + +export default MachineSidebarContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js index 532add37..88591208 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js @@ -1,28 +1,36 @@ import PropTypes from 'prop-types' -import React, { useRef } from 'react' -import { Button, Form, FormGroup, Input } from 'reactstrap' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faPlus } from '@fortawesome/free-solid-svg-icons' +import React, { useState } from 'react' +import { Button, InputGroup, Select, SelectOption, SelectVariant } from '@patternfly/react-core' +import PlusIcon from '@patternfly/react-icons/dist/js/icons/plus-icon' function UnitAddComponent({ units, onAdd }) { - const unitSelect = useRef(null) + const [isOpen, setOpen] = useState(false) + const [selected, setSelected] = useState(null) return ( -
- - - {units.map((unit) => ( - - ))} - - - -
+ + + + ) } diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddContainer.js new file mode 100644 index 00000000..8a6680e6 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddContainer.js @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { addUnit } from '../../../../../redux/actions/topology/machine' +import UnitAddComponent from './UnitAddComponent' + +const UnitAddContainer = ({ unitType }) => { + const units = useSelector((state) => Object.values(state.objects[unitType])) + const dispatch = useDispatch() + + const onAdd = (id) => dispatch(addUnit(unitType, id)) + + return +} + +UnitAddContainer.propTypes = { + unitType: PropTypes.string.isRequired, +} + +export default UnitAddContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js deleted file mode 100644 index 46c639bd..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js +++ /dev/null @@ -1,67 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { UncontrolledPopover, PopoverHeader, PopoverBody, Button } from 'reactstrap' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faTrash, faInfoCircle } from '@fortawesome/free-solid-svg-icons' -import { ProcessingUnit, StorageUnit } from '../../../../../shapes' - -function UnitComponent({ index, unitType, unit, onDelete }) { - let unitInfo - if (unitType === 'cpu' || unitType === 'gpu') { - unitInfo = ( - <> - Clockrate: - {unit.clockRateMhz} -
- Num. Cores: - {unit.numberOfCores} -
- Energy Cons.: - {unit.energyConsumptionW} W -
- - ) - } else if (unitType === 'memory' || unitType === 'storage') { - unitInfo = ( - <> - Speed: - {unit.speedMbPerS} Mb/s -
- Size: - {unit.sizeMb} MB -
- Energy Cons.: - {unit.energyConsumptionW} W -
- - ) - } - - return ( -
  • - {unit.name} - - - - Unit Information - {unitInfo} - - - - -
  • - ) -} - -UnitComponent.propTypes = { - index: PropTypes.number, - unitType: PropTypes.string, - unit: PropTypes.oneOfType([ProcessingUnit, StorageUnit]), - onDelete: PropTypes.func, -} - -export default UnitComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListComponent.js index 54c1a6cc..9c3c08fd 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListComponent.js @@ -1,29 +1,107 @@ import PropTypes from 'prop-types' import React from 'react' import { ProcessingUnit, StorageUnit } from '../../../../../shapes' -import UnitComponent from './UnitComponent' +import { + Button, + DataList, + DataListAction, + DataListCell, + DataListItem, + DataListItemCells, + DataListItemRow, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, + EmptyState, + EmptyStateBody, + EmptyStateIcon, + Popover, + Title, +} from '@patternfly/react-core' +import { CubesIcon, InfoIcon, TrashIcon } from '@patternfly/react-icons' -const UnitListComponent = ({ unitType, units, onDelete }) => ( -
      - {units.length !== 0 ? ( - units.map((unit, index) => ( - onDelete(unit, unitType)} - index={index} - key={index} - /> - )) - ) : ( -
      - - No units... Add some with the menu above! - -
      - )} -
    -) +const UnitInfo = ({ unit, unitType }) => { + if (unitType === 'cpu' || unitType === 'gpu') { + return ( + + + Clock Frequency + {unit.clockRateMhz} MHz + + + Number of Cores + {unit.numberOfCores} + + + Energy Consumption + {unit.energyConsumptionW} W + + + ) + } + + return ( + + + Speed + {unit.speedMbPerS} Mb/s + + + Capacity + {unit.sizeMb} MB + + + Energy Consumption + {unit.energyConsumptionW} W + + + ) +} + +UnitInfo.propTypes = { + unitType: PropTypes.string.isRequired, + unit: PropTypes.oneOfType([ProcessingUnit, StorageUnit]).isRequired, +} + +const UnitListComponent = ({ unitType, units, onDelete }) => { + if (units.length === 0) { + return ( + + + + No units found + + You have not configured any units yet. Add some with the menu above! + + ) + } + + return ( + + {units.map((unit, index) => ( + + + {unit.name}]} /> + + } + > + + + + + + + ))} + + ) +} UnitListComponent.propTypes = { unitType: PropTypes.string.isRequired, diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListContainer.js new file mode 100644 index 00000000..2d994f97 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListContainer.js @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import UnitListComponent from './UnitListComponent' +import { deleteUnit } from '../../../../../redux/actions/topology/machine' + +const unitMapping = { + cpu: 'cpus', + gpu: 'gpus', + memory: 'memories', + storage: 'storages', +} + +const UnitListContainer = ({ unitType, ...props }) => { + const dispatch = useDispatch() + const units = useSelector((state) => { + const machine = + state.objects.machine[ + state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rack].machines[ + state.interactionLevel.position - 1 + ] + ] + return machine[unitMapping[unitType]].map((id) => state.objects[unitType][id]) + }) + const onDelete = (unit, unitType) => dispatch(deleteUnit(unitType, unit._id)) + + return +} + +UnitListContainer.propTypes = { + unitType: PropTypes.string.isRequired, +} + +export default UnitListContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js index ebb5f479..723ed2e2 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js @@ -1,85 +1,30 @@ import React, { useState } from 'react' -import { Nav, NavItem, NavLink, Row, TabContent, TabPane } from 'reactstrap' -import UnitAddContainer from '../../../../../containers/app/sidebars/topology/machine/UnitAddContainer' -import UnitListContainer from '../../../../../containers/app/sidebars/topology/machine/UnitListContainer' +import { Tab, Tabs, TabTitleText } from '@patternfly/react-core' +import UnitAddContainer from './UnitAddContainer' +import UnitListContainer from './UnitListContainer' const UnitTabsComponent = () => { const [activeTab, setActiveTab] = useState('cpu-units') - const toggle = (tab) => { - if (activeTab !== tab) setActiveTab(tab) - } return ( -
    - - - -
    - - -
    -
    - -
    - - -
    -
    - -
    - - -
    -
    - -
    - - -
    -
    -
    -
    + setActiveTab(tab)}> + CPU}> + + + + GPU}> + + + + Memory}> + + + + Storage}> + + + + ) } diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js index a330c302..c8543134 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js @@ -1,12 +1,10 @@ import PropTypes from 'prop-types' import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faSave } from '@fortawesome/free-solid-svg-icons' -import { Button } from 'reactstrap' +import { SaveIcon } from '@patternfly/react-icons' +import { Button } from '@patternfly/react-core' const AddPrefabComponent = ({ onClick }) => ( - ) diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabContainer.js new file mode 100644 index 00000000..d3d9aaf5 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabContainer.js @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useDispatch } from 'react-redux' +import { addPrefab } from '../../../../../redux/actions/prefabs' +import AddPrefabComponent from './AddPrefabComponent' + +const AddPrefabContainer = (props) => { + const dispatch = useDispatch() + return dispatch(addPrefab('name'))} /> +} + +export default AddPrefabContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js deleted file mode 100644 index e0eb5979..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js +++ /dev/null @@ -1,18 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faAngleLeft } from '@fortawesome/free-solid-svg-icons' -import { Button } from 'reactstrap' - -const BackToRoomComponent = ({ onClick }) => ( - -) - -BackToRoomComponent.propTypes = { - onClick: PropTypes.func, -} - -export default BackToRoomComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackContainer.js new file mode 100644 index 00000000..47959f03 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackContainer.js @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' +import ConfirmationModal from '../../../../../components/modals/ConfirmationModal' +import { deleteRack } from '../../../../../redux/actions/topology/rack' +import TrashIcon from '@patternfly/react-icons/dist/js/icons/trash-icon' +import { Button } from '@patternfly/react-core' + +const DeleteRackContainer = () => { + const dispatch = useDispatch() + const [isVisible, setVisible] = useState(false) + const callback = (isConfirmed) => { + if (isConfirmed) { + dispatch(deleteRack()) + } + setVisible(false) + } + return ( + <> + + + + ) +} + +export default DeleteRackContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js deleted file mode 100644 index 63b319e0..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js +++ /dev/null @@ -1,24 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faPlus } from '@fortawesome/free-solid-svg-icons' -import { ListGroupItem, Badge, Button } from 'reactstrap' - -const EmptySlotComponent = ({ position, onAdd }) => ( - - - {position} - - - -) - -EmptySlotComponent.propTypes = { - position: PropTypes.number, - onAdd: PropTypes.func, -} - -export default EmptySlotComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js index b71918da..1617b3bf 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js @@ -2,18 +2,16 @@ import PropTypes from 'prop-types' import React from 'react' import Image from 'next/image' import { Machine } from '../../../../../shapes' -import { Badge, ListGroupItem } from 'reactstrap' +import { Flex, Label } from '@patternfly/react-core' const UnitIcon = ({ id, type }) => ( -
    - {'Machine -
    + {'Machine ) UnitIcon.propTypes = { @@ -21,34 +19,27 @@ UnitIcon.propTypes = { type: PropTypes.string, } -const MachineComponent = ({ position, machine, onClick }) => { +const MachineComponent = ({ machine, onClick }) => { const hasNoUnits = machine.cpus.length + machine.gpus.length + machine.memories.length + machine.storages.length === 0 return ( - - - {position} - -
    - {machine.cpus.length > 0 ? : undefined} - {machine.gpus.length > 0 ? : undefined} - {machine.memories.length > 0 ? : undefined} - {machine.storages.length > 0 ? : undefined} - {hasNoUnits ? Machine with no units : undefined} -
    -
    + onClick()}> + {machine.cpus.length > 0 ? : undefined} + {machine.gpus.length > 0 ? : undefined} + {machine.memories.length > 0 ? : undefined} + {machine.storages.length > 0 ? : undefined} + {hasNoUnits ? ( + + ) : undefined} + ) } MachineComponent.propTypes = { - machine: Machine, - position: PropTypes.number, + machine: Machine.isRequired, onClick: PropTypes.func, } diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js index e024a417..27834cf4 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js @@ -1,21 +1,66 @@ import PropTypes from 'prop-types' import React from 'react' -import { machineList } from './MachineListComponent.module.scss' import MachineComponent from './MachineComponent' import { Machine } from '../../../../../shapes' -import EmptySlotComponent from './EmptySlotComponent' +import { + Badge, + Button, + DataList, + DataListAction, + DataListCell, + DataListItem, + DataListItemCells, + DataListItemRow, +} from '@patternfly/react-core' +import { AngleRightIcon, PlusIcon } from '@patternfly/react-icons' -const MachineListComponent = ({ machines = [], onSelect, onAdd }) => { +function MachineListComponent({ machines = [], onSelect, onAdd }) { return ( -
      - {machines.map((machine, index) => { - if (machine === null) { - return onAdd(index + 1)} /> - } else { - return onSelect(index + 1)} machine={machine} /> - } - })} -
    + + {machines.map((machine, index) => + machine ? ( + onSelect(index + 1)}> + + + {machines.length - index}U + , + + onSelect(index + 1)} machine={machine} /> + , + ]} + /> + + + + + + ) : ( + + + + {machines.length - index}U + , + + Empty Slot + , + ]} + /> + + + + + + ) + )} + ) } diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.module.scss b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.module.scss deleted file mode 100644 index f075aac9..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.module.scss +++ /dev/null @@ -1,3 +0,0 @@ -.machineList li { - min-height: 64px; -} diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListContainer.js new file mode 100644 index 00000000..54e6db0a --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListContainer.js @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import React, { useMemo } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import MachineListComponent from './MachineListComponent' +import { goFromRackToMachine } from '../../../../../redux/actions/interaction-level' +import { addMachine } from '../../../../../redux/actions/topology/rack' + +function MachineListContainer({ tileId, ...props }) { + const rack = useSelector((state) => state.objects.rack[state.objects.tile[tileId].rack]) + const machines = useSelector((state) => rack.machines.map((id) => state.objects.machine[id])) + const machinesNull = useMemo(() => { + const res = Array(rack.capacity).fill(null) + for (const machine of machines) { + res[machine.position - 1] = machine + } + return res + }, [rack, machines]) + const dispatch = useDispatch() + + return ( + dispatch(addMachine(index))} + onSelect={(index) => dispatch(goFromRackToMachine(index))} + /> + ) +} + +MachineListContainer.propTypes = { + tileId: PropTypes.string.isRequired, +} + +export default MachineListContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameContainer.js new file mode 100644 index 00000000..11529b55 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameContainer.js @@ -0,0 +1,22 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import NameComponent from '../NameComponent' +import { editRackName } from '../../../../../redux/actions/topology/rack' + +const RackNameContainer = ({ tileId }) => { + const rackName = useSelector((state) => state.objects.rack[state.objects.tile[tileId].rack].name) + const dispatch = useDispatch() + const callback = (name) => { + if (name) { + dispatch(editRackName(name)) + } + } + return +} + +RackNameContainer.propTypes = { + tileId: PropTypes.string.isRequired, +} + +export default RackNameContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js index 74313bf7..dd5117f7 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js @@ -1,25 +1,58 @@ +import PropTypes from 'prop-types' import React from 'react' -import BackToRoomContainer from '../../../../../containers/app/sidebars/topology/rack/BackToRoomContainer' -import DeleteRackContainer from '../../../../../containers/app/sidebars/topology/rack/DeleteRackContainer' -import MachineListContainer from '../../../../../containers/app/sidebars/topology/rack/MachineListContainer' -import RackNameContainer from '../../../../../containers/app/sidebars/topology/rack/RackNameContainer' -import { sidebarContainer, sidebarHeaderContainer, machineListContainer } from './RackSidebarComponent.module.scss' -import AddPrefabContainer from '../../../../../containers/app/sidebars/topology/rack/AddPrefabContainer' +import { machineListContainer, sidebarContainer } from './RackSidebarComponent.module.scss' +import RackNameContainer from './RackNameContainer' +import AddPrefabContainer from './AddPrefabContainer' +import DeleteRackContainer from './DeleteRackContainer' +import MachineListContainer from './MachineListContainer' +import { + Skeleton, + TextContent, + TextList, + TextListItem, + TextListItemVariants, + TextListVariants, + Title, +} from '@patternfly/react-core' +import { useSelector } from 'react-redux' + +function RackSidebarComponent({ tileId }) { + const rack = useSelector((state) => state.objects.rack[state.objects.tile[tileId].rack]) -const RackSidebarComponent = () => { return ( -
    -
    - - +
    + + Details + + + Name + + + + + Capacity + + {rack?.capacity ?? } + + + Actions -
    -
    - + + Slots + +
    +
    ) } +RackSidebarComponent.propTypes = { + tileId: PropTypes.string.isRequired, +} + export default RackSidebarComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.module.scss b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.module.scss index 8ce3836a..6f258aec 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.module.scss +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.module.scss @@ -2,13 +2,11 @@ display: flex; height: 100%; max-height: 100%; -} - -.sidebarHeaderContainer { - flex: 0; + flex-direction: column; } .machineListContainer { flex: 1; overflow-y: scroll; + margin-top: 10px; } diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarContainer.js new file mode 100644 index 00000000..2b31413d --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarContainer.js @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useSelector } from 'react-redux' +import RackSidebarComponent from './RackSidebarComponent' + +const RackSidebarContainer = (props) => { + const tileId = useSelector((state) => state.interactionLevel.tileId) + return +} + +export default RackSidebarContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js deleted file mode 100644 index 043cc713..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js +++ /dev/null @@ -1,17 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faAngleLeft } from '@fortawesome/free-solid-svg-icons' - -const BackToBuildingComponent = ({ onClick }) => ( -
    - - Back to building -
    -) - -BackToBuildingComponent.propTypes = { - onClick: PropTypes.func, -} - -export default BackToBuildingComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js deleted file mode 100644 index d81bad0f..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js +++ /dev/null @@ -1,18 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faTrash } from '@fortawesome/free-solid-svg-icons' -import { Button } from 'reactstrap' - -const DeleteRoomComponent = ({ onClick }) => ( - -) - -DeleteRoomComponent.propTypes = { - onClick: PropTypes.func, -} - -export default DeleteRoomComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomContainer.js new file mode 100644 index 00000000..284c4d53 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomContainer.js @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' +import ConfirmationModal from '../../../../../components/modals/ConfirmationModal' +import { deleteRoom } from '../../../../../redux/actions/topology/room' +import TrashIcon from '@patternfly/react-icons/dist/js/icons/trash-icon' +import { Button } from '@patternfly/react-core' + +const DeleteRoomContainer = () => { + const dispatch = useDispatch() + const [isVisible, setVisible] = useState(false) + const callback = (isConfirmed) => { + if (isConfirmed) { + dispatch(deleteRoom()) + } + setVisible(false) + } + return ( + <> + + + + ) +} + +export default DeleteRoomContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomContainer.js new file mode 100644 index 00000000..6db2bfb6 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomContainer.js @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { finishRoomEdit, startRoomEdit } from '../../../../../redux/actions/topology/building' +import CheckIcon from '@patternfly/react-icons/dist/js/icons/check-icon' +import PencilAltIcon from '@patternfly/react-icons/dist/js/icons/pencil-alt-icon' +import { Button } from '@patternfly/react-core' + +const EditRoomContainer = () => { + const isEditing = useSelector((state) => state.construction.currentRoomInConstruction !== '-1') + const isInRackConstructionMode = useSelector((state) => state.construction.inRackConstructionMode) + + const dispatch = useDispatch() + const onEdit = () => dispatch(startRoomEdit()) + const onFinish = () => dispatch(finishRoomEdit()) + + return isEditing ? ( + + ) : ( + + ) +} + +export default EditRoomContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js index 0a27910c..8aebe969 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js @@ -1,14 +1,13 @@ import PropTypes from 'prop-types' import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faTimes, faPlus } from '@fortawesome/free-solid-svg-icons' -import { Button } from 'reactstrap' +import { Button } from '@patternfly/react-core' +import PlusIcon from '@patternfly/react-icons/dist/js/icons/plus-icon' +import TimesIcon from '@patternfly/react-icons/dist/js/icons/times-icon' const RackConstructionComponent = ({ onStart, onStop, inRackConstructionMode, isEditingRoom }) => { if (inRackConstructionMode) { return ( - ) @@ -16,13 +15,12 @@ const RackConstructionComponent = ({ onStart, onStop, inRackConstructionMode, is return ( ) diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionContainer.js new file mode 100644 index 00000000..38af447a --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionContainer.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { startRackConstruction, stopRackConstruction } from '../../../../../redux/actions/topology/room' +import RackConstructionComponent from './RackConstructionComponent' + +const RackConstructionContainer = (props) => { + const isRackConstructionMode = useSelector((state) => state.construction.inRackConstructionMode) + const isEditingRoom = useSelector((state) => state.construction.currentRoomInConstruction !== '-1') + + const dispatch = useDispatch() + const onStart = () => dispatch(startRackConstruction()) + const onStop = () => dispatch(stopRackConstruction()) + return ( + + ) +} + +export default RackConstructionContainer diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomName.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomName.js new file mode 100644 index 00000000..d7b006a6 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomName.js @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import NameComponent from '../../../../../components/app/sidebars/topology/NameComponent' +import { editRoomName } from '../../../../../redux/actions/topology/room' + +function RoomName({ roomId }) { + const roomName = useSelector((state) => state.objects.room[roomId].name) + const dispatch = useDispatch() + const callback = (name) => { + if (name) { + dispatch(editRoomName(name)) + } + } + return +} + +RoomName.propTypes = { + roomId: PropTypes.string.isRequired, +} + +export default RoomName diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarComponent.js index 1bc6533e..fac58c51 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarComponent.js @@ -1,20 +1,43 @@ +import PropTypes from 'prop-types' import React from 'react' -import BackToBuildingContainer from '../../../../../containers/app/sidebars/topology/room/BackToBuildingContainer' -import DeleteRoomContainer from '../../../../../containers/app/sidebars/topology/room/DeleteRoomContainer' -import EditRoomContainer from '../../../../../containers/app/sidebars/topology/room/EditRoomContainer' -import RackConstructionContainer from '../../../../../containers/app/sidebars/topology/room/RackConstructionContainer' -import RoomNameContainer from '../../../../../containers/app/sidebars/topology/room/RoomNameContainer' +import RoomName from './RoomName' +import RackConstructionContainer from './RackConstructionContainer' +import EditRoomContainer from './EditRoomContainer' +import DeleteRoomContainer from './DeleteRoomContainer' +import { + TextContent, + TextList, + TextListItem, + TextListItemVariants, + TextListVariants, + Title, +} from '@patternfly/react-core' -const RoomSidebarComponent = () => { +const RoomSidebarComponent = ({ roomId }) => { return ( -
    - - + + Details + + + Name + + + + + + Construction -
    + ) } +RoomSidebarComponent.propTypes = { + roomId: PropTypes.string.isRequired, +} + export default RoomSidebarComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarContainer.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarContainer.js new file mode 100644 index 00000000..2076b00e --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarContainer.js @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useSelector } from 'react-redux' +import RoomSidebarComponent from './RoomSidebarComponent' + +const RoomSidebarContainer = (props) => { + const roomId = useSelector((state) => state.interactionLevel.roomId) + return +} + +export default RoomSidebarContainer diff --git a/opendc-web/opendc-web-ui/src/components/modals/ConfirmationModal.js b/opendc-web/opendc-web-ui/src/components/modals/ConfirmationModal.js index 5a95810a..f6e1c98b 100644 --- a/opendc-web/opendc-web-ui/src/components/modals/ConfirmationModal.js +++ b/opendc-web/opendc-web-ui/src/components/modals/ConfirmationModal.js @@ -2,11 +2,11 @@ import PropTypes from 'prop-types' import React from 'react' import Modal from './Modal' -function ConfirmationModal({ title, message, show, callback }) { +function ConfirmationModal({ title, message, isOpen, callback }) { return ( callback(true)} onCancel={() => callback(false)} submitButtonType="danger" @@ -20,7 +20,7 @@ function ConfirmationModal({ title, message, show, callback }) { ConfirmationModal.propTypes = { title: PropTypes.string.isRequired, message: PropTypes.string.isRequired, - show: PropTypes.bool.isRequired, + isOpen: PropTypes.bool.isRequired, callback: PropTypes.func.isRequired, } diff --git a/opendc-web/opendc-web-ui/src/components/modals/Modal.js b/opendc-web/opendc-web-ui/src/components/modals/Modal.js index 8ab3924c..d4577062 100644 --- a/opendc-web/opendc-web-ui/src/components/modals/Modal.js +++ b/opendc-web/opendc-web-ui/src/components/modals/Modal.js @@ -1,43 +1,27 @@ -import React, { useState, useEffect } from 'react' +import React from 'react' import PropTypes from 'prop-types' -import { Modal as RModal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap' +import { Button, Modal as PModal, ModalVariant } from '@patternfly/react-core' -function Modal({ children, title, show, onSubmit, onCancel, submitButtonType, submitButtonText }) { - const [modal, setModal] = useState(show) - - useEffect(() => setModal(show), [show]) - - const toggle = () => setModal(!modal) - const cancel = () => { - if (onCancel() !== false) { - toggle() - } - } - const submit = () => { - if (onSubmit() !== false) { - toggle() - } - } +function Modal({ children, title, isOpen, onSubmit, onCancel, submitButtonType, submitButtonText }) { + const actions = [ + , + , + ] return ( - - {title} - {children} - - - - - + + {children} + ) } Modal.propTypes = { title: PropTypes.string.isRequired, - show: PropTypes.bool.isRequired, + isOpen: PropTypes.bool, onSubmit: PropTypes.func.isRequired, onCancel: PropTypes.func.isRequired, submitButtonType: PropTypes.string, @@ -48,7 +32,7 @@ Modal.propTypes = { Modal.defaultProps = { submitButtonType: 'primary', submitButtonText: 'Save', - show: false, + isOpen: false, } export default Modal diff --git a/opendc-web/opendc-web-ui/src/components/modals/TextInputModal.js b/opendc-web/opendc-web-ui/src/components/modals/TextInputModal.js index 6758fdc0..392a729e 100644 --- a/opendc-web/opendc-web-ui/src/components/modals/TextInputModal.js +++ b/opendc-web/opendc-web-ui/src/components/modals/TextInputModal.js @@ -1,31 +1,60 @@ import PropTypes from 'prop-types' -import React, { useRef } from 'react' +import React, { useRef, useState } from 'react' import Modal from './Modal' +import { Form, FormGroup, TextInput } from '@patternfly/react-core' -function TextInputModal({ title, label, show, callback, initialValue }) { +function TextInputModal({ title, label, isOpen, callback, initialValue }) { const textInput = useRef(null) - const onSubmit = () => { - callback(textInput.current.value) + const [isSubmitted, setSubmitted] = useState(false) + const [isValid, setValid] = useState(true) + + const resetState = () => { textInput.current.value = '' + setSubmitted(false) + setValid(false) + } + const onSubmit = (event) => { + const value = textInput.current.value + setSubmitted(true) + + if (event) { + event.preventDefault() + } + + if (!value) { + setValid(false) + return false + } + + callback(value) + resetState() + return true } const onCancel = () => { callback(undefined) - textInput.current.value = '' + resetState() } return ( - -
    { - e.preventDefault() - onSubmit() - }} - > -
    - - -
    -
    + +
    + + + +
    ) } @@ -33,7 +62,7 @@ function TextInputModal({ title, label, show, callback, initialValue }) { TextInputModal.propTypes = { title: PropTypes.string.isRequired, label: PropTypes.string.isRequired, - show: PropTypes.bool.isRequired, + isOpen: PropTypes.bool.isRequired, callback: PropTypes.func.isRequired, initialValue: PropTypes.string, } diff --git a/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewPortfolioModal.js b/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewPortfolioModal.js new file mode 100644 index 00000000..afe07597 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewPortfolioModal.js @@ -0,0 +1,139 @@ +import PropTypes from 'prop-types' +import React, { useRef, useState } from 'react' +import Modal from '../Modal' +import { + Form, + FormGroup, + FormSection, + NumberInput, + Select, + SelectGroup, + SelectOption, + SelectVariant, + TextInput, +} from '@patternfly/react-core' +import { METRIC_GROUPS, METRIC_NAMES } from '../../../util/available-metrics' + +const NewPortfolioModal = ({ isOpen, onSubmit: onSubmitUpstream, onCancel: onUpstreamCancel }) => { + const nameInput = useRef(null) + const [repeats, setRepeats] = useState(1) + const [isSelectOpen, setSelectOpen] = useState(false) + const [selectedMetrics, setSelectedMetrics] = useState([]) + + const [isSubmitted, setSubmitted] = useState(false) + const [errors, setErrors] = useState({}) + + const clearState = () => { + setSubmitted(false) + setErrors({}) + nameInput.current.value = '' + setRepeats(1) + setSelectOpen(false) + setSelectedMetrics([]) + } + + const onSubmit = (event) => { + setSubmitted(true) + + if (event) { + event.preventDefault() + } + + const name = nameInput.current.value + + if (!name) { + setErrors({ name: true }) + return false + } else { + onSubmitUpstream(name, { enabledMetrics: selectedMetrics, repeatsPerScenario: repeats }) + } + + clearState() + return false + } + const onCancel = () => { + onUpstreamCancel() + clearState() + } + + const onSelect = (event, selection) => { + if (selectedMetrics.includes(selection)) { + setSelectedMetrics((metrics) => metrics.filter((item) => item !== selection)) + } else { + setSelectedMetrics((metrics) => [...metrics, selection]) + } + } + + return ( + +
    + + + + + + + + + + + setRepeats(Number(e.target.value))} + onPlus={() => setRepeats((r) => r + 1)} + onMinus={() => setRepeats((r) => r - 1)} + min={1} + /> + + +
    +
    + ) +} + +NewPortfolioModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onSubmit: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, +} + +export default NewPortfolioModal diff --git a/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewPortfolioModalComponent.js b/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewPortfolioModalComponent.js deleted file mode 100644 index 3c6b8724..00000000 --- a/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewPortfolioModalComponent.js +++ /dev/null @@ -1,78 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useRef } from 'react' -import { Form, FormGroup, Input, Label } from 'reactstrap' -import Modal from '../Modal' -import { AVAILABLE_METRICS, METRIC_NAMES } from '../../../util/available-metrics' - -const NewPortfolioModalComponent = ({ show, callback }) => { - const form = useRef(null) - const textInput = useRef(null) - const repeatsInput = useRef(null) - const metricCheckboxes = useRef({}) - - const onSubmit = () => { - if (form.current.reportValidity()) { - callback(textInput.current.value, { - enabledMetrics: AVAILABLE_METRICS.filter((metric) => metricCheckboxes.current[metric].checked), - repeatsPerScenario: parseInt(repeatsInput.current.value), - }) - - return true - } else { - return false - } - } - const onCancel = () => callback(undefined) - - return ( - -
    { - e.preventDefault() - this.onSubmit() - }} - innerRef={form} - > - - - - -

    Targets

    -
    Metrics
    - - {AVAILABLE_METRICS.map((metric) => ( - - - - ))} - - - - - -
    -
    - ) -} - -NewPortfolioModalComponent.propTypes = { - show: PropTypes.bool.isRequired, - callback: PropTypes.func.isRequired, -} - -export default NewPortfolioModalComponent diff --git a/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewScenarioModal.js b/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewScenarioModal.js new file mode 100644 index 00000000..94d0d424 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewScenarioModal.js @@ -0,0 +1,159 @@ +import PropTypes from 'prop-types' +import React, { useRef, useState } from 'react' +import { Portfolio } from '../../../shapes' +import Modal from '../Modal' +import { + Checkbox, + Form, + FormGroup, + FormSection, + FormSelect, + FormSelectOption, + NumberInput, + TextInput, +} from '@patternfly/react-core' +import { useSchedulers, useTraces } from '../../../data/experiments' +import { useProjectTopologies } from '../../../data/topology' +import { usePortfolio } from '../../../data/project' + +const NewScenarioModal = ({ portfolioId, isOpen, onSubmit: onSubmitUpstream, onCancel: onCancelUpstream }) => { + const { data: portfolio } = usePortfolio(portfolioId) + const { data: topologies = [] } = useProjectTopologies(portfolio?.projectId) + const { data: traces = [] } = useTraces() + const { data: schedulers = [] } = useSchedulers() + + const [isSubmitted, setSubmitted] = useState(false) + const [traceLoad, setTraceLoad] = useState(100) + const [trace, setTrace] = useState(undefined) + const [topology, setTopology] = useState(undefined) + const [scheduler, setScheduler] = useState(undefined) + const [failuresEnabled, setFailuresEnabled] = useState(false) + const [opPhenEnabled, setOpPhenEnabled] = useState(false) + const nameInput = useRef(null) + + const resetState = () => { + setSubmitted(false) + setTraceLoad(100) + setTrace(undefined) + setTopology(undefined) + setScheduler(undefined) + setFailuresEnabled(false) + setOpPhenEnabled(false) + nameInput.current.value = '' + } + + const onSubmit = (event) => { + setSubmitted(true) + + if (event) { + event.preventDefault() + } + + const name = nameInput.current.value + + onSubmitUpstream( + name, + portfolio._id, + { + traceId: trace || traces[0]._id, + loadSamplingFraction: traceLoad / 100, + }, + { + topologyId: topology || topologies[0]._id, + }, + { + failuresEnabled, + performanceInterferenceEnabled: opPhenEnabled, + schedulerName: scheduler || schedulers[0].name, + } + ) + + resetState() + return true + } + const onCancel = () => { + onCancelUpstream() + resetState() + } + + return ( + +
    + + + + + + + {traces.map((trace) => ( + + ))} + + + + setTraceLoad((load) => load - 1)} + onPlus={() => setTraceLoad((load) => load + 1)} + onChange={(e) => setTraceLoad(Number(e.target.value))} + unit="%" + /> + + + + + + {topologies.map((topology) => ( + + ))} + + + + + + {schedulers.map((scheduler) => ( + + ))} + + + + + setFailuresEnabled((e) => !e)} + /> + setOpPhenEnabled((e) => !e)} + /> + +
    +
    + ) +} + +NewScenarioModal.propTypes = { + portfolioId: PropTypes.string, + isOpen: PropTypes.bool.isRequired, + onSubmit: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, +} + +export default NewScenarioModal diff --git a/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewScenarioModalComponent.js b/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewScenarioModalComponent.js deleted file mode 100644 index 782812ac..00000000 --- a/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewScenarioModalComponent.js +++ /dev/null @@ -1,144 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useRef } from 'react' -import { Form, FormGroup, Input, Label } from 'reactstrap' -import { Scheduler, Topology, Trace } from '../../../shapes' -import Modal from '../Modal' - -const NewScenarioModalComponent = ({ - show, - callback, - currentPortfolioId, - currentPortfolioScenarioIds, - traces, - topologies, - schedulers, -}) => { - const form = useRef(null) - const textInput = useRef(null) - const traceSelect = useRef(null) - const traceLoadInput = useRef(null) - const topologySelect = useRef(null) - const failuresCheckbox = useRef(null) - const performanceInterferenceCheckbox = useRef(null) - const schedulerSelect = useRef(null) - - const onSubmit = () => { - if (!form.current.reportValidity()) { - return false - } - callback( - textInput.current.value, - currentPortfolioId, - { - traceId: traceSelect.current.value, - loadSamplingFraction: parseFloat(traceLoadInput.current.value), - }, - { - topologyId: topologySelect.current.value, - }, - { - failuresEnabled: failuresCheckbox.current.checked, - performanceInterferenceEnabled: performanceInterferenceCheckbox.current.checked, - schedulerName: schedulerSelect.current.value, - } - ) - return true - } - const onCancel = () => { - callback(undefined) - } - - return ( - -
    { - e.preventDefault() - onSubmit() - }} - innerRef={form} - > - - - - -

    Trace

    - - - - {traces.map((trace) => ( - - ))} - - - - - - -

    Topology

    -
    - - - {topologies.map((topology) => ( - - ))} - -
    -

    Operational Phenomena

    - - - - - - - - - - {schedulers.map((scheduler) => ( - - ))} - - -
    -
    - ) -} - -NewScenarioModalComponent.propTypes = { - show: PropTypes.bool.isRequired, - currentPortfolioId: PropTypes.string.isRequired, - currentPortfolioScenarioIds: PropTypes.arrayOf(PropTypes.string), - traces: PropTypes.arrayOf(Trace), - topologies: PropTypes.arrayOf(Topology), - schedulers: PropTypes.arrayOf(Scheduler), - callback: PropTypes.func.isRequired, -} - -export default NewScenarioModalComponent diff --git a/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewTopologyModal.js b/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewTopologyModal.js new file mode 100644 index 00000000..49952aec --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewTopologyModal.js @@ -0,0 +1,81 @@ +import PropTypes from 'prop-types' +import React, { useRef, useState } from 'react' +import Modal from '../Modal' +import { Form, FormGroup, FormSelect, FormSelectOption, TextInput } from '@patternfly/react-core' +import { useProjectTopologies } from '../../../data/topology' + +const NewTopologyModal = ({ projectId, isOpen, onSubmit: onSubmitUpstream, onCancel: onCancelUpstream }) => { + const nameInput = useRef(null) + const [isSubmitted, setSubmitted] = useState(false) + const [originTopology, setOriginTopology] = useState(-1) + const [errors, setErrors] = useState({}) + + const { data: topologies = [] } = useProjectTopologies(projectId) + + const clearState = () => { + nameInput.current.value = '' + setSubmitted(false) + setOriginTopology(-1) + setErrors({}) + } + + const onSubmit = (event) => { + setSubmitted(true) + + if (event) { + event.preventDefault() + } + + const name = nameInput.current.value + + if (!name) { + setErrors({ name: true }) + return false + } else if (originTopology === -1) { + onSubmitUpstream(name) + } else { + onSubmitUpstream(name, originTopology) + } + + clearState() + return true + } + + const onCancel = () => { + onCancelUpstream() + clearState() + } + + return ( + +
    + + + + + + + {topologies.map((topology) => ( + + ))} + + +
    +
    + ) +} + +NewTopologyModal.propTypes = { + projectId: PropTypes.string, + isOpen: PropTypes.bool.isRequired, + onSubmit: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, +} + +export default NewTopologyModal diff --git a/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewTopologyModalComponent.js b/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewTopologyModalComponent.js deleted file mode 100644 index f06fe797..00000000 --- a/opendc-web/opendc-web-ui/src/components/modals/custom-components/NewTopologyModalComponent.js +++ /dev/null @@ -1,71 +0,0 @@ -import PropTypes from 'prop-types' -import { Form, FormGroup, Input, Label } from 'reactstrap' -import React, { useRef } from 'react' -import { Topology } from '../../../shapes' -import Modal from '../Modal' - -const NewTopologyModalComponent = ({ show, onCreateTopology, onDuplicateTopology, onCancel, topologies }) => { - const form = useRef(null) - const textInput = useRef(null) - const originTopology = useRef(null) - - const onCreate = () => { - onCreateTopology(textInput.current.value) - } - - const onDuplicate = () => { - onDuplicateTopology(textInput.current.value, originTopology.current.value) - } - - const onSubmit = () => { - if (!form.current.reportValidity()) { - return false - } else if (originTopology.current.selectedIndex === 0) { - onCreate() - } else { - onDuplicate() - } - - return true - } - - return ( - -
    { - e.preventDefault() - onSubmit() - }} - innerRef={form} - > - - - - - - - - - {topologies.map((topology) => ( - - ))} - - -
    -
    - ) -} - -NewTopologyModalComponent.propTypes = { - show: PropTypes.bool.isRequired, - topologies: PropTypes.arrayOf(Topology), - onCreateTopology: PropTypes.func.isRequired, - onDuplicateTopology: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, -} - -export default NewTopologyModalComponent diff --git a/opendc-web/opendc-web-ui/src/components/navigation/AppNavbarComponent.js b/opendc-web/opendc-web-ui/src/components/navigation/AppNavbarComponent.js deleted file mode 100644 index 8aaaf847..00000000 --- a/opendc-web/opendc-web-ui/src/components/navigation/AppNavbarComponent.js +++ /dev/null @@ -1,37 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import Link from 'next/link' -import { NavLink, NavItem as RNavItem } from 'reactstrap' -import Navbar, { NavItem } from './Navbar' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faList } from '@fortawesome/free-solid-svg-icons' -import { Project } from '../../shapes' - -const AppNavbarComponent = ({ project, fullWidth }) => ( - - - - - - My Projects - - - - {project ? ( - - - - {project.name} - - - - ) : undefined} - -) - -AppNavbarComponent.propTypes = { - project: Project, - fullWidth: PropTypes.bool, -} - -export default AppNavbarComponent diff --git a/opendc-web/opendc-web-ui/src/components/navigation/LogoutButton.js b/opendc-web/opendc-web-ui/src/components/navigation/LogoutButton.js deleted file mode 100644 index 4ab577e0..00000000 --- a/opendc-web/opendc-web-ui/src/components/navigation/LogoutButton.js +++ /dev/null @@ -1,17 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { NavLink } from 'reactstrap' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faSignOutAlt } from '@fortawesome/free-solid-svg-icons' - -const LogoutButton = ({ onLogout }) => ( - - - -) - -LogoutButton.propTypes = { - onLogout: PropTypes.func.isRequired, -} - -export default LogoutButton diff --git a/opendc-web/opendc-web-ui/src/components/navigation/Navbar.js b/opendc-web/opendc-web-ui/src/components/navigation/Navbar.js deleted file mode 100644 index dc74bb8f..00000000 --- a/opendc-web/opendc-web-ui/src/components/navigation/Navbar.js +++ /dev/null @@ -1,120 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useState } from 'react' -import Link from 'next/link' -import { useRouter } from 'next/router' -import Image from 'next/image' -import { - Navbar as RNavbar, - NavItem as RNavItem, - NavLink, - NavbarBrand, - NavbarToggler, - Collapse, - Nav, - Container, -} from 'reactstrap' -import Login from '../../containers/auth/Login' -import Logout from '../../containers/auth/Logout' -import ProfileName from '../../containers/auth/ProfileName' -import { login, navbar, opendcBrand } from './Navbar.module.scss' -import { useAuth } from '../../auth' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faGithub } from '@fortawesome/free-brands-svg-icons' - -export const NAVBAR_HEIGHT = 60 - -const GitHubLink = () => ( - - - -) - -export const NavItem = ({ route, children }) => { - const router = useRouter() - const handleClick = (e) => { - e.preventDefault() - router.push(route) - } - return ( - - {children} - - ) -} - -NavItem.propTypes = { - route: PropTypes.string.isRequired, - children: PropTypes.node, -} - -export const LoggedInSection = () => { - const router = useRouter() - const { isAuthenticated } = useAuth() - return ( - - ) -} - -const Navbar = ({ fullWidth, children }) => { - const [isOpen, setIsOpen] = useState(false) - const toggle = () => setIsOpen(!isOpen) - - return ( - - - - -
    - OpenDC -
    -
    - - - - - -
    -
    - ) -} - -Navbar.propTypes = { - fullWidth: PropTypes.bool, - children: PropTypes.node, -} - -export default Navbar diff --git a/opendc-web/opendc-web-ui/src/components/navigation/Navbar.module.scss b/opendc-web/opendc-web-ui/src/components/navigation/Navbar.module.scss deleted file mode 100644 index 8b9e4c97..00000000 --- a/opendc-web/opendc-web-ui/src/components/navigation/Navbar.module.scss +++ /dev/null @@ -1,36 +0,0 @@ -@import 'src/style/_mixins.scss'; -@import 'src/style/_variables.scss'; - -.navbar { - border-top: $blue 3px solid; - border-bottom: $gray-semi-dark 1px solid; - color: $gray-very-dark; - background: #fafafb; -} - -.opendcBrand { - display: inline-block; - color: $gray-very-dark; - - transition: background $transition-length; - - img { - position: relative; - bottom: 3px; - display: inline-block; - width: 30px; - } -} - -.login { - height: 40px; - background: $blue; - border: none; - padding-top: 10px; - - @include clickable; - - &:hover { - background: $blue-dark; - } -} diff --git a/opendc-web/opendc-web-ui/src/components/not-found/BlinkingCursor.js b/opendc-web/opendc-web-ui/src/components/not-found/BlinkingCursor.js deleted file mode 100644 index 03a4894b..00000000 --- a/opendc-web/opendc-web-ui/src/components/not-found/BlinkingCursor.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react' -import { blinkingCursor } from './BlinkingCursor.module.scss' - -const BlinkingCursor = () => _ - -export default BlinkingCursor diff --git a/opendc-web/opendc-web-ui/src/components/not-found/BlinkingCursor.module.scss b/opendc-web/opendc-web-ui/src/components/not-found/BlinkingCursor.module.scss deleted file mode 100644 index aba0c604..00000000 --- a/opendc-web/opendc-web-ui/src/components/not-found/BlinkingCursor.module.scss +++ /dev/null @@ -1,13 +0,0 @@ -.blinkingCursor { - animation: blink 1s step-end infinite; -} - -@keyframes blink { - from, - to { - color: #eeeeee; - } - 50% { - color: #333333; - } -} diff --git a/opendc-web/opendc-web-ui/src/components/not-found/CodeBlock.js b/opendc-web/opendc-web-ui/src/components/not-found/CodeBlock.js deleted file mode 100644 index 6ded4350..00000000 --- a/opendc-web/opendc-web-ui/src/components/not-found/CodeBlock.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react' -import { codeBlock } from './CodeBlock.module.scss' - -const CodeBlock = () => { - const textBlock = - ' oo oooo oo
    ' + - ' oo oo oo oo
    ' + - ' oo oo oo oo
    ' + - ' oooooo oo oo oooooo
    ' + - ' oo oo oo oo
    ' + - ' oo oooo oo
    ' - const charList = textBlock.split('') - - // Binary representation of the string "OpenDC!" ;) - const binaryString = '01001111011100000110010101101110010001000100001100100001' - - let binaryIndex = 0 - for (let i = 0; i < charList.length; i++) { - if (charList[i] === 'o') { - charList[i] = binaryString[binaryIndex] - binaryIndex++ - } - } - - return
    -} - -export default CodeBlock diff --git a/opendc-web/opendc-web-ui/src/components/not-found/CodeBlock.module.scss b/opendc-web/opendc-web-ui/src/components/not-found/CodeBlock.module.scss deleted file mode 100644 index 8af3ee6d..00000000 --- a/opendc-web/opendc-web-ui/src/components/not-found/CodeBlock.module.scss +++ /dev/null @@ -1,4 +0,0 @@ -.codeBlock { - white-space: pre-wrap; - margin-top: 60px; -} diff --git a/opendc-web/opendc-web-ui/src/components/not-found/TerminalWindow.js b/opendc-web/opendc-web-ui/src/components/not-found/TerminalWindow.js deleted file mode 100644 index e6200b10..00000000 --- a/opendc-web/opendc-web-ui/src/components/not-found/TerminalWindow.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react' -import Link from 'next/link' -import BlinkingCursor from './BlinkingCursor' -import CodeBlock from './CodeBlock' -import { terminalWindow, terminalHeader, terminalBody, segfault, subTitle, homeBtn } from './TerminalWindow.module.scss' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faHome } from '@fortawesome/free-solid-svg-icons' - -const TerminalWindow = () => ( -
    -
    Terminal -- bash
    -
    -
    - $ status -
    - opendc[4264]: segfault at 0000051497be459d1 err 12 in libopendc.9.0.4 -
    - opendc[4269]: segfault at 000004234855fc2db err 3 in libopendc.9.0.4 -
    - opendc[4270]: STDERR Page does not exist -
    -
    - -
    - Got lost? - -
    - - - GET ME BACK TO OPENDC - - -
    -
    -) - -export default TerminalWindow diff --git a/opendc-web/opendc-web-ui/src/components/not-found/TerminalWindow.module.scss b/opendc-web/opendc-web-ui/src/components/not-found/TerminalWindow.module.scss deleted file mode 100644 index 614852d3..00000000 --- a/opendc-web/opendc-web-ui/src/components/not-found/TerminalWindow.module.scss +++ /dev/null @@ -1,61 +0,0 @@ -.terminalWindow { - display: block; - align-self: center; - - margin: auto; - - user-select: none; - cursor: default; - - overflow: hidden; - - box-shadow: 5px 5px 20px #444444; -} - -.terminalHeader { - font-family: monospace; - background: #cccccc; - color: #444444; - height: 30px; - line-height: 30px; - padding-left: 10px; - - border-top-left-radius: 7px; - border-top-right-radius: 7px; -} - -.terminalBody { - font-family: monospace; - text-align: center; - background-color: #333333; - color: #eeeeee; - padding: 10px; - - height: 100%; -} - -.segfault { - text-align: left; -} - -.subTitle { - margin-top: 20px; -} - -.homeBtn { - margin-top: 10px; - padding: 5px; - display: inline-block; - border: 1px solid #eeeeee; - color: #eeeeee; - text-decoration: none; - cursor: pointer; - - transition: all 200ms; - - &:hover, - &:active { - background: #eeeeee; - color: #333333; - } -} diff --git a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js b/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js index 5129c013..285217e9 100644 --- a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js +++ b/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js @@ -1,23 +1,21 @@ import React from 'react' import PropTypes from 'prop-types' -import { Button, ButtonGroup } from 'reactstrap' +import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core' import { filterPanel } from './FilterPanel.module.scss' export const FILTERS = { SHOW_ALL: 'All Projects', SHOW_OWN: 'My Projects', SHOW_SHARED: 'Shared with me' } const FilterPanel = ({ onSelect, activeFilter = 'SHOW_ALL' }) => ( - + {Object.keys(FILTERS).map((filter) => ( - + onChange={() => activeFilter === filter || onSelect(filter)} + isSelected={activeFilter === filter} + text={FILTERS[filter]} + /> ))} - + ) FilterPanel.propTypes = { diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js b/opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js new file mode 100644 index 00000000..ae4cb9cd --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import { PlusIcon } from '@patternfly/react-icons' +import { Button } from '@patternfly/react-core' +import { useState } from 'react' +import NewPortfolioModal from '../modals/custom-components/NewPortfolioModal' +import { useMutation } from 'react-query' + +function NewPortfolio({ projectId }) { + const [isVisible, setVisible] = useState(false) + const { mutate: addPortfolio } = useMutation('addPortfolio') + + const onSubmit = (name, targets) => { + addPortfolio({ projectId, name, targets }) + setVisible(false) + } + + return ( + <> + + setVisible(false)} /> + + ) +} + +NewPortfolio.propTypes = { + projectId: PropTypes.string, +} + +export default NewPortfolio diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewProject.js b/opendc-web/opendc-web-ui/src/components/projects/NewProject.js new file mode 100644 index 00000000..4f5d79cf --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/NewProject.js @@ -0,0 +1,39 @@ +import React, { useState } from 'react' +import TextInputModal from '../../components/modals/TextInputModal' +import { Button } from '@patternfly/react-core' +import { useMutation } from 'react-query' +import { PlusIcon } from '@patternfly/react-icons' +import { buttonContainer } from './NewProject.module.scss' + +/** + * A container for creating a new project. + */ +const NewProject = () => { + const [isVisible, setVisible] = useState(false) + const { mutate: addProject } = useMutation('addProject') + + const onSubmit = (name) => { + if (name) { + addProject({ name }) + } + setVisible(false) + } + + return ( + <> +
    + +
    + + + ) +} + +export default NewProject diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewProject.module.scss b/opendc-web/opendc-web-ui/src/components/projects/NewProject.module.scss new file mode 100644 index 00000000..5a0e74fc --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/NewProject.module.scss @@ -0,0 +1,26 @@ +/*! + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +.buttonContainer { + flex: 0 1 auto; + padding: 20px 0; +} diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewScenario.js b/opendc-web/opendc-web-ui/src/components/projects/NewScenario.js new file mode 100644 index 00000000..6d4f48c1 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/NewScenario.js @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import { PlusIcon } from '@patternfly/react-icons' +import { Button } from '@patternfly/react-core' +import { useState } from 'react' +import NewScenarioModal from '../modals/custom-components/NewScenarioModal' +import { useMutation } from 'react-query' + +function NewScenario({ portfolioId }) { + const [isVisible, setVisible] = useState(false) + const { mutate: addScenario } = useMutation('addScenario') + + const onSubmit = (name, portfolioId, trace, topology, operational) => { + addScenario({ + portfolioId, + name, + trace, + topology, + operational, + }) + setVisible(false) + } + + return ( + <> + + setVisible(false)} + /> + + ) +} + +NewScenario.propTypes = { + portfolioId: PropTypes.string, +} + +export default NewScenario diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewTopology.js b/opendc-web/opendc-web-ui/src/components/projects/NewTopology.js new file mode 100644 index 00000000..c6c4171b --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/NewTopology.js @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import { PlusIcon } from '@patternfly/react-icons' +import { Button } from '@patternfly/react-core' +import { useState } from 'react' +import NewTopologyModal from '../modals/custom-components/NewTopologyModal' +import { useDispatch } from 'react-redux' +import { addTopology } from '../../redux/actions/topologies' + +function NewTopology({ projectId }) { + const [isVisible, setVisible] = useState(false) + const dispatch = useDispatch() + + const onSubmit = (name, duplicateId) => { + dispatch(addTopology(projectId, name, duplicateId)) + setVisible(false) + } + return ( + <> + + setVisible(false)} + /> + + ) +} + +NewTopology.propTypes = { + projectId: PropTypes.string, +} + +export default NewTopology diff --git a/opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js b/opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js new file mode 100644 index 00000000..45e399ed --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import Link from 'next/link' +import { Table, TableBody, TableHeader } from '@patternfly/react-table' +import React from 'react' +import TableEmptyState from '../util/TableEmptyState' +import { useProjectPortfolios } from '../../data/project' +import { useMutation } from 'react-query' + +const PortfolioTable = ({ projectId }) => { + const { status, data: portfolios = [] } = useProjectPortfolios(projectId) + const { mutate: deletePortfolio } = useMutation('deletePortfolio') + + const columns = ['Name', 'Scenarios', 'Metrics', 'Repeats'] + const rows = + portfolios.length > 0 + ? portfolios.map((portfolio) => [ + { + title: ( + + {portfolio.name} + + ), + }, + + portfolio.scenarioIds.length === 1 ? '1 scenario' : `${portfolio.scenarioIds.length} scenarios`, + + portfolio.targets.enabledMetrics.length === 1 + ? '1 metric' + : `${portfolio.targets.enabledMetrics.length} metrics`, + portfolio.targets.repeatsPerScenario === 1 + ? '1 repeat' + : `${portfolio.targets.repeatsPerScenario} repeats`, + ]) + : [ + { + heightAuto: true, + cells: [ + { + props: { colSpan: 4 }, + title: ( + + ), + }, + ], + }, + ] + + const actions = + portfolios.length > 0 + ? [ + { + title: 'Delete Portfolio', + onClick: (_, rowId) => deletePortfolio(portfolios[rowId]._id), + }, + ] + : [] + + return ( + + + +
    + ) +} + +PortfolioTable.propTypes = { + projectId: PropTypes.string, +} + +export default PortfolioTable diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectActionButtons.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectActionButtons.js deleted file mode 100644 index 4adf3205..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/ProjectActionButtons.js +++ /dev/null @@ -1,38 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import Link from 'next/link' -import { Button } from 'reactstrap' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faPlay, faUsers, faTrash } from '@fortawesome/free-solid-svg-icons' - -const ProjectActionButtons = ({ projectId, onViewUsers, onDelete }) => ( - - - - - - - -) - -ProjectActionButtons.propTypes = { - projectId: PropTypes.string.isRequired, - onViewUsers: PropTypes.func, - onDelete: PropTypes.func, -} - -export default ProjectActionButtons diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js deleted file mode 100644 index 46ef4691..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js +++ /dev/null @@ -1,41 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Project } from '../../shapes' -import ProjectRow from './ProjectRow' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons' - -const ProjectList = ({ projects }) => { - return ( -
    - {projects.length === 0 ? ( -
    - - No projects here yet... Add some with the ‘New Project’ button! -
    - ) : ( - - - - - - - - - - {projects.map((project) => ( - - ))} - -
    Project nameLast editedAccess rights -
    - )} -
    - ) -} - -ProjectList.propTypes = { - projects: PropTypes.arrayOf(Project).isRequired, -} - -export default ProjectList diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js deleted file mode 100644 index 91368de8..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react' -import ProjectActions from '../../containers/projects/ProjectActions' -import { Project } from '../../shapes' -import { AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP } from '../../util/authorizations' -import { parseAndFormatDateTime } from '../../util/date-time' -import { useAuth } from '../../auth' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' - -const ProjectRow = ({ project }) => { - const { user } = useAuth() - const { level } = project.authorizations.find((auth) => auth.userId === user.sub) - return ( - - {project.name} - {parseAndFormatDateTime(project.datetimeLastEdited)} - - - {AUTH_DESCRIPTION_MAP[level]} - - - - ) -} - -ProjectRow.propTypes = { - project: Project.isRequired, -} - -export default ProjectRow diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectTable.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectTable.js new file mode 100644 index 00000000..a7290259 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/ProjectTable.js @@ -0,0 +1,76 @@ +import PropTypes from 'prop-types' +import React from 'react' +import Link from 'next/link' +import { Project, Status } from '../../shapes' +import { Table, TableBody, TableHeader } from '@patternfly/react-table' +import { parseAndFormatDateTime } from '../../util/date-time' +import { AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP } from '../../util/authorizations' +import { useAuth } from '../../auth' +import TableEmptyState from '../util/TableEmptyState' + +const ProjectTable = ({ status, projects, onDelete, isFiltering }) => { + const { user } = useAuth() + const columns = ['Project name', 'Last edited', 'Access Rights'] + const rows = + projects.length > 0 + ? projects.map((project) => { + const { level } = project.authorizations.find((auth) => auth.userId === user.sub) + const Icon = AUTH_ICON_MAP[level] + return [ + { + title: {project.name}, + }, + parseAndFormatDateTime(project.datetimeLastEdited), + { + title: ( + <> + {AUTH_DESCRIPTION_MAP[level]} + + ), + }, + ] + }) + : [ + { + heightAuto: true, + cells: [ + { + props: { colSpan: 3 }, + title: ( + + ), + }, + ], + }, + ] + + const actions = + projects.length > 0 + ? [ + { + title: 'Delete Project', + onClick: (_, rowId) => onDelete(projects[rowId]), + }, + ] + : [] + + return ( + + + +
    + ) +} + +ProjectTable.propTypes = { + status: Status.isRequired, + isFiltering: PropTypes.bool, + projects: PropTypes.arrayOf(Project).isRequired, + onDelete: PropTypes.func, +} + +export default ProjectTable diff --git a/opendc-web/opendc-web-ui/src/components/projects/ScenarioState.js b/opendc-web/opendc-web-ui/src/components/projects/ScenarioState.js new file mode 100644 index 00000000..285345e7 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/ScenarioState.js @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import { ClockIcon, InfoIcon, CheckCircleIcon, ErrorCircleOIcon } from '@patternfly/react-icons' + +function ScenarioState({ state }) { + switch (state) { + case 'CLAIMED': + case 'QUEUED': + return ( + + Queued + + ) + case 'RUNNING': + return ( + + Running + + ) + case 'FINISHED': + return ( + + Finished + + ) + case 'FAILED': + return ( + + Failed + + ) + } + + return 'Unknown' +} + +ScenarioState.propTypes = { + state: PropTypes.oneOf(['QUEUED', 'CLAIMED', 'RUNNING', 'FINISHED', 'FAILED']), +} + +export default ScenarioState diff --git a/opendc-web/opendc-web-ui/src/components/projects/ScenarioTable.js b/opendc-web/opendc-web-ui/src/components/projects/ScenarioTable.js new file mode 100644 index 00000000..9966e3ba --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/ScenarioTable.js @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import Link from 'next/link' +import { Table, TableBody, TableHeader } from '@patternfly/react-table' +import React from 'react' +import TableEmptyState from '../util/TableEmptyState' +import ScenarioState from './ScenarioState' +import { usePortfolio, usePortfolioScenarios } from '../../data/project' +import { useProjectTopologies } from '../../data/topology' +import { useMutation } from 'react-query' + +const ScenarioTable = ({ portfolioId }) => { + const { data: portfolio } = usePortfolio(portfolioId) + const { status, data: scenarios = [] } = usePortfolioScenarios(portfolioId) + const { data: topologies } = useProjectTopologies(portfolio?.projectId, { + select: (topologies) => new Map(topologies.map((topology) => [topology._id, topology])), + }) + + const { mutate: deleteScenario } = useMutation('deleteScenario') + + const columns = ['Name', 'Topology', 'Trace', 'State'] + const rows = + scenarios.length > 0 + ? scenarios.map((scenario) => { + const topology = topologies?.get(scenario.topology.topologyId) + + return [ + scenario.name, + { + title: topology ? ( + + {topology.name} + + ) : ( + 'Unknown Topology' + ), + }, + scenario.trace.traceId, + { title: }, + ] + }) + : [ + { + heightAuto: true, + cells: [ + { + props: { colSpan: 4 }, + title: ( + + ), + }, + ], + }, + ] + + const actionResolver = (_, { rowIndex }) => [ + { + title: 'Delete Scenario', + onClick: (_, rowId) => deleteScenario(scenarios[rowId]._id), + isDisabled: rowIndex === 0, + }, + ] + + return ( + 0 ? actionResolver : undefined} + > + + +
    + ) +} + +ScenarioTable.propTypes = { + portfolioId: PropTypes.string, +} + +export default ScenarioTable diff --git a/opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js b/opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js new file mode 100644 index 00000000..80099ece --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import Link from 'next/link' +import { Table, TableBody, TableHeader } from '@patternfly/react-table' +import React from 'react' +import TableEmptyState from '../util/TableEmptyState' +import { parseAndFormatDateTime } from '../../util/date-time' +import { useMutation } from 'react-query' +import { useProjectTopologies } from '../../data/topology' + +const TopologyTable = ({ projectId }) => { + const { status, data: topologies = [] } = useProjectTopologies(projectId) + const { mutate: deleteTopology } = useMutation('deleteTopology') + + const columns = ['Name', 'Rooms', 'Last Edited'] + const rows = + topologies.length > 0 + ? topologies.map((topology) => [ + { + title: ( + + {topology.name} + + ), + }, + topology.rooms.length === 1 ? '1 room' : `${topology.rooms.length} rooms`, + parseAndFormatDateTime(topology.datetimeLastEdited), + ]) + : [ + { + heightAuto: true, + cells: [ + { + props: { colSpan: 3 }, + title: ( + + ), + }, + ], + }, + ] + + const actionResolver = (_, { rowIndex }) => [ + { + title: 'Delete Topology', + onClick: (_, rowId) => deleteTopology(topologies[rowId]._id), + isDisabled: rowIndex === 0, + }, + ] + + return ( + 0 ? actionResolver : () => []} + > + + +
    + ) +} + +TopologyTable.propTypes = { + projectId: PropTypes.string, +} + +export default TopologyTable diff --git a/opendc-web/opendc-web-ui/src/components/util/BreadcrumbLink.js b/opendc-web/opendc-web-ui/src/components/util/BreadcrumbLink.js new file mode 100644 index 00000000..c6ab214a --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/util/BreadcrumbLink.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import Link from 'next/link' + +const BreadcrumbLink = ({ children, href, ...props }) => ( + + {children} + +) + +BreadcrumbLink.propTypes = { + children: PropTypes.node, + href: PropTypes.string.isRequired, +} + +export default BreadcrumbLink diff --git a/opendc-web/opendc-web-ui/src/components/util/NavItemLink.js b/opendc-web/opendc-web-ui/src/components/util/NavItemLink.js new file mode 100644 index 00000000..c0d109bd --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/util/NavItemLink.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Link from 'next/link' +import PropTypes from 'prop-types' + +const NavItemLink = ({ children, href, ...props }) => ( + + {children} + +) + +NavItemLink.propTypes = { + children: PropTypes.node, + href: PropTypes.string.isRequired, +} + +export default NavItemLink diff --git a/opendc-web/opendc-web-ui/src/components/util/TableEmptyState.js b/opendc-web/opendc-web-ui/src/components/util/TableEmptyState.js new file mode 100644 index 00000000..9d16ffbb --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/util/TableEmptyState.js @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import { Bullseye, EmptyState, EmptyStateBody, EmptyStateIcon, Spinner, Title } from '@patternfly/react-core' +import { SearchIcon, CubesIcon } from '@patternfly/react-icons' +import { Status } from '../../shapes' + +function TableEmptyState({ + status, + isFiltering, + loadingTitle = 'Loading', + emptyTitle = 'No results found', + emptyText = 'No results found of this type.', + emptyAction = '', +}) { + if (status === 'loading') { + return ( + + + + + {loadingTitle} + + + + ) + } else if (status === 'error') { + return ( + + + Unable to connect + + + There was an error retrieving data. Check your connection and try again. + + + ) + } else if (status === 'idle') { + return ( + + + + {emptyTitle} + + No results available at this moment. + + ) + } else if (isFiltering) { + return ( + + + + No results found + + + No results match this filter criteria. Remove all filters or clear all filters to show results. + + + ) + } + + return ( + + + + {emptyTitle} + + {emptyText} + {emptyAction} + + ) +} + +TableEmptyState.propTypes = { + status: Status.isRequired, + isFiltering: PropTypes.bool, + loadingTitle: PropTypes.string, + emptyTitle: PropTypes.string, + emptyText: PropTypes.string, + emptyAction: PropTypes.node, +} + +export default TableEmptyState diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/GrayContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/GrayContainer.js deleted file mode 100644 index bac24c8b..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/GrayContainer.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' -import { useDispatch } from 'react-redux' -import { goDownOneInteractionLevel } from '../../../redux/actions/interaction-level' -import GrayLayer from '../../../components/app/map/elements/GrayLayer' - -const GrayContainer = () => { - const dispatch = useDispatch() - const onClick = () => dispatch(goDownOneInteractionLevel()) - return -} - -export default GrayContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js b/opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js deleted file mode 100644 index 91ceb35d..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react' -import { useDispatch } from 'react-redux' -import { setMapDimensions, setMapPositionWithBoundsCheck, zoomInOnPosition } from '../../../redux/actions/map' -import MapStageComponent from '../../../components/app/map/MapStageComponent' -import { useMapDimensions, useMapPosition } from '../../../data/map' - -const MapStage = () => { - const position = useMapPosition() - const dimensions = useMapDimensions() - const dispatch = useDispatch() - const zoomInOnPositionA = (zoomIn, x, y) => dispatch(zoomInOnPosition(zoomIn, x, y)) - const setMapPositionWithBoundsCheckA = (x, y) => dispatch(setMapPositionWithBoundsCheck(x, y)) - const setMapDimensionsA = (width, height) => dispatch(setMapDimensions(width, height)) - - return ( - - ) -} - -export default MapStage diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/RackContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/RackContainer.js deleted file mode 100644 index e5af5117..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/RackContainer.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import RackGroup from '../../../components/app/map/groups/RackGroup' - -const RackContainer = ({ tile }) => { - const interactionLevel = useSelector((state) => state.interactionLevel) - return -} - -export default RackContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/RackEnergyFillContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/RackEnergyFillContainer.js deleted file mode 100644 index d22317a5..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/RackEnergyFillContainer.js +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import RackFillBar from '../../../components/app/map/elements/RackFillBar' - -const RackSpaceFillContainer = (props) => { - const state = useSelector((state) => { - let energyConsumptionTotal = 0 - const rack = state.objects.rack[state.objects.tile[props.tileId].rack] - const machineIds = rack.machines - machineIds.forEach((machineId) => { - if (machineId !== null) { - const machine = state.objects.machine[machineId] - machine.cpus.forEach((id) => (energyConsumptionTotal += state.objects.cpu[id].energyConsumptionW)) - machine.gpus.forEach((id) => (energyConsumptionTotal += state.objects.gpu[id].energyConsumptionW)) - machine.memories.forEach( - (id) => (energyConsumptionTotal += state.objects.memory[id].energyConsumptionW) - ) - machine.storages.forEach( - (id) => (energyConsumptionTotal += state.objects.storage[id].energyConsumptionW) - ) - } - }) - - return { - type: 'energy', - fillFraction: Math.min(1, energyConsumptionTotal / rack.powerCapacityW), - } - }) - return -} - -export default RackSpaceFillContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/RackSpaceFillContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/RackSpaceFillContainer.js deleted file mode 100644 index 8d6f61e0..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/RackSpaceFillContainer.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import RackFillBar from '../../../components/app/map/elements/RackFillBar' - -const RackSpaceFillContainer = (props) => { - const state = useSelector((state) => { - const machineIds = state.objects.rack[state.objects.tile[props.tileId].rack].machines - return { - type: 'space', - fillFraction: machineIds.filter((id) => id !== null).length / machineIds.length, - } - }) - return -} - -export default RackSpaceFillContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/RoomContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/RoomContainer.js deleted file mode 100644 index 0a9e1503..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/RoomContainer.js +++ /dev/null @@ -1,23 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { goFromBuildingToRoom } from '../../../redux/actions/interaction-level' -import RoomGroup from '../../../components/app/map/groups/RoomGroup' - -const RoomContainer = (props) => { - const state = useSelector((state) => { - return { - interactionLevel: state.interactionLevel, - currentRoomInConstruction: state.construction.currentRoomInConstruction, - room: state.objects.room[props.roomId], - } - }) - const dispatch = useDispatch() - return dispatch(goFromBuildingToRoom(props.roomId))} /> -} - -RoomContainer.propTypes = { - roomId: PropTypes.string, -} - -export default RoomContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/TileContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/TileContainer.js deleted file mode 100644 index 50a2abfd..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/TileContainer.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { goFromRoomToRack } from '../../../redux/actions/interaction-level' -import TileGroup from '../../../components/app/map/groups/TileGroup' - -const TileContainer = (props) => { - const interactionLevel = useSelector((state) => state.interactionLevel) - const tile = useSelector((state) => state.objects.tile[props.tileId]) - - const dispatch = useDispatch() - const onClick = (tile) => { - if (tile.rack) { - dispatch(goFromRoomToRack(tile._id)) - } - } - return -} - -export default TileContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/TopologyContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/TopologyContainer.js deleted file mode 100644 index e7ab3c72..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/TopologyContainer.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import TopologyGroup from '../../../components/app/map/groups/TopologyGroup' -import { useActiveTopology } from '../../../data/topology' - -const TopologyContainer = () => { - const topology = useActiveTopology() - const interactionLevel = useSelector((state) => state.interactionLevel) - - return -} - -export default TopologyContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/WallContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/WallContainer.js deleted file mode 100644 index 67f36396..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/WallContainer.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import WallGroup from '../../../components/app/map/groups/WallGroup' - -const WallContainer = (props) => { - const tiles = useSelector((state) => - state.objects.room[props.roomId].tiles.map((tileId) => state.objects.tile[tileId]) - ) - return -} - -export default WallContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/controls/ScaleIndicatorContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/controls/ScaleIndicatorContainer.js deleted file mode 100644 index a10eea22..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/controls/ScaleIndicatorContainer.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import ScaleIndicatorComponent from '../../../../components/app/map/controls/ScaleIndicatorComponent' -import { useMapScale } from '../../../../data/map' - -const ScaleIndicatorContainer = (props) => { - const scale = useMapScale() - return -} - -export default ScaleIndicatorContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/controls/ZoomControlContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/controls/ZoomControlContainer.js deleted file mode 100644 index a39c6077..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/controls/ZoomControlContainer.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react' -import { useDispatch } from 'react-redux' -import { zoomInOnCenter } from '../../../../redux/actions/map' -import ZoomControlComponent from '../../../../components/app/map/controls/ZoomControlComponent' -import { useMapScale } from '../../../../data/map' - -const ZoomControlContainer = () => { - const dispatch = useDispatch() - const scale = useMapScale() - return dispatch(zoomInOnCenter(zoomIn))} /> -} - -export default ZoomControlContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/layers/MapLayer.js b/opendc-web/opendc-web-ui/src/containers/app/map/layers/MapLayer.js deleted file mode 100644 index 633ebcc7..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/layers/MapLayer.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import MapLayerComponent from '../../../../components/app/map/layers/MapLayerComponent' -import { useMapPosition, useMapScale } from '../../../../data/map' - -const MapLayer = (props) => { - const position = useMapPosition() - const scale = useMapScale() - return -} - -export default MapLayer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/layers/ObjectHoverLayer.js b/opendc-web/opendc-web-ui/src/containers/app/map/layers/ObjectHoverLayer.js deleted file mode 100644 index e9a64545..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/layers/ObjectHoverLayer.js +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { addRackToTile } from '../../../../redux/actions/topology/room' -import ObjectHoverLayerComponent from '../../../../components/app/map/layers/ObjectHoverLayerComponent' -import { findTileWithPosition } from '../../../../util/tile-calculations' - -const ObjectHoverLayer = (props) => { - const state = useSelector((state) => { - return { - mapPosition: state.map.position, - mapScale: state.map.scale, - isEnabled: () => state.construction.inRackConstructionMode, - isValid: (x, y) => { - if (state.interactionLevel.mode !== 'ROOM') { - return false - } - - const currentRoom = state.objects.room[state.interactionLevel.roomId] - const tiles = currentRoom.tiles.map((tileId) => state.objects.tile[tileId]) - const tile = findTileWithPosition(tiles, x, y) - - return !(tile === null || tile.rack) - }, - } - }) - - const dispatch = useDispatch() - const onClick = (x, y) => dispatch(addRackToTile(x, y)) - return -} - -export default ObjectHoverLayer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/layers/RoomHoverLayer.js b/opendc-web/opendc-web-ui/src/containers/app/map/layers/RoomHoverLayer.js deleted file mode 100644 index 4070c766..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/map/layers/RoomHoverLayer.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { toggleTileAtLocation } from '../../../../redux/actions/topology/building' -import RoomHoverLayerComponent from '../../../../components/app/map/layers/RoomHoverLayerComponent' -import { - deriveValidNextTilePositions, - findPositionInPositions, - findPositionInRooms, -} from '../../../../util/tile-calculations' - -const RoomHoverLayer = (props) => { - const dispatch = useDispatch() - const onClick = (x, y) => dispatch(toggleTileAtLocation(x, y)) - - const state = useSelector((state) => { - return { - mapPosition: state.map.position, - mapScale: state.map.scale, - isEnabled: () => state.construction.currentRoomInConstruction !== '-1', - isValid: (x, y) => { - const newRoom = Object.assign({}, state.objects.room[state.construction.currentRoomInConstruction]) - const oldRooms = Object.keys(state.objects.room) - .map((id) => Object.assign({}, state.objects.room[id])) - .filter( - (room) => - state.objects.topology[state.currentTopologyId].rooms.indexOf(room._id) !== -1 && - room._id !== state.construction.currentRoomInConstruction - ) - - ;[...oldRooms, newRoom].forEach((room) => { - room.tiles = room.tiles.map((tileId) => state.objects.tile[tileId]) - }) - if (newRoom.tiles.length === 0) { - return findPositionInRooms(oldRooms, x, y) === -1 - } - - const validNextPositions = deriveValidNextTilePositions(oldRooms, newRoom.tiles) - return findPositionInPositions(validNextPositions, x, y) !== -1 - }, - } - }) - return -} - -export default RoomHoverLayer diff --git a/opendc-web/opendc-web-ui/src/containers/app/results/PortfolioResultsContainer.js b/opendc-web/opendc-web-ui/src/containers/app/results/PortfolioResultsContainer.js deleted file mode 100644 index a75f15ae..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/results/PortfolioResultsContainer.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import PortfolioResultsComponent from '../../../components/app/results/PortfolioResultsComponent' -import { useRouter } from 'next/router' -import { usePortfolio, usePortfolioScenarios } from '../../../data/project' - -const PortfolioResultsContainer = (props) => { - const router = useRouter() - const { portfolio: currentPortfolioId } = router.query - const { data: portfolio } = usePortfolio(currentPortfolioId) - const scenarios = usePortfolioScenarios(currentPortfolioId).data ?? [] - return -} - -export default PortfolioResultsContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js deleted file mode 100644 index 60ac666c..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js +++ /dev/null @@ -1,48 +0,0 @@ -import React, { useState } from 'react' -import { useRouter } from 'next/router' -import PortfolioListComponent from '../../../../components/app/sidebars/project/PortfolioListComponent' -import NewPortfolioModalComponent from '../../../../components/modals/custom-components/NewPortfolioModalComponent' -import { useProjectPortfolios } from '../../../../data/project' -import { useMutation } from 'react-query' - -const PortfolioListContainer = () => { - const router = useRouter() - const { project: currentProjectId, portfolio: currentPortfolioId } = router.query - const portfolios = useProjectPortfolios(currentProjectId).data ?? [] - - const { mutate: addPortfolio } = useMutation('addPortfolio') - const { mutateAsync: deletePortfolio } = useMutation('deletePortfolio') - - const [isVisible, setVisible] = useState(false) - const actions = { - onNewPortfolio: () => setVisible(true), - onChoosePortfolio: async (portfolioId) => { - await router.push(`/projects/${currentProjectId}/portfolios/${portfolioId}`) - }, - onDeletePortfolio: async (id) => { - if (id) { - await deletePortfolio(id) - await router.push(`/projects/${currentProjectId}`) - } - }, - } - const callback = (name, targets) => { - if (name) { - addPortfolio({ projectId: currentProjectId, name, targets }) - } - setVisible(false) - } - return ( - <> - - - - ) -} - -export default PortfolioListContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js deleted file mode 100644 index 06c7f0f7..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import { useRouter } from 'next/router' -import ProjectSidebarComponent from '../../../../components/app/sidebars/project/ProjectSidebarComponent' -import { isCollapsible } from '../../../../util/sidebar-space' - -const ProjectSidebarContainer = (props) => { - const router = useRouter() - return -} - -export default ProjectSidebarContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js deleted file mode 100644 index 3b68df38..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js +++ /dev/null @@ -1,67 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useState } from 'react' -import ScenarioListComponent from '../../../../components/app/sidebars/project/ScenarioListComponent' -import NewScenarioModalComponent from '../../../../components/modals/custom-components/NewScenarioModalComponent' -import { useProjectTopologies } from '../../../../data/topology' -import { usePortfolio, usePortfolioScenarios } from '../../../../data/project' -import { useSchedulers, useTraces } from '../../../../data/experiments' -import { useMutation } from 'react-query' - -const ScenarioListContainer = ({ portfolioId }) => { - const { data: portfolio } = usePortfolio(portfolioId) - const scenarios = usePortfolioScenarios(portfolioId).data ?? [] - const topologies = - useProjectTopologies(portfolio?.projectId).data?.map((topology) => ({ - _id: topology._id, - name: topology.name, - })) ?? [] - const traces = useTraces().data ?? [] - const schedulers = useSchedulers().data ?? [] - - const { mutate: addScenario } = useMutation('addScenario') - const { mutate: deleteScenario } = useMutation('deleteScenario') - - const [isVisible, setVisible] = useState(false) - - const onNewScenario = () => setVisible(true) - const onDeleteScenario = (id) => id && deleteScenario(id) - const callback = (name, portfolioId, trace, topology, operational) => { - if (name) { - addScenario({ - portfolioId, - name, - trace, - topology, - operational, - }) - } - - setVisible(false) - } - - return ( - <> - - s._id)} - traces={traces} - schedulers={schedulers} - topologies={topologies} - callback={callback} - /> - - ) -} - -ScenarioListContainer.propTypes = { - portfolioId: PropTypes.string, -} - -export default ScenarioListContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js deleted file mode 100644 index a2244a30..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js +++ /dev/null @@ -1,67 +0,0 @@ -import React, { useState } from 'react' -import { useDispatch } from 'react-redux' -import TopologyListComponent from '../../../../components/app/sidebars/project/TopologyListComponent' -import { setCurrentTopology } from '../../../../redux/actions/topology/building' -import { useRouter } from 'next/router' -import { addTopology } from '../../../../redux/actions/topologies' -import NewTopologyModalComponent from '../../../../components/modals/custom-components/NewTopologyModalComponent' -import { useActiveTopology, useProjectTopologies } from '../../../../data/topology' -import { useMutation } from 'react-query' - -const TopologyListContainer = () => { - const dispatch = useDispatch() - const router = useRouter() - const { project: currentProjectId } = router.query - const topologies = - useProjectTopologies(currentProjectId).data?.map((topology) => ({ _id: topology._id, name: topology.name })) ?? - [] - const currentTopologyId = useActiveTopology()?._id - const [isVisible, setVisible] = useState(false) - - const { mutate: deleteTopology } = useMutation('deleteTopology') - - const onChooseTopology = async (id) => { - dispatch(setCurrentTopology(id)) - await router.push(`/projects/${currentProjectId}/topologies/${id}`) - } - const onDeleteTopology = async (id) => { - if (id) { - deleteTopology(id) - await router.push(`/projects/${currentProjectId}`) - } - } - const onCreateTopology = (name) => { - if (name) { - dispatch(addTopology(currentProjectId, name, undefined)) - } - setVisible(false) - } - const onDuplicateTopology = (name, id) => { - if (name) { - dispatch(addTopology(currentProjectId, name, id)) - } - setVisible(false) - } - const onCancel = () => setVisible(false) - - return ( - <> - setVisible(true)} - onDeleteTopology={onDeleteTopology} - /> - - - ) -} - -export default TopologyListContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/TopologySidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/TopologySidebarContainer.js deleted file mode 100644 index 42c81c65..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/TopologySidebarContainer.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import TopologySidebarComponent from '../../../../components/app/sidebars/topology/TopologySidebarComponent' - -const TopologySidebarContainer = (props) => { - const interactionLevel = useSelector((state) => state.interactionLevel) - return -} - -export default TopologySidebarContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/BuildingSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/BuildingSidebarContainer.js deleted file mode 100644 index a0b52e56..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/BuildingSidebarContainer.js +++ /dev/null @@ -1,5 +0,0 @@ -import BuildingSidebarComponent from '../../../../../components/app/sidebars/topology/building/BuildingSidebarComponent' - -const BuildingSidebarContainer = BuildingSidebarComponent - -export default BuildingSidebarContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/NewRoomConstructionContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/NewRoomConstructionContainer.js deleted file mode 100644 index 96f42a44..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/NewRoomConstructionContainer.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { - cancelNewRoomConstruction, - finishNewRoomConstruction, - startNewRoomConstruction, -} from '../../../../../redux/actions/topology/building' -import StartNewRoomConstructionComponent from '../../../../../components/app/sidebars/topology/building/NewRoomConstructionComponent' - -const NewRoomConstructionButton = (props) => { - const currentRoomInConstruction = useSelector((state) => state.construction.currentRoomInConstruction) - - const dispatch = useDispatch() - const actions = { - onStart: () => dispatch(startNewRoomConstruction()), - onFinish: () => dispatch(finishNewRoomConstruction()), - onCancel: () => dispatch(cancelNewRoomConstruction()), - } - return ( - - ) -} - -export default NewRoomConstructionButton diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/BackToRackContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/BackToRackContainer.js deleted file mode 100644 index ea250767..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/BackToRackContainer.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import { useDispatch } from 'react-redux' -import { goDownOneInteractionLevel } from '../../../../../redux/actions/interaction-level' -import BackToRackComponent from '../../../../../components/app/sidebars/topology/machine/BackToRackComponent' - -const BackToRackContainer = (props) => { - const dispatch = useDispatch() - return dispatch(goDownOneInteractionLevel())} /> -} - -export default BackToRackContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/DeleteMachineContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/DeleteMachineContainer.js deleted file mode 100644 index 54e406f4..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/DeleteMachineContainer.js +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState } from 'react' -import { useDispatch } from 'react-redux' -import ConfirmationModal from '../../../../../components/modals/ConfirmationModal' -import { deleteMachine } from '../../../../../redux/actions/topology/machine' -import { Button } from 'reactstrap' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faTrash } from '@fortawesome/free-solid-svg-icons' - -const DeleteMachineContainer = () => { - const dispatch = useDispatch() - const [isVisible, setVisible] = useState(false) - const callback = (isConfirmed) => { - if (isConfirmed) { - dispatch(deleteMachine()) - } - setVisible(false) - } - return ( - <> - - - - ) -} - -export default DeleteMachineContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineNameContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineNameContainer.js deleted file mode 100644 index 9cbb32c5..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineNameContainer.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' - -const MachineNameContainer = () => { - const position = useSelector((state) => state.interactionLevel.position) - return

    Machine at slot {position}

    -} - -export default MachineNameContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineSidebarContainer.js deleted file mode 100644 index 7553c2fe..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineSidebarContainer.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import MachineSidebarComponent from '../../../../../components/app/sidebars/topology/machine/MachineSidebarComponent' - -const MachineSidebarContainer = (props) => { - const machineId = useSelector( - (state) => - state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rack].machines[ - state.interactionLevel.position - 1 - ] - ) - return -} - -export default MachineSidebarContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitAddContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitAddContainer.js deleted file mode 100644 index 0f85aa76..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitAddContainer.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { addUnit } from '../../../../../redux/actions/topology/machine' -import UnitAddComponent from '../../../../../components/app/sidebars/topology/machine/UnitAddComponent' - -const UnitAddContainer = (props) => { - const units = useSelector((state) => Object.values(state.objects[props.unitType])) - const dispatch = useDispatch() - - const onAdd = (id) => dispatch(addUnit(props.unitType, id)) - - return -} - -export default UnitAddContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitListContainer.js deleted file mode 100644 index cdd7e268..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitListContainer.js +++ /dev/null @@ -1,34 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import UnitListComponent from '../../../../../components/app/sidebars/topology/machine/UnitListComponent' -import { deleteUnit } from '../../../../../redux/actions/topology/machine' - -const unitMapping = { - cpu: 'cpus', - gpu: 'gpus', - memory: 'memories', - storage: 'storages', -} - -const UnitListContainer = ({ unitType, ...props }) => { - const dispatch = useDispatch() - const units = useSelector((state) => { - const machine = - state.objects.machine[ - state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rack].machines[ - state.interactionLevel.position - 1 - ] - ] - return machine[unitMapping[unitType]].map((id) => state.objects[unitType][id]) - }) - const onDelete = (unit, unitType) => dispatch(deleteUnit(unitType, unit._id)) - - return -} - -UnitListContainer.propTypes = { - unitType: PropTypes.string.isRequired, -} - -export default UnitListContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitTabsContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitTabsContainer.js deleted file mode 100644 index 00fe4067..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitTabsContainer.js +++ /dev/null @@ -1,5 +0,0 @@ -import UnitTabsComponent from '../../../../../components/app/sidebars/topology/machine/UnitTabsComponent' - -const UnitTabsContainer = UnitTabsComponent - -export default UnitTabsContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/AddPrefabContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/AddPrefabContainer.js deleted file mode 100644 index c2a0fc48..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/AddPrefabContainer.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import { useDispatch } from 'react-redux' -import { addPrefab } from '../../../../../redux/actions/prefabs' -import AddPrefabComponent from '../../../../../components/app/sidebars/topology/rack/AddPrefabComponent' - -const AddPrefabContainer = (props) => { - const dispatch = useDispatch() - return dispatch(addPrefab('name'))} /> -} - -export default AddPrefabContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/BackToRoomContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/BackToRoomContainer.js deleted file mode 100644 index a98728a6..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/BackToRoomContainer.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import { useDispatch } from 'react-redux' -import { goDownOneInteractionLevel } from '../../../../../redux/actions/interaction-level' -import BackToRoomComponent from '../../../../../components/app/sidebars/topology/rack/BackToRoomComponent' - -const BackToRoomContainer = (props) => { - const dispatch = useDispatch() - return dispatch(goDownOneInteractionLevel())} /> -} - -export default BackToRoomContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/DeleteRackContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/DeleteRackContainer.js deleted file mode 100644 index 4463530e..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/DeleteRackContainer.js +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState } from 'react' -import { useDispatch } from 'react-redux' -import ConfirmationModal from '../../../../../components/modals/ConfirmationModal' -import { deleteRack } from '../../../../../redux/actions/topology/rack' -import { Button } from 'reactstrap' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faTrash } from '@fortawesome/free-solid-svg-icons' - -const DeleteRackContainer = () => { - const dispatch = useDispatch() - const [isVisible, setVisible] = useState(false) - const callback = (isConfirmed) => { - if (isConfirmed) { - dispatch(deleteRack()) - } - setVisible(false) - } - return ( - <> - - - - ) -} - -export default DeleteRackContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineListContainer.js deleted file mode 100644 index 2118d915..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineListContainer.js +++ /dev/null @@ -1,29 +0,0 @@ -import React, { useMemo } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import MachineListComponent from '../../../../../components/app/sidebars/topology/rack/MachineListComponent' -import { goFromRackToMachine } from '../../../../../redux/actions/interaction-level' -import { addMachine } from '../../../../../redux/actions/topology/rack' - -const MachineListContainer = (props) => { - const rack = useSelector((state) => state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rack]) - const machines = useSelector((state) => rack.machines.map((id) => state.objects.machine[id])) - const machinesNull = useMemo(() => { - const res = Array(rack.capacity).fill(null) - for (const machine of machines) { - res[machine.position - 1] = machine - } - return res - }, [rack, machines]) - const dispatch = useDispatch() - - return ( - dispatch(addMachine(index))} - onSelect={(index) => dispatch(goFromRackToMachine(index))} - /> - ) -} - -export default MachineListContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackNameContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackNameContainer.js deleted file mode 100644 index 2c39cf9f..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackNameContainer.js +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import NameComponent from '../../../../../components/app/sidebars/topology/NameComponent' -import TextInputModal from '../../../../../components/modals/TextInputModal' -import { editRackName } from '../../../../../redux/actions/topology/rack' - -const RackNameContainer = () => { - const [isVisible, setVisible] = useState(false) - const rackName = useSelector( - (state) => state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rack].name - ) - const dispatch = useDispatch() - const callback = (name) => { - if (name) { - dispatch(editRackName(name)) - } - setVisible(false) - } - return ( - <> - setVisible(true)} /> - - - ) -} - -export default RackNameContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackSidebarContainer.js deleted file mode 100644 index 34777125..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackSidebarContainer.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import RackSidebarComponent from '../../../../../components/app/sidebars/topology/rack/RackSidebarComponent' - -const RackSidebarContainer = (props) => { - const rackId = useSelector((state) => state.objects.tile[state.interactionLevel.tileId].rack) - return -} - -export default RackSidebarContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/BackToBuildingContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/BackToBuildingContainer.js deleted file mode 100644 index 9fa1e95f..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/BackToBuildingContainer.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' -import { useDispatch } from 'react-redux' -import { goDownOneInteractionLevel } from '../../../../../redux/actions/interaction-level' -import BackToBuildingComponent from '../../../../../components/app/sidebars/topology/room/BackToBuildingComponent' - -const BackToBuildingContainer = () => { - const dispatch = useDispatch() - const onClick = () => dispatch(goDownOneInteractionLevel()) - return -} - -export default BackToBuildingContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/DeleteRoomContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/DeleteRoomContainer.js deleted file mode 100644 index 0fbbb036..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/DeleteRoomContainer.js +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState } from 'react' -import { useDispatch } from 'react-redux' -import { Button } from 'reactstrap' -import ConfirmationModal from '../../../../../components/modals/ConfirmationModal' -import { deleteRoom } from '../../../../../redux/actions/topology/room' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faTrash } from '@fortawesome/free-solid-svg-icons' - -const DeleteRoomContainer = () => { - const dispatch = useDispatch() - const [isVisible, setVisible] = useState(false) - const callback = (isConfirmed) => { - if (isConfirmed) { - dispatch(deleteRoom()) - } - setVisible(false) - } - return ( - <> - - - - ) -} - -export default DeleteRoomContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/EditRoomContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/EditRoomContainer.js deleted file mode 100644 index ec4f586b..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/EditRoomContainer.js +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { finishRoomEdit, startRoomEdit } from '../../../../../redux/actions/topology/building' -import { Button } from 'reactstrap' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faCheck, faPencilAlt } from '@fortawesome/free-solid-svg-icons' - -const EditRoomContainer = () => { - const isEditing = useSelector((state) => state.construction.currentRoomInConstruction !== '-1') - const isInRackConstructionMode = useSelector((state) => state.construction.inRackConstructionMode) - - const dispatch = useDispatch() - const onEdit = () => dispatch(startRoomEdit()) - const onFinish = () => dispatch(finishRoomEdit()) - - return isEditing ? ( - - ) : ( - - ) -} - -export default EditRoomContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RackConstructionContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RackConstructionContainer.js deleted file mode 100644 index 79584e98..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RackConstructionContainer.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { startRackConstruction, stopRackConstruction } from '../../../../../redux/actions/topology/room' -import RackConstructionComponent from '../../../../../components/app/sidebars/topology/room/RackConstructionComponent' - -const RackConstructionContainer = (props) => { - const isRackConstructionMode = useSelector((state) => state.construction.inRackConstructionMode) - const isEditingRoom = useSelector((state) => state.construction.currentRoomInConstruction !== '-1') - - const dispatch = useDispatch() - const onStart = () => dispatch(startRackConstruction()) - const onStop = () => dispatch(stopRackConstruction()) - return ( - - ) -} - -export default RackConstructionContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomNameContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomNameContainer.js deleted file mode 100644 index 3b35a849..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomNameContainer.js +++ /dev/null @@ -1,31 +0,0 @@ -import React, { useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import NameComponent from '../../../../../components/app/sidebars/topology/NameComponent' -import TextInputModal from '../../../../../components/modals/TextInputModal' -import { editRoomName } from '../../../../../redux/actions/topology/room' - -const RoomNameContainer = () => { - const [isVisible, setVisible] = useState(false) - const roomName = useSelector((state) => state.objects.room[state.interactionLevel.roomId].name) - const dispatch = useDispatch() - const callback = (name) => { - if (name) { - dispatch(editRoomName(name)) - } - setVisible(false) - } - return ( - <> - setVisible(true)} /> - - - ) -} - -export default RoomNameContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomSidebarContainer.js deleted file mode 100644 index 252881a0..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomSidebarContainer.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import RoomSidebarComponent from '../../../../../components/app/sidebars/topology/room/RoomSidebarComponent' - -const RoomSidebarContainer = (props) => { - const roomId = useSelector((state) => state.interactionLevel.roomId) - return -} - -export default RoomSidebarContainer diff --git a/opendc-web/opendc-web-ui/src/containers/auth/Login.js b/opendc-web/opendc-web-ui/src/containers/auth/Login.js deleted file mode 100644 index d8083d89..00000000 --- a/opendc-web/opendc-web-ui/src/containers/auth/Login.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import { Button } from 'reactstrap' -import { useAuth } from '../../auth' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faSignInAlt } from '@fortawesome/free-solid-svg-icons' - -function Login({ visible, className }) { - const { loginWithRedirect } = useAuth() - - if (!visible) { - return - } - - return ( - - ) -} - -export default Login diff --git a/opendc-web/opendc-web-ui/src/containers/auth/Logout.js b/opendc-web/opendc-web-ui/src/containers/auth/Logout.js deleted file mode 100644 index 37705c5d..00000000 --- a/opendc-web/opendc-web-ui/src/containers/auth/Logout.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import LogoutButton from '../../components/navigation/LogoutButton' -import { useAuth } from '../../auth' - -const Logout = (props) => { - const { logout } = useAuth() - return logout({ returnTo: window.location.origin })} /> -} - -export default Logout diff --git a/opendc-web/opendc-web-ui/src/containers/auth/ProfileName.js b/opendc-web/opendc-web-ui/src/containers/auth/ProfileName.js deleted file mode 100644 index 70f5b884..00000000 --- a/opendc-web/opendc-web-ui/src/containers/auth/ProfileName.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' -import { useAuth } from '../../auth' - -function ProfileName() { - const { isLoading, user } = useAuth() - return isLoading ? Loading... : {user.name} -} - -export default ProfileName diff --git a/opendc-web/opendc-web-ui/src/containers/navigation/AppNavbarContainer.js b/opendc-web/opendc-web-ui/src/containers/navigation/AppNavbarContainer.js deleted file mode 100644 index ff9f9fe7..00000000 --- a/opendc-web/opendc-web-ui/src/containers/navigation/AppNavbarContainer.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import AppNavbarComponent from '../../components/navigation/AppNavbarComponent' -import { useActiveProjectId, useProject } from '../../data/project' - -const AppNavbarContainer = (props) => { - const projectId = useActiveProjectId() - const { data: project } = useProject(projectId) - return -} - -export default AppNavbarContainer diff --git a/opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js b/opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js deleted file mode 100644 index ac0edae4..00000000 --- a/opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState } from 'react' -import TextInputModal from '../../components/modals/TextInputModal' -import { Button } from 'reactstrap' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faPlus } from '@fortawesome/free-solid-svg-icons' -import { useMutation } from 'react-query' - -/** - * A container for creating a new project. - */ -const NewProjectContainer = () => { - const [isVisible, setVisible] = useState(false) - const { mutate: addProject } = useMutation('addProject') - const callback = (text) => { - if (text) { - addProject({ name: text }) - } - setVisible(false) - } - - return ( - <> -
    - -
    - - - ) -} - -export default NewProjectContainer diff --git a/opendc-web/opendc-web-ui/src/containers/projects/ProjectActions.js b/opendc-web/opendc-web-ui/src/containers/projects/ProjectActions.js deleted file mode 100644 index 62985742..00000000 --- a/opendc-web/opendc-web-ui/src/containers/projects/ProjectActions.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import ProjectActionButtons from '../../components/projects/ProjectActionButtons' -import { useMutation } from 'react-query' - -const ProjectActions = (props) => { - const { mutate: deleteProject } = useMutation('deleteProject') - const actions = { - onViewUsers: (id) => {}, // TODO implement user viewing - onDelete: (id) => deleteProject(id), - } - return -} - -export default ProjectActions diff --git a/opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js b/opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js deleted file mode 100644 index b5c5dd68..00000000 --- a/opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import ProjectList from '../../components/projects/ProjectList' -import { useAuth } from '../../auth' -import { useProjects } from '../../data/project' - -const getVisibleProjects = (projects, filter, userId) => { - switch (filter) { - case 'SHOW_ALL': - return projects - case 'SHOW_OWN': - return projects.filter((project) => - project.authorizations.some((a) => a.userId === userId && a.level === 'OWN') - ) - case 'SHOW_SHARED': - return projects.filter((project) => - project.authorizations.some((a) => a.userId === userId && a.level !== 'OWN') - ) - default: - return projects - } -} - -const ProjectListContainer = ({ filter }) => { - const { user } = useAuth() - const { data: projects } = useProjects() - return -} - -ProjectListContainer.propTypes = { - filter: PropTypes.string.isRequired, -} - -export default ProjectListContainer diff --git a/opendc-web/opendc-web-ui/src/data/map.js b/opendc-web/opendc-web-ui/src/data/map.js index 6aef6ac5..348a6664 100644 --- a/opendc-web/opendc-web-ui/src/data/map.js +++ b/opendc-web/opendc-web-ui/src/data/map.js @@ -35,7 +35,3 @@ export function useMapScale() { export function useMapPosition() { return useSelector((state) => state.map.position) } - -export function useMapDimensions() { - return useSelector((state) => state.map.dimensions) -} diff --git a/opendc-web/opendc-web-ui/src/data/project.js b/opendc-web/opendc-web-ui/src/data/project.js index 9bdcfb93..9dcd8532 100644 --- a/opendc-web/opendc-web-ui/src/data/project.js +++ b/opendc-web/opendc-web-ui/src/data/project.js @@ -20,9 +20,8 @@ * SOFTWARE. */ -import { useQueries, useQuery } from 'react-query' +import { useQuery } from 'react-query' import { addProject, deleteProject, fetchProject, fetchProjects } from '../api/projects' -import { useRouter } from 'next/router' import { addPortfolio, deletePortfolio, fetchPortfolio, fetchPortfoliosOfProject } from '../api/portfolios' import { addScenario, deleteScenario, fetchScenario, fetchScenariosOfPortfolio } from '../api/scenarios' @@ -38,6 +37,7 @@ export function configureProjectClient(queryClient, auth) { mutationFn: (data) => addProject(auth, data), onSuccess: async (result) => { queryClient.setQueryData('projects', (old = []) => [...old, result]) + queryClient.setQueryData(['projects', result._id], result) }, }) queryClient.setMutationDefaults('deleteProject', { @@ -61,6 +61,7 @@ export function configureProjectClient(queryClient, auth) { ...old, portfolioIds: [...old.portfolioIds, result._id], })) + queryClient.setQueryData(['project-portfolios', result.projectId], (old = []) => [...old, result]) queryClient.setQueryData(['portfolios', result._id], result) }, }) @@ -71,6 +72,9 @@ export function configureProjectClient(queryClient, auth) { ...old, portfolioIds: old.portfolioIds.filter((id) => id !== result._id), })) + queryClient.setQueryData(['project-portfolios', result.projectId], (old = []) => + old.filter((portfolio) => portfolio._id !== result._id) + ) queryClient.removeQueries(['portfolios', result._id]) }, }) @@ -86,6 +90,7 @@ export function configureProjectClient(queryClient, auth) { onSuccess: async (result) => { // Register updated scenario in cache queryClient.setQueryData(['scenarios', result._id], result) + queryClient.setQueryData(['portfolio-scenarios', result.portfolioId], (old = []) => [...old, result]) // Add scenario id to portfolio queryClient.setQueryData(['portfolios', result.portfolioId], (old) => ({ @@ -101,6 +106,9 @@ export function configureProjectClient(queryClient, auth) { ...old, scenarioIds: old.scenarioIds.filter((id) => id !== result._id), })) + queryClient.setQueryData(['portfolio-scenarios', result.portfolioId], (old = []) => + old.filter((scenario) => scenario._id !== result._id) + ) queryClient.removeQueries(['scenarios', result._id]) }, }) @@ -109,54 +117,34 @@ export function configureProjectClient(queryClient, auth) { /** * Return the available projects. */ -export function useProjects() { - return useQuery('projects') +export function useProjects(options = {}) { + return useQuery('projects', options) } /** * Return the project with the specified identifier. */ -export function useProject(projectId) { - return useQuery(['projects', projectId], { enabled: !!projectId }) +export function useProject(projectId, options = {}) { + return useQuery(['projects', projectId], { enabled: !!projectId, ...options }) } /** * Return the portfolio with the specified identifier. */ -export function usePortfolio(portfolioId) { - return useQuery(['portfolios', portfolioId], { enabled: !!portfolioId }) +export function usePortfolio(portfolioId, options = {}) { + return useQuery(['portfolios', portfolioId], { enabled: !!portfolioId, ...options }) } /** * Return the portfolios of the specified project. */ -export function useProjectPortfolios(projectId) { - return useQuery(['project-portfolios', projectId], { enabled: !!projectId }) -} - -/** - * Return the scenarios with the specified identifiers. - */ -export function useScenarios(scenarioIds) { - return useQueries( - scenarioIds.map((scenarioId) => ({ - queryKey: ['scenarios', scenarioId], - })) - ) +export function useProjectPortfolios(projectId, options = {}) { + return useQuery(['project-portfolios', projectId], { enabled: !!projectId, ...options }) } /** * Return the scenarios of the specified portfolio. */ -export function usePortfolioScenarios(portfolioId) { - return useQuery(['portfolio-scenarios', portfolioId], { enabled: !!portfolioId }) -} - -/** - * Return the current active project identifier. - */ -export function useActiveProjectId() { - const router = useRouter() - const { project } = router.query - return project +export function usePortfolioScenarios(portfolioId, options = {}) { + return useQuery(['portfolio-scenarios', portfolioId], { enabled: !!portfolioId, ...options }) } diff --git a/opendc-web/opendc-web-ui/src/data/topology.js b/opendc-web/opendc-web-ui/src/data/topology.js index 8db75877..14bd7562 100644 --- a/opendc-web/opendc-web-ui/src/data/topology.js +++ b/opendc-web/opendc-web-ui/src/data/topology.js @@ -21,7 +21,7 @@ */ import { useSelector } from 'react-redux' -import { useQueries, useQuery } from 'react-query' +import { useQuery } from 'react-query' import { addTopology, deleteTopology, fetchTopologiesOfProject, fetchTopology, updateTopology } from '../api/topologies' /** @@ -40,6 +40,7 @@ export function configureTopologyClient(queryClient, auth) { ...old, topologyIds: [...old.topologyIds, result._id], })) + queryClient.setQueryData(['project-topologies', result.projectId], (old = []) => [...old, result]) queryClient.setQueryData(['topologies', result._id], result) }, }) @@ -54,6 +55,9 @@ export function configureTopologyClient(queryClient, auth) { ...old, topologyIds: old.topologyIds.filter((id) => id !== result._id), })) + queryClient.setQueryData(['project-topologies', result.projectId], (old = []) => + old.filter((topology) => topology._id !== result._id) + ) queryClient.removeQueries(['topologies', result._id]) }, }) @@ -69,6 +73,6 @@ export function useActiveTopology() { /** * Return the topologies of the specified project. */ -export function useProjectTopologies(projectId) { - return useQuery(['project-topologies', projectId], { enabled: !!projectId }) +export function useProjectTopologies(projectId, options = {}) { + return useQuery(['project-topologies', projectId], { enabled: !!projectId, ...options }) } diff --git a/opendc-web/opendc-web-ui/src/index.scss b/opendc-web/opendc-web-ui/src/index.scss deleted file mode 100644 index dbd9550c..00000000 --- a/opendc-web/opendc-web-ui/src/index.scss +++ /dev/null @@ -1,68 +0,0 @@ -@import '~bootstrap/scss/bootstrap'; - -@import './style/_mixins.scss'; -@import './style/_variables.scss'; - -html, -body, -#__next { - margin: 0; - padding: 0; - width: 100%; - height: 100%; - - font-family: Roboto, Helvetica, Verdana, sans-serif; - background: #eee; - - // Scroll padding for top navbar - scroll-padding-top: 60px; -} - -.full-height { - position: relative; - height: 100% !important; -} - -.page-container { - padding-top: 60px; -} - -.text-page-container { - padding-top: 80px; - display: flex; - flex-flow: column; -} - -.vertically-expanding-container { - flex: 1 1 auto; - overflow-y: auto; -} - -.bottom-btn-container { - flex: 0 1 auto; - padding: 20px 0; -} - -.btn, -.list-group-item-action, -.clickable { - @include clickable; -} - -.btn-circle { - border-radius: 50%; -} - -a, -a:hover { - text-decoration: none; -} - -.app-page-container { - padding-left: $side-bar-width; - padding-top: 15px; -} - -.w-70 { - width: 70% !important; -} diff --git a/opendc-web/opendc-web-ui/src/pages/404.js b/opendc-web/opendc-web-ui/src/pages/404.js index cc9281fc..0939bc56 100644 --- a/opendc-web/opendc-web-ui/src/pages/404.js +++ b/opendc-web/opendc-web-ui/src/pages/404.js @@ -1,18 +1,37 @@ import React from 'react' import Head from 'next/head' -import TerminalWindow from '../components/not-found/TerminalWindow' -import style from './404.module.scss' +import { AppPage } from '../components/AppPage' +import { + Bullseye, + EmptyState, + EmptyStateBody, + EmptyStateIcon, + PageSection, + PageSectionVariants, + Title, +} from '@patternfly/react-core' +import { UnknownIcon } from '@patternfly/react-icons' const NotFound = () => { return ( - <> + Page Not Found - OpenDC -
    - -
    - + + + + + + 404: That page does not exist + + + The requested page is not found. Try refreshing the page if it was recently added. + + + + +
    ) } diff --git a/opendc-web/opendc-web-ui/src/pages/404.module.scss b/opendc-web/opendc-web-ui/src/pages/404.module.scss deleted file mode 100644 index e91c2780..00000000 --- a/opendc-web/opendc-web-ui/src/pages/404.module.scss +++ /dev/null @@ -1,8 +0,0 @@ -.not-found-backdrop { - display: flex; - - width: 100%; - height: 100%; - - background-image: linear-gradient(135deg, #00678a, #008fbf, #00a6d6); -} diff --git a/opendc-web/opendc-web-ui/src/pages/_app.js b/opendc-web/opendc-web-ui/src/pages/_app.js index 78402323..028dab10 100644 --- a/opendc-web/opendc-web-ui/src/pages/_app.js +++ b/opendc-web/opendc-web-ui/src/pages/_app.js @@ -24,8 +24,6 @@ import PropTypes from 'prop-types' import Head from 'next/head' import { Provider } from 'react-redux' import { useStore } from '../redux' -import '../index.scss' -import '@patternfly/react-core/dist/styles/base.css' import { AuthProvider, useAuth } from '../auth' import * as Sentry from '@sentry/react' import { Integrations } from '@sentry/tracing' @@ -35,6 +33,19 @@ import { configureProjectClient } from '../data/project' import { configureExperimentClient } from '../data/experiments' import { configureTopologyClient } from '../data/topology' +import '@patternfly/react-core/dist/styles/base.css' +import '@patternfly/react-styles/css/utilities/Alignment/alignment.css' +import '@patternfly/react-styles/css/utilities/BackgroundColor/BackgroundColor.css' +import '@patternfly/react-styles/css/utilities/BoxShadow/box-shadow.css' +import '@patternfly/react-styles/css/utilities/Display/display.css' +import '@patternfly/react-styles/css/utilities/Flex/flex.css' +import '@patternfly/react-styles/css/utilities/Float/float.css' +import '@patternfly/react-styles/css/utilities/Sizing/sizing.css' +import '@patternfly/react-styles/css/utilities/Spacing/spacing.css' +import '@patternfly/react-styles/css/utilities/Text/text.css' +import '@patternfly/react-styles/css/components/InlineEdit/inline-edit.css' +import '../style/index.scss' + // This setup is necessary to forward the Auth0 context to the Redux context const Inner = ({ Component, pageProps }) => { const auth = useAuth() diff --git a/opendc-web/opendc-web-ui/src/pages/logout.js b/opendc-web/opendc-web-ui/src/pages/logout.js index e96e0605..38d5968e 100644 --- a/opendc-web/opendc-web-ui/src/pages/logout.js +++ b/opendc-web/opendc-web-ui/src/pages/logout.js @@ -22,17 +22,17 @@ import React from 'react' import Head from 'next/head' -import AppNavbarContainer from '../containers/navigation/AppNavbarContainer' +import { AppPage } from '../components/AppPage' +import { PageSection, PageSectionVariants } from '@patternfly/react-core' function Logout() { return ( - <> + Logged Out - OpenDC - - Logged out successfully - + Logged out successfully + ) } diff --git a/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js b/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js index cce887aa..c6ded12b 100644 --- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js +++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js @@ -20,6 +20,113 @@ * SOFTWARE. */ -import Topology from './topologies/[topology]' +import { useRouter } from 'next/router' +import { useProject } from '../../../data/project' +import { AppPage } from '../../../components/AppPage' +import Head from 'next/head' +import { + Breadcrumb, + BreadcrumbItem, + Button, + Card, + CardActions, + CardBody, + CardHeader, + CardTitle, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, + Grid, + GridItem, + PageSection, + PageSectionVariants, + Skeleton, + Text, + TextContent, +} from '@patternfly/react-core' +import BreadcrumbLink from '../../../components/util/BreadcrumbLink' +import PortfolioTable from '../../../components/projects/PortfolioTable' +import TopologyTable from '../../../components/projects/TopologyTable' +import NewTopology from '../../../components/projects/NewTopology' +import NewPortfolio from '../../../components/projects/NewPortfolio' -export default Topology +function Project() { + const router = useRouter() + const { project: projectId } = router.query + + const { data: project } = useProject(projectId) + + const breadcrumb = ( + + + Projects + + + Project details + + + ) + + return ( + + + {project?.name ?? 'Project'} - OpenDC + + + + + {project?.name ?? } + + + + + + + + Details + + + + Name + + {project?.name ?? } + + + + + + + + + + + + + Topologies + + + + + + + + + + + + + Portfolios + + + + + + + + + + ) +} + +export default Project diff --git a/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js b/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js index d3d61271..55bee445 100644 --- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js +++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js @@ -22,12 +22,41 @@ import { useRouter } from 'next/router' import Head from 'next/head' -import AppNavbarContainer from '../../../../containers/navigation/AppNavbarContainer' -import React from 'react' -import { useProject } from '../../../../data/project' -import ProjectSidebarContainer from '../../../../containers/app/sidebars/project/ProjectSidebarContainer' -import PortfolioResultsContainer from '../../../../containers/app/results/PortfolioResultsContainer' -import { useDispatch } from 'react-redux' +import React, { useRef } from 'react' +import { usePortfolio, useProject } from '../../../../data/project' +import { + Breadcrumb, + BreadcrumbItem, + Card, + CardActions, + CardBody, + CardHeader, + CardTitle, + Chip, + ChipGroup, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, + Divider, + Grid, + GridItem, + PageSection, + PageSectionVariants, + Skeleton, + Tab, + TabContent, + Tabs, + TabTitleText, + Text, + TextContent, +} from '@patternfly/react-core' +import { AppPage } from '../../../../components/AppPage' +import BreadcrumbLink from '../../../../components/util/BreadcrumbLink' +import ScenarioTable from '../../../../components/projects/ScenarioTable' +import NewScenario from '../../../../components/projects/NewScenario' +import { METRIC_NAMES } from '../../../../util/available-metrics' +import PortfolioResults from '../../../../components/app/results/PortfolioResults' /** * Page that displays the results in a portfolio. @@ -36,24 +65,127 @@ function Portfolio() { const router = useRouter() const { project: projectId, portfolio: portfolioId } = router.query - const project = useProject(projectId) - const title = project?.name ? project?.name + ' - OpenDC' : 'Simulation - OpenDC' + const { data: project } = useProject(projectId) + const { data: portfolio } = usePortfolio(portfolioId) - const dispatch = useDispatch() + const overviewRef = useRef(null) + const resultsRef = useRef(null) + + const breadcrumb = ( + + + Projects + + + Project details + + + Portfolio + + + ) return ( -
    + - {title} + {project?.name ?? 'Portfolios'} - OpenDC - -
    - -
    - -
    -
    -
    + + + Portfolio + + + + + + Overview} + tabContentId="overview" + tabContentRef={overviewRef} + /> + Results} + tabContentId="results" + tabContentRef={resultsRef} + /> + + + + + + + + Details + + + + Name + + {portfolio?.name ?? } + + + + Scenarios + + {portfolio?.scenarioIds.length ?? ( + + )} + + + + Metrics + + {portfolio?.targets?.enabledMetrics ? ( + portfolio.targets.enabledMetrics.length > 0 ? ( + + {portfolio.targets.enabledMetrics.map((metric) => ( + + {METRIC_NAMES[metric]} + + ))} + + ) : ( + 'No metrics enabled' + ) + ) : ( + + )} + + + + Repeats per Scenario + + {portfolio?.targets?.repeatsPerScenario ?? ( + + )} + + + + + + + + + + + + + Scenarios + + + + + + + + + + + ) } diff --git a/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js b/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js index a9dfdb19..5873ed11 100644 --- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js +++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js @@ -23,18 +23,29 @@ import { useRouter } from 'next/router' import { useProject } from '../../../../data/project' import { useDispatch, useSelector } from 'react-redux' -import React, { useEffect } from 'react' +import React, { useEffect, useState } from 'react' import { HotKeys } from 'react-hotkeys' import { KeymapConfiguration } from '../../../../hotkeys' import Head from 'next/head' -import AppNavbarContainer from '../../../../containers/navigation/AppNavbarContainer' -import LoadingScreen from '../../../../components/app/map/LoadingScreen' -import MapStage from '../../../../containers/app/map/MapStage' -import ScaleIndicatorContainer from '../../../../containers/app/map/controls/ScaleIndicatorContainer' -import ToolPanelComponent from '../../../../components/app/map/controls/ToolPanelComponent' -import ProjectSidebarContainer from '../../../../containers/app/sidebars/project/ProjectSidebarContainer' -import TopologySidebarContainer from '../../../../containers/app/sidebars/topology/TopologySidebarContainer' +import MapStage from '../../../../components/app/map/MapStage' import { openProjectSucceeded } from '../../../../redux/actions/projects' +import { AppPage } from '../../../../components/AppPage' +import { + Bullseye, + Drawer, + DrawerContent, + DrawerContentBody, + EmptyState, + EmptyStateIcon, + Spinner, + Title, +} from '@patternfly/react-core' +import { zoomInOnCenter } from '../../../../redux/actions/map' +import Toolbar from '../../../../components/app/map/controls/Toolbar' +import { useMapScale } from '../../../../data/map' +import ScaleIndicator from '../../../../components/app/map/controls/ScaleIndicator' +import TopologySidebar from '../../../../components/app/sidebars/topology/TopologySidebar' +import Collapse from '../../../../components/app/map/controls/Collapse' /** * Page that displays a datacenter topology. @@ -44,7 +55,6 @@ function Topology() { const { project: projectId, topology: topologyId } = router.query const { data: project } = useProject(projectId) - const title = project?.name ? project?.name + ' - OpenDC' : 'Simulation - OpenDC' const dispatch = useDispatch() useEffect(() => { @@ -54,27 +64,44 @@ function Topology() { }, [projectId, topologyId, dispatch]) const topologyIsLoading = useSelector((state) => state.currentTopologyId === '-1') + const scale = useMapScale() + const interactionLevel = useSelector((state) => state.interactionLevel) + + const [isExpanded, setExpanded] = useState(true) + const panelContent = setExpanded(false)} /> return ( - + - {title} + {project?.name ?? 'Topologies'} - OpenDC - {topologyIsLoading ? ( -
    - -
    + + + + + Loading Topology + + + ) : ( -
    - - - - - -
    + + + + + + + dispatch(zoomInOnCenter(zoomIn))} + onExport={() => window['exportCanvasToImage']()} + /> + setExpanded(true)} /> + + + + )} -
    + ) } diff --git a/opendc-web/opendc-web-ui/src/pages/projects/index.js b/opendc-web/opendc-web-ui/src/pages/projects/index.js index 2d8e6de7..9dcc9aea 100644 --- a/opendc-web/opendc-web-ui/src/pages/projects/index.js +++ b/opendc-web/opendc-web-ui/src/pages/projects/index.js @@ -1,31 +1,87 @@ -import React, { useState } from 'react' +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React, { useMemo, useState } from 'react' import Head from 'next/head' import ProjectFilterPanel from '../../components/projects/FilterPanel' -import NewProjectContainer from '../../containers/projects/NewProjectContainer' -import ProjectListContainer from '../../containers/projects/ProjectListContainer' -import AppNavbarContainer from '../../containers/navigation/AppNavbarContainer' -import { useRequireAuth } from '../../auth' -import { Container } from 'reactstrap' +import { useAuth, useRequireAuth } from '../../auth' +import { AppPage } from '../../components/AppPage' +import { PageSection, PageSectionVariants, Text, TextContent } from '@patternfly/react-core' +import { useProjects } from '../../data/project' +import ProjectTable from '../../components/projects/ProjectTable' +import { useMutation } from 'react-query' +import NewProject from '../../components/projects/NewProject' + +const getVisibleProjects = (projects, filter, userId) => { + switch (filter) { + case 'SHOW_ALL': + return projects + case 'SHOW_OWN': + return projects.filter((project) => + project.authorizations.some((a) => a.userId === userId && a.level === 'OWN') + ) + case 'SHOW_SHARED': + return projects.filter((project) => + project.authorizations.some((a) => a.userId === userId && a.level !== 'OWN') + ) + default: + return projects + } +} function Projects() { useRequireAuth() - + const { user } = useAuth() + const { status, data: projects } = useProjects() const [filter, setFilter] = useState('SHOW_ALL') + const visibleProjects = useMemo(() => getVisibleProjects(projects ?? [], filter, user?.sub), [ + projects, + filter, + user?.sub, + ]) + + const { mutate: deleteProject } = useMutation('deleteProject') return ( - <> + My Projects - OpenDC -
    - - - - - - -
    - + + + My Projects + + + + + deleteProject(project._id)} + /> + + +
    ) } diff --git a/opendc-web/opendc-web-ui/src/shapes.js b/opendc-web/opendc-web-ui/src/shapes.js index 3c27ad11..abdf146e 100644 --- a/opendc-web/opendc-web-ui/src/shapes.js +++ b/opendc-web/opendc-web-ui/src/shapes.js @@ -149,3 +149,5 @@ export const InteractionLevel = PropTypes.shape({ roomId: PropTypes.string, rackId: PropTypes.string, }) + +export const Status = PropTypes.oneOf(['idle', 'loading', 'error', 'success']) diff --git a/opendc-web/opendc-web-ui/src/style/_mixins.scss b/opendc-web/opendc-web-ui/src/style/_mixins.scss deleted file mode 100644 index 5f103cd7..00000000 --- a/opendc-web/opendc-web-ui/src/style/_mixins.scss +++ /dev/null @@ -1,5 +0,0 @@ -/* General Button Abstractions */ -@mixin clickable { - cursor: pointer; - user-select: none; -} diff --git a/opendc-web/opendc-web-ui/src/style/_variables.scss b/opendc-web/opendc-web-ui/src/style/_variables.scss deleted file mode 100644 index e3df6cbd..00000000 --- a/opendc-web/opendc-web-ui/src/style/_variables.scss +++ /dev/null @@ -1,31 +0,0 @@ -// Sizes and Margins -$document-padding: 20px; -$inter-element-margin: 5px; -$standard-border-radius: 5px; -$side-menu-width: 350px; -$color-indicator-width: 140px; - -$global-padding: 30px; -$side-bar-width: 350px; -$navbar-height: 50px; -$navbar-padding: 10px; - -// Durations -$transition-length: 150ms; - -// Colors -$gray-very-dark: #5c5c5c; -$gray-dark: #aaa; -$gray-semi-dark: #bbb; -$gray-semi-light: #ccc; -$gray-light: #ddd; -$gray-very-light: #eee; -$blue: #00a6d6; -$blue-dark: #0087b5; -$blue-very-dark: #006182; -$blue-light: #deebf7; - -// Media queries -$screen-sm: 768px; -$screen-md: 992px; -$screen-lg: 1200px; diff --git a/opendc-web/opendc-web-ui/src/style/index.scss b/opendc-web/opendc-web-ui/src/style/index.scss new file mode 100644 index 00000000..ff84e24a --- /dev/null +++ b/opendc-web/opendc-web-ui/src/style/index.scss @@ -0,0 +1,36 @@ +/*! + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +body, +#__next { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + + background: #eee; +} + +.full-height { + position: relative; + height: 100% !important; +} diff --git a/opendc-web/opendc-web-ui/src/util/authorizations.js b/opendc-web/opendc-web-ui/src/util/authorizations.js index eb492d26..ce5d34b6 100644 --- a/opendc-web/opendc-web-ui/src/util/authorizations.js +++ b/opendc-web/opendc-web-ui/src/util/authorizations.js @@ -1,9 +1,11 @@ -import { faHome, faPencilAlt, faEye } from '@fortawesome/free-solid-svg-icons' +import HomeIcon from '@patternfly/react-icons/dist/js/icons/home-icon' +import EditIcon from '@patternfly/react-icons/dist/js/icons/edit-icon' +import EyeIcon from '@patternfly/react-icons/dist/js/icons/eye-icon' export const AUTH_ICON_MAP = { - OWN: faHome, - EDIT: faPencilAlt, - VIEW: faEye, + OWN: HomeIcon, + EDIT: EditIcon, + VIEW: EyeIcon, } export const AUTH_DESCRIPTION_MAP = { diff --git a/opendc-web/opendc-web-ui/src/util/available-metrics.js b/opendc-web/opendc-web-ui/src/util/available-metrics.js index 807bc0c1..b21ab150 100644 --- a/opendc-web/opendc-web-ui/src/util/available-metrics.js +++ b/opendc-web/opendc-web-ui/src/util/available-metrics.js @@ -1,12 +1,28 @@ +export const METRIC_GROUPS = { + 'Host Metrics': [ + 'total_overcommitted_burst', + 'total_power_draw', + 'total_failure_vm_slices', + 'total_granted_burst', + 'total_interfered_burst', + 'total_requested_burst', + 'mean_cpu_usage', + 'mean_cpu_demand', + 'mean_num_deployed_images', + 'max_num_deployed_images', + ], + 'Compute Service Metrics': ['total_vms_submitted', 'total_vms_queued', 'total_vms_finished', 'total_vms_failed'], +} + export const AVAILABLE_METRICS = [ + 'mean_cpu_usage', + 'mean_cpu_demand', + 'total_requested_burst', + 'total_granted_burst', 'total_overcommitted_burst', + 'total_interfered_burst', 'total_power_draw', 'total_failure_vm_slices', - 'total_granted_burst', - 'total_interfered_burst', - 'total_requested_burst', - 'mean_cpu_usage', - 'mean_cpu_demand', 'mean_num_deployed_images', 'max_num_deployed_images', 'total_vms_submitted', @@ -65,3 +81,22 @@ export const METRIC_UNITS = { total_vms_finished: 'VMs', total_vms_failed: 'VMs', } + +export const METRIC_DESCRIPTIONS = { + total_overcommitted_burst: + 'The total CPU clock cycles lost due to overcommitting of resources. This metric is an indicator for resource overload.', + total_requested_burst: 'The total CPU clock cycles that were requested by all virtual machines.', + total_granted_burst: 'The total CPU clock cycles executed by the hosts.', + total_interfered_burst: 'The total CPU clock cycles lost due to resource interference between virtual machines.', + total_power_draw: 'The average power usage in watts.', + mean_cpu_usage: 'The average amount of CPU clock cycles consumed by all virtual machines on a host.', + mean_cpu_demand: 'The average amount of CPU clock cycles requested by all powered on virtual machines on a host.', + mean_num_deployed_images: 'The average number of virtual machines deployed on a host.', + max_num_deployed_images: 'The maximum number of virtual machines deployed at any time.', + total_failure_vm_slices: 'The total amount of CPU clock cycles lost due to failure.', + total_vms_submitted: 'The total number of virtual machines scheduled by the compute service.', + total_vms_queued: + 'The maximum number of virtual machines waiting to be scheduled by the compute service at any point.', + total_vms_finished: 'The total number of virtual machines that completed successfully.', + total_vms_failed: 'The total number of virtual machines that failed during execution.', +} diff --git a/opendc-web/opendc-web-ui/src/util/date-time.js b/opendc-web/opendc-web-ui/src/util/date-time.js index 66efdf5b..7e2f6623 100644 --- a/opendc-web/opendc-web-ui/src/util/date-time.js +++ b/opendc-web/opendc-web-ui/src/util/date-time.js @@ -7,19 +7,7 @@ * @returns {string} A human-friendly string version of that date and time. */ export function parseAndFormatDateTime(dateTimeString) { - return formatDateTime(parseDateTime(dateTimeString)) -} - -/** - * Parses date-time string representations and returns a parsed object. - * - * The format assumed is "YYYY-MM-DDTHH:MM:SS". - * - * @param dateTimeString A string expressing a date and a time, in the above mentioned format. - * @returns {object} A Date object with the parsed date and time information as content. - */ -export function parseDateTime(dateTimeString) { - return new Date(dateTimeString + '.000Z') + return formatDateTime(new Date(dateTimeString)) } /** diff --git a/opendc-web/opendc-web-ui/src/util/date-time.test.js b/opendc-web/opendc-web-ui/src/util/date-time.test.js index 3d95eba6..431e39f7 100644 --- a/opendc-web/opendc-web-ui/src/util/date-time.test.js +++ b/opendc-web/opendc-web-ui/src/util/date-time.test.js @@ -1,18 +1,4 @@ -import { convertSecondsToFormattedTime, parseDateTime } from './date-time' - -describe('date-time parsing', () => { - it('reads components properly', () => { - const dateString = '2017-09-27T20:55:01' - const parsedDate = parseDateTime(dateString) - - expect(parsedDate.getUTCFullYear()).toEqual(2017) - expect(parsedDate.getUTCMonth()).toEqual(8) - expect(parsedDate.getUTCDate()).toEqual(27) - expect(parsedDate.getUTCHours()).toEqual(20) - expect(parsedDate.getUTCMinutes()).toEqual(55) - expect(parsedDate.getUTCSeconds()).toEqual(1) - }) -}) +import { convertSecondsToFormattedTime } from './date-time' describe('tick formatting', () => { it("returns '0s' for numbers <= 0", () => { diff --git a/opendc-web/opendc-web-ui/yarn.lock b/opendc-web/opendc-web-ui/yarn.lock index abd2613e..cd446e99 100644 --- a/opendc-web/opendc-web-ui/yarn.lock +++ b/opendc-web/opendc-web-ui/yarn.lock @@ -148,14 +148,6 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug== -"@hypnosphi/create-react-context@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz#f8bfebdc7665f5d426cba3753e0e9c7d3154d7c6" - integrity sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A== - dependencies: - gud "^1.0.0" - warning "^4.0.3" - "@next/env@11.0.1": version "11.0.1" resolved "https://registry.yarnpkg.com/@next/env/-/env-11.0.1.tgz#6dc96ac76f1663ab747340e907e8933f190cc8fd" @@ -214,33 +206,45 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@patternfly/react-core@^4.135.0": - version "4.135.0" - resolved "https://registry.yarnpkg.com/@patternfly/react-core/-/react-core-4.135.0.tgz#b64ad4da10a8814926e28fad727bc7690cd60e66" - integrity sha512-DZcONUGOR7Znd6BsUJ4L+KrrnIpyjUvh3JNcYiHW3loytxShCGcx+a04QjOOcZm+MtFhkgs/t51yiC5IP12abA== +"@patternfly/react-core@^4.135.7": + version "4.135.7" + resolved "https://registry.yarnpkg.com/@patternfly/react-core/-/react-core-4.135.7.tgz#7292e4472ffcd519e3db1af782cb4d28024a6992" + integrity sha512-bxYLJ7TTXiEVHscfzqGbI+YFqwQMYWfG2Yknw/b/QqwN/bv5eCpioYOo35Wr4jvFAWkXvUUKp/DUpcSHrrFgTg== dependencies: - "@patternfly/react-icons" "^4.11.0" - "@patternfly/react-styles" "^4.11.0" - "@patternfly/react-tokens" "^4.12.0" + "@patternfly/react-icons" "^4.11.2" + "@patternfly/react-styles" "^4.11.2" + "@patternfly/react-tokens" "^4.12.3" focus-trap "6.2.2" react-dropzone "9.0.0" tippy.js "5.1.2" tslib "1.13.0" -"@patternfly/react-icons@^4.11.0": - version "4.11.0" - resolved "https://registry.yarnpkg.com/@patternfly/react-icons/-/react-icons-4.11.0.tgz#26790eeff22dc3204aa8cd094470f0a2f915634a" - integrity sha512-WsIX34bO9rhVRmPG0jlV3GoFGfYgPC64TscNV0lxQosiVRnYIA6Z3nBSArtJsxo5Yn6c63glIefC/YTy6D/ZYg== - -"@patternfly/react-styles@^4.11.0": - version "4.11.0" - resolved "https://registry.yarnpkg.com/@patternfly/react-styles/-/react-styles-4.11.0.tgz#0068dcb18e1343242f93fa6024dcc077acd57fb9" - integrity sha512-4eIqTwGI4mjt9DMqX6hnan4eRS+3LUWNaneTEJdmk+flKxtAE/O/OmQHvH4GetDnlSbyfATcA0VFbVtR0aRJAg== +"@patternfly/react-icons@^4.11.2": + version "4.11.2" + resolved "https://registry.yarnpkg.com/@patternfly/react-icons/-/react-icons-4.11.2.tgz#46f01197f779996954fd8033681644dd167cebcb" + integrity sha512-BPRWHAopry4a8aFd3VWaqO8h7QDA4NeYZ80YAY1iGqWeClIAmZlxs6jGCUhTC7iuYmjKruiVoDQwuwr/u+p6JQ== + +"@patternfly/react-styles@^4.11.2": + version "4.11.2" + resolved "https://registry.yarnpkg.com/@patternfly/react-styles/-/react-styles-4.11.2.tgz#3548d1c2a5b8a989f9edc6bd31165952e5f8bcb7" + integrity sha512-rUtw2tfniBiVIYEXPO8buisWFEHVR6dO5PH6C2MvbMk+VyUIS31TzOeNFyJs/gCurt7t1dEKjI9yNEGuAOjUPQ== + +"@patternfly/react-table@^4.29.8": + version "4.29.8" + resolved "https://registry.yarnpkg.com/@patternfly/react-table/-/react-table-4.29.8.tgz#9a8f1d1332c2f708427c81a693c2e90f9f52c75e" + integrity sha512-N6oKSE0sRHvkc9KWF03oTISfsc6G5NRBRCedi81hnpOSVo2515lGnA/r1ipR1DwM9qHYxmPNsrkPeoYtDQBOSQ== + dependencies: + "@patternfly/react-core" "^4.135.7" + "@patternfly/react-icons" "^4.11.2" + "@patternfly/react-styles" "^4.11.2" + "@patternfly/react-tokens" "^4.12.3" + lodash "^4.17.19" + tslib "1.13.0" -"@patternfly/react-tokens@^4.12.0": - version "4.12.0" - resolved "https://registry.yarnpkg.com/@patternfly/react-tokens/-/react-tokens-4.12.0.tgz#2973c7f08a2f35997a0054bbf3c886b3c5c68822" - integrity sha512-Oj+GxqTtx0Yu9IDCTibZLQnpcKp58JneNKEFQkJ29WJydhPG4j6oFFElkK+ub+Ft/f9B1Ky1SsfR9eabo6IykQ== +"@patternfly/react-tokens@^4.12.3": + version "4.12.3" + resolved "https://registry.yarnpkg.com/@patternfly/react-tokens/-/react-tokens-4.12.3.tgz#600b77e460e291032d649febf43002fe355a4468" + integrity sha512-RgXPEDdgx/p2VPQyCCxvv+ff9lmgYm4QDcI3c8iIwQjpPnJ5KF/bT6lPoAfeLFocRIZDnDSDcwSINbQwPQGp2A== "@redux-saga/core@^1.1.3": version "1.1.3" @@ -727,11 +731,6 @@ bn.js@^5.0.0, bn.js@^5.1.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== -bootstrap@~4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.0.tgz#97b9f29ac98f98dfa43bf7468262d84392552fd7" - integrity sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw== - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -958,7 +957,7 @@ classnames@2.2.6, classnames@~2.2.5: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== -classnames@^2.2.3, classnames@^2.2.5: +classnames@^2.2.5: version "2.3.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== @@ -1289,18 +1288,6 @@ deep-diff@^0.3.5: resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84" integrity sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ= -deep-equal@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== - dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" - object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" - deep-is@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -1990,11 +1977,6 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== -gud@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" - integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== - has-bigints@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" @@ -2291,7 +2273,7 @@ is-obj@^1.0.1: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= -is-regex@^1.0.4, is-regex@^1.1.2, is-regex@^1.1.3: +is-regex@^1.1.2, is-regex@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== @@ -3153,7 +3135,7 @@ pnp-webpack-plugin@1.6.4: dependencies: ts-pnp "^1.1.6" -popper.js@^1.14.4, popper.js@^1.16.0: +popper.js@^1.16.0: version "1.16.1" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== @@ -3210,7 +3192,7 @@ prop-types-extra@^1.1.0: react-is "^16.3.2" warning "^4.0.0" -prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@~15.7.2: +prop-types@15.7.2, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@~15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -3367,19 +3349,6 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-popper@^1.3.6: - version "1.3.11" - resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.11.tgz#a2cc3f0a67b75b66cfa62d2c409f9dd1fcc71ffd" - integrity sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg== - dependencies: - "@babel/runtime" "^7.1.2" - "@hypnosphi/create-react-context" "^0.3.1" - deep-equal "^1.1.1" - popper.js "^1.14.4" - prop-types "^15.6.1" - typed-styles "^0.0.7" - warning "^4.0.2" - react-query@^3.18.1: version "3.18.1" resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.18.1.tgz#893b5475a7b4add099e007105317446f7a2cd310" @@ -3434,7 +3403,7 @@ react-smooth@^2.0.0: raf "^3.4.0" react-transition-group "2.9.0" -react-transition-group@2.9.0, react-transition-group@^2.3.1: +react-transition-group@2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== @@ -3452,17 +3421,6 @@ react@^17.0.2: loose-envify "^1.1.0" object-assign "^4.1.1" -reactstrap@^8.9.0: - version "8.9.0" - resolved "https://registry.yarnpkg.com/reactstrap/-/reactstrap-8.9.0.tgz#bca4afa3f5cd18899ef9b33d877a141886d5abae" - integrity sha512-pmf33YjpNZk1IfrjqpWCUMq9hk6GzSnMWBAofTBNIRJQB1zQ0Au2kzv3lPUAFsBYgWEuI9iYa/xKXHaboSiMkQ== - dependencies: - "@babel/runtime" "^7.12.5" - classnames "^2.2.3" - prop-types "^15.5.8" - react-popper "^1.3.6" - react-transition-group "^2.3.1" - read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" @@ -3582,7 +3540,7 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1: +regexp.prototype.flags@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== @@ -4222,11 +4180,6 @@ typed-function@^2.0.0: resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-2.0.0.tgz#15ab3825845138a8b1113bd89e60cd6a435739e8" integrity sha512-Hhy1Iwo/e4AtLZNK10ewVVcP2UEs408DS35ubP825w/YgSBK1KVLwALvvIG4yX75QJrxjCpcWkzkVRB0BwwYlA== -typed-styles@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" - integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q== - typescript-compare@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425" @@ -4289,6 +4242,13 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" +use-resize-observer@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/use-resize-observer/-/use-resize-observer-7.0.0.tgz#15f0efbd5a4e08a8cc51901f21a89ba836f2116e" + integrity sha512-+RjrQsk/mL8aKy4TGBDiPkUv6whyeoGDMIZYk0gOGHOlnrsjImC+jG6lfAFcBCKAG9epGRL419adhDNdkDCQkA== + dependencies: + resize-observer-polyfill "^1.5.1" + use-subscription@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" @@ -4358,7 +4318,7 @@ vm-browserify@1.1.2, vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -warning@^4.0.0, warning@^4.0.2, warning@^4.0.3: +warning@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== -- cgit v1.2.3