├── .eslintignore ├── public ├── favicon.ico ├── apple-icon.png ├── manifest.json └── index.html ├── src ├── assets │ ├── img │ │ ├── nano.ico │ │ ├── favicon.png │ │ ├── sidebar.jpg │ │ ├── reactlogo.png │ │ ├── sidebar-2.jpg │ │ ├── login_background.jpg │ │ └── nano_white.svg │ ├── github │ │ ├── edge.png │ │ ├── html.png │ │ ├── map.jpg │ │ ├── angular.png │ │ ├── chrome.png │ │ ├── firefox.png │ │ ├── opera.png │ │ ├── safari.png │ │ ├── tables.jpg │ │ ├── vuejs.png │ │ ├── dashboard.jpg │ │ ├── md-react.gif │ │ ├── userprofile.jpg │ │ ├── notifications.jpg │ │ ├── opt_md_thumbnail.jpg │ │ ├── opt_mdr_thumbnail.jpg │ │ ├── opt_md_vue_thumbnail.jpg │ │ ├── opt_md_angular_thumbnail.jpg │ │ └── react.svg │ └── jss │ │ └── material-dashboard-react │ │ ├── components │ │ ├── cardBodyStyle.js │ │ ├── cardIconStyle.js │ │ ├── cardAvatarStyle.js │ │ ├── cardStyle.js │ │ ├── typographyStyle.js │ │ ├── footerStyle.js │ │ ├── cardFooterStyle.js │ │ ├── tasksStyle.js │ │ ├── customInputStyle.js │ │ ├── tableStyle.js │ │ ├── customTabsStyle.js │ │ ├── headerStyle.js │ │ ├── headerLinksStyle.js │ │ ├── rtlHeaderLinksStyle.js │ │ ├── snackbarContentStyle.js │ │ └── cardHeaderStyle.js │ │ ├── cardImagesStyles.js │ │ ├── layouts │ │ ├── adminStyle.js │ │ └── rtlStyle.js │ │ ├── tooltipStyle.js │ │ ├── views │ │ ├── iconsStyle.js │ │ ├── dashboardStyle.js │ │ └── rtlStyle.js │ │ ├── checkboxAdnRadioStyle.js │ │ └── dropdownStyle.js ├── components │ ├── Navbars │ │ ├── AdminNavbarLinks.js │ │ └── Navbar.js │ ├── Backdrop │ │ └── Backdrop.js │ ├── Grid │ │ ├── SingleRow.js │ │ ├── GridItem.js │ │ └── GridContainer.js │ ├── Typography │ │ ├── Info.js │ │ ├── Muted.js │ │ ├── Danger.js │ │ ├── Primary.js │ │ ├── Success.js │ │ ├── Warning.js │ │ └── Quote.js │ ├── Card │ │ ├── CardBody.js │ │ ├── CardAvatar.js │ │ ├── Card.js │ │ ├── CardIcon.js │ │ ├── CardFooter.js │ │ └── CardHeader.js │ ├── CustomButtons │ │ ├── IconButton.js │ │ └── Button.js │ ├── Table │ │ ├── OperableTable.js │ │ ├── ObjectTable.js │ │ └── Table.js │ ├── Snackbar │ │ ├── SnackbarContent.js │ │ └── Snackbar.js │ ├── Footer │ │ └── Footer.js │ ├── Dialog │ │ └── CustomDialog.js │ ├── CustomInput │ │ └── CustomInput.js │ ├── Language │ │ └── Selector.js │ ├── CustomTabs │ │ └── CustomTabs.js │ └── LoggedUser │ │ └── ModifyPasswordDialog.js ├── views │ ├── PolicyGroups │ │ ├── PolicyGroups.js │ │ ├── DeletePolicyDialog.js │ │ └── RemoveRuleDialog.js │ ├── AddressPools │ │ ├── AddressPools.js │ │ ├── DeleteDialog.js │ │ └── RemoveRangeDialog.js │ ├── Dashboard │ │ ├── Dashboard.js │ │ ├── PieChart.js │ │ ├── HorizontalBarChart.js │ │ ├── MultiBarCard.js │ │ ├── StackedBarCard.js │ │ ├── LineCard.js │ │ ├── SeriesLabels.js │ │ ├── MultiBarChart.js │ │ ├── StackedBarChart.js │ │ ├── PieCard.js │ │ └── LineChart.js │ ├── Instances │ │ ├── Instances.js │ │ ├── DeleteDialog.js │ │ ├── ResetSecretDialog.js │ │ ├── DeleteSnapshotDialog.js │ │ ├── RevertSnapshotDialog.js │ │ ├── RemoveRuleDialog.js │ │ ├── VncDisplay.js │ │ ├── ShrinkDiskSizeDialog.js │ │ ├── CreateSnapshotDialog.js │ │ ├── ModifyNameDialog.js │ │ └── ModifyCoresDialog.js │ ├── DiskImages │ │ ├── SyncDialog.js │ │ └── DeleteDialog.js │ ├── MediaImages │ │ ├── SyncDialog.js │ │ └── DeleteDialog.js │ ├── ComputePools │ │ └── DeleteDialog.js │ ├── SystemTemplates │ │ └── DeleteDialog.js │ ├── StoragePools │ │ └── DeleteDialog.js │ ├── Users │ │ ├── Main.js │ │ ├── DeleteUserDialog.js │ │ ├── RemoveGroupDialog.js │ │ ├── RemoveRoleDialog.js │ │ └── RemoveMemberDialog.js │ ├── ComputeCells │ │ └── RemoveDialog.js │ └── Logs │ │ └── BatchDeleteDialog.js ├── variables │ └── general.js ├── logo.svg └── index.js ├── jsconfig.json ├── .babelrc ├── release.patch ├── .gitignore ├── .eslintrc.js ├── ISSUE_TEMPLATE.md ├── bower.json ├── LICENSE.md ├── LICENSE-tim.md ├── package.json ├── gulpfile.js ├── CHANGELOG.md └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | !.eslintrc.js 2 | documentation/ 3 | build/ 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/public/apple-icon.png -------------------------------------------------------------------------------- /src/assets/img/nano.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/img/nano.ico -------------------------------------------------------------------------------- /src/assets/github/edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/edge.png -------------------------------------------------------------------------------- /src/assets/github/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/html.png -------------------------------------------------------------------------------- /src/assets/github/map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/map.jpg -------------------------------------------------------------------------------- /src/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/img/favicon.png -------------------------------------------------------------------------------- /src/assets/img/sidebar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/img/sidebar.jpg -------------------------------------------------------------------------------- /src/assets/github/angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/angular.png -------------------------------------------------------------------------------- /src/assets/github/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/chrome.png -------------------------------------------------------------------------------- /src/assets/github/firefox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/firefox.png -------------------------------------------------------------------------------- /src/assets/github/opera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/opera.png -------------------------------------------------------------------------------- /src/assets/github/safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/safari.png -------------------------------------------------------------------------------- /src/assets/github/tables.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/tables.jpg -------------------------------------------------------------------------------- /src/assets/github/vuejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/vuejs.png -------------------------------------------------------------------------------- /src/assets/img/reactlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/img/reactlogo.png -------------------------------------------------------------------------------- /src/assets/img/sidebar-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/img/sidebar-2.jpg -------------------------------------------------------------------------------- /src/assets/github/dashboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/dashboard.jpg -------------------------------------------------------------------------------- /src/assets/github/md-react.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/md-react.gif -------------------------------------------------------------------------------- /src/assets/github/userprofile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/userprofile.jpg -------------------------------------------------------------------------------- /src/assets/github/notifications.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/notifications.jpg -------------------------------------------------------------------------------- /src/assets/img/login_background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/img/login_background.jpg -------------------------------------------------------------------------------- /src/assets/github/opt_md_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/opt_md_thumbnail.jpg -------------------------------------------------------------------------------- /src/assets/github/opt_mdr_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/opt_mdr_thumbnail.jpg -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src", 4 | "paths": { 5 | "*": ["src/*"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/assets/github/opt_md_vue_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/opt_md_vue_thumbnail.jpg -------------------------------------------------------------------------------- /src/assets/github/opt_md_angular_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-nano/portal/HEAD/src/assets/github/opt_md_angular_thumbnail.jpg -------------------------------------------------------------------------------- /src/components/Navbars/AdminNavbarLinks.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import LoggedUser from "components/LoggedUser/LoggedUser.js"; 3 | 4 | 5 | export default function AdminNavbarLinks(props) { 6 | return ( 7 |
8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": [ 4 | "transform-class-properties", 5 | "transform-react-jsx", 6 | "transform-object-rest-spread", 7 | ["module-resolver", { 8 | "root": ["./src"] 9 | }], 10 | ["import-rename", {"^(.*)\\.jsx$": "$1"}] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /release.patch: -------------------------------------------------------------------------------- 1 | diff --git a/package.json b/package.json 2 | index 30645bc..1e35d4a 100644 3 | --- a/package.json 4 | +++ b/package.json 5 | @@ -3,7 +3,7 @@ 6 | "version": "1.4.0", 7 | "description": "Project-Nano manage portal", 8 | "service": { 9 | - "host": "192.168.1.39", 10 | + "host": "", 11 | "port": 5870, 12 | "debug": false 13 | }, 14 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # npmjs 13 | /dist 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /src/assets/github/react.svg: -------------------------------------------------------------------------------- 1 | 2 | React Logo 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardBodyStyle.js: -------------------------------------------------------------------------------- 1 | const cardBodyStyle = { 2 | cardBody: { 3 | padding: "0.9375rem 20px", 4 | flex: "1 1 auto", 5 | WebkitBoxFlex: "1", 6 | position: "relative" 7 | }, 8 | cardBodyPlain: { 9 | paddingLeft: "5px", 10 | paddingRight: "5px" 11 | }, 12 | cardBodyProfile: { 13 | marginTop: "15px" 14 | } 15 | }; 16 | 17 | export default cardBodyStyle; 18 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "babel-eslint", 3 | env: { 4 | es6: true, 5 | node: true, 6 | browser: true 7 | }, 8 | parserOptions: { 9 | ecmaVersion: 6, 10 | sourceType: "module", 11 | ecmaFeatures: { 12 | jsx: true 13 | } 14 | }, 15 | plugins: ["react"], 16 | extends: [ 17 | "eslint:recommended", 18 | "plugin:react/recommended", 19 | "plugin:prettier/recommended" 20 | ] 21 | }; 22 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /src/components/Backdrop/Backdrop.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import InnerBackdrop from '@material-ui/core/Backdrop'; 4 | 5 | const useStyles = makeStyles(theme => ({ 6 | backdrop: { 7 | zIndex: theme.zIndex.drawer + 1, 8 | color: '#fff', 9 | }, 10 | })); 11 | 12 | export default function Backdrop(props){ 13 | const classes = useStyles(); 14 | const {children, ...rest} = props; 15 | return ( 16 | 17 | {children} 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nano-portal", 3 | "homepage": "https://nanos.cloud/", 4 | "authors": [ 5 | "Akumas" 6 | ], 7 | "description": "Web Portal for project-nano frontend", 8 | "main": "public/index.html", 9 | "keywords": [ 10 | "material", 11 | "design", 12 | "material-ui", 13 | "material", 14 | "design", 15 | "google", 16 | "twitter" 17 | ], 18 | "license": "MIT License", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "tests", 25 | "build" 26 | ], 27 | "private": true 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Grid/SingleRow.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library to set properties for components 3 | import PropTypes from "prop-types"; 4 | import Grid from "@material-ui/core/Grid"; 5 | import Box from '@material-ui/core/Box'; 6 | 7 | export default function SingleRow(props){ 8 | const { children, ...rest } = props; 9 | return ( 10 | 11 | 12 | 13 | {children} 14 | 15 | 16 | 17 | ); 18 | } 19 | 20 | SingleRow.propTypes = { 21 | children: PropTypes.node 22 | }; 23 | -------------------------------------------------------------------------------- /src/views/PolicyGroups/PolicyGroups.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route } from "react-router-dom"; 3 | import PolicyGroupList from "views/PolicyGroups/PolicyGroupList.js"; 4 | import SecurityRules from "views/PolicyGroups/SecurityRules"; 5 | 6 | export default function Instances(props){ 7 | return ( 8 |
9 | React.createElement(PolicyGroupList, props)}/> 10 | React.createElement(SecurityRules, { 11 | ...childrenProps, 12 | ...props, 13 | })}/> 14 |
15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Typography/Info.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | // core components 6 | import styles from "assets/jss/material-dashboard-react/components/typographyStyle.js"; 7 | 8 | const useStyles = makeStyles(styles); 9 | 10 | export default function Info(props) { 11 | const classes = useStyles(); 12 | const { children } = props; 13 | return ( 14 |
15 | {children} 16 |
17 | ); 18 | } 19 | 20 | Info.propTypes = { 21 | children: PropTypes.node 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/Typography/Muted.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | // core components 6 | import styles from "assets/jss/material-dashboard-react/components/typographyStyle.js"; 7 | 8 | const useStyles = makeStyles(styles); 9 | 10 | export default function Muted(props) { 11 | const classes = useStyles(); 12 | const { children } = props; 13 | return ( 14 |
15 | {children} 16 |
17 | ); 18 | } 19 | 20 | Muted.propTypes = { 21 | children: PropTypes.node 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/Typography/Danger.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | // core components 6 | import styles from "assets/jss/material-dashboard-react/components/typographyStyle.js"; 7 | 8 | const useStyles = makeStyles(styles); 9 | 10 | export default function Danger(props) { 11 | const classes = useStyles(); 12 | const { children } = props; 13 | return ( 14 |
15 | {children} 16 |
17 | ); 18 | } 19 | 20 | Danger.propTypes = { 21 | children: PropTypes.node 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/Typography/Primary.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | // core components 6 | import styles from "assets/jss/material-dashboard-react/components/typographyStyle.js"; 7 | 8 | const useStyles = makeStyles(styles); 9 | 10 | export default function Primary(props) { 11 | const classes = useStyles(); 12 | const { children } = props; 13 | return ( 14 |
15 | {children} 16 |
17 | ); 18 | } 19 | 20 | Primary.propTypes = { 21 | children: PropTypes.node 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/Typography/Success.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | // core components 6 | import styles from "assets/jss/material-dashboard-react/components/typographyStyle.js"; 7 | 8 | const useStyles = makeStyles(styles); 9 | 10 | export default function Success(props) { 11 | const classes = useStyles(); 12 | const { children } = props; 13 | return ( 14 |
15 | {children} 16 |
17 | ); 18 | } 19 | 20 | Success.propTypes = { 21 | children: PropTypes.node 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/Typography/Warning.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | // core components 6 | import styles from "assets/jss/material-dashboard-react/components/typographyStyle.js"; 7 | 8 | const useStyles = makeStyles(styles); 9 | 10 | export default function Warning(props) { 11 | const classes = useStyles(); 12 | const { children } = props; 13 | return ( 14 |
15 | {children} 16 |
17 | ); 18 | } 19 | 20 | Warning.propTypes = { 21 | children: PropTypes.node 22 | }; 23 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/cardImagesStyles.js: -------------------------------------------------------------------------------- 1 | const cardImagesStyles = { 2 | cardImgTop: { 3 | width: "100%", 4 | borderTopLeftRadius: "calc(.25rem - 1px)", 5 | borderTopRightRadius: "calc(.25rem - 1px)" 6 | }, 7 | cardImgBottom: { 8 | width: "100%", 9 | borderBottomRightRadius: "calc(.25rem - 1px)", 10 | borderBottomLeftRadius: "calc(.25rem - 1px)" 11 | }, 12 | cardImgOverlay: { 13 | position: "absolute", 14 | top: "0", 15 | right: "0", 16 | bottom: "0", 17 | left: "0", 18 | padding: "1.25rem" 19 | }, 20 | cardImg: { 21 | width: "100%", 22 | borderRadius: "calc(.25rem - 1px)" 23 | } 24 | }; 25 | 26 | export default cardImagesStyles; 27 | -------------------------------------------------------------------------------- /src/components/Grid/GridItem.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library to set properties for components 3 | import PropTypes from "prop-types"; 4 | // @material-ui/core components 5 | import { makeStyles } from "@material-ui/core/styles"; 6 | import Grid from "@material-ui/core/Grid"; 7 | 8 | const styles = { 9 | grid: { 10 | padding: "0 15px !important" 11 | } 12 | }; 13 | 14 | const useStyles = makeStyles(styles); 15 | 16 | export default function GridItem(props) { 17 | const classes = useStyles(); 18 | const { children, ...rest } = props; 19 | return ( 20 | 21 | {children} 22 | 23 | ); 24 | } 25 | 26 | GridItem.propTypes = { 27 | children: PropTypes.node 28 | }; 29 | -------------------------------------------------------------------------------- /src/components/Grid/GridContainer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library to set properties for components 3 | import PropTypes from "prop-types"; 4 | // @material-ui/core components 5 | import { makeStyles } from "@material-ui/core/styles"; 6 | import Grid from "@material-ui/core/Grid"; 7 | 8 | const styles = { 9 | grid: { 10 | margin: "0 -15px !important", 11 | width: "unset" 12 | } 13 | }; 14 | 15 | const useStyles = makeStyles(styles); 16 | 17 | export default function GridContainer(props) { 18 | const classes = useStyles(); 19 | const { children, ...rest } = props; 20 | return ( 21 | 22 | {children} 23 | 24 | ); 25 | } 26 | 27 | GridContainer.propTypes = { 28 | children: PropTypes.node 29 | }; 30 | -------------------------------------------------------------------------------- /src/components/Typography/Quote.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | // core components 6 | import styles from "assets/jss/material-dashboard-react/components/typographyStyle.js"; 7 | 8 | const useStyles = makeStyles(styles); 9 | 10 | export default function Quote(props) { 11 | const classes = useStyles(); 12 | const { text, author } = props; 13 | return ( 14 |
15 |

{text}

16 | {author} 17 |
18 | ); 19 | } 20 | 21 | Quote.propTypes = { 22 | text: PropTypes.node, 23 | author: PropTypes.node 24 | }; 25 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardIconStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | warningCardHeader, 3 | successCardHeader, 4 | dangerCardHeader, 5 | infoCardHeader, 6 | primaryCardHeader, 7 | roseCardHeader, 8 | grayColor 9 | } from "assets/jss/material-dashboard-react.js"; 10 | 11 | const cardIconStyle = { 12 | cardIcon: { 13 | "&$warningCardHeader,&$successCardHeader,&$dangerCardHeader,&$infoCardHeader,&$primaryCardHeader,&$roseCardHeader": { 14 | borderRadius: "3px", 15 | backgroundColor: grayColor[0], 16 | padding: "15px", 17 | marginTop: "-20px", 18 | marginRight: "15px", 19 | float: "left" 20 | } 21 | }, 22 | warningCardHeader, 23 | successCardHeader, 24 | dangerCardHeader, 25 | infoCardHeader, 26 | primaryCardHeader, 27 | roseCardHeader 28 | }; 29 | 30 | export default cardIconStyle; 31 | -------------------------------------------------------------------------------- /src/views/AddressPools/AddressPools.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route } from "react-router-dom"; 3 | import PoolList from "views/AddressPools/PoolList"; 4 | import PoolStatus from "views/AddressPools/PoolStatus"; 5 | import RangeStatus from "views/AddressPools/RangeStatus"; 6 | 7 | export default function AddressPools(props){ 8 | return ( 9 |
10 | React.createElement(PoolList, props)}/> 11 | React.createElement(PoolStatus, { 12 | ...props, 13 | ...current, 14 | })}/> 15 | React.createElement(RangeStatus, { 16 | ...props, 17 | ...current, 18 | })}/> 19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/layouts/adminStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | drawerWidth, 3 | transition, 4 | container 5 | } from "assets/jss/material-dashboard-react.js"; 6 | 7 | const appStyle = theme => ({ 8 | wrapper: { 9 | position: "relative", 10 | top: "0", 11 | height: "100vh" 12 | }, 13 | mainPanel: { 14 | [theme.breakpoints.up("md")]: { 15 | width: `calc(100% - ${drawerWidth}px)` 16 | }, 17 | overflow: "auto", 18 | position: "relative", 19 | float: "right", 20 | ...transition, 21 | maxHeight: "100%", 22 | width: "100%", 23 | overflowScrolling: "touch" 24 | }, 25 | content: { 26 | marginTop: "70px", 27 | padding: "30px 15px", 28 | minHeight: "calc(100vh - 123px)" 29 | }, 30 | container, 31 | map: { 32 | marginTop: "70px" 33 | } 34 | }); 35 | 36 | export default appStyle; 37 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/layouts/rtlStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | drawerWidth, 3 | transition, 4 | container 5 | } from "assets/jss/material-dashboard-react.js"; 6 | 7 | const appStyle = theme => ({ 8 | wrapper: { 9 | position: "relative", 10 | top: "0", 11 | height: "100vh", 12 | direction: "rtl" 13 | }, 14 | mainPanel: { 15 | [theme.breakpoints.up("md")]: { 16 | width: `calc(100% - ${drawerWidth}px)` 17 | }, 18 | overflow: "auto", 19 | position: "relative", 20 | float: "left", 21 | ...transition, 22 | maxHeight: "100%", 23 | width: "100%", 24 | overflowScrolling: "touch" 25 | }, 26 | content: { 27 | marginTop: "70px", 28 | padding: "30px 15px", 29 | minHeight: "calc(100vh - 123px)" 30 | }, 31 | container, 32 | map: { 33 | marginTop: "70px" 34 | } 35 | }); 36 | 37 | export default appStyle; 38 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardAvatarStyle.js: -------------------------------------------------------------------------------- 1 | import { hexToRgb, blackColor } from "assets/jss/material-dashboard-react.js"; 2 | 3 | const cardAvatarStyle = { 4 | cardAvatar: { 5 | "&$cardAvatarProfile img": { 6 | width: "100%", 7 | height: "auto" 8 | } 9 | }, 10 | cardAvatarProfile: { 11 | maxWidth: "130px", 12 | maxHeight: "130px", 13 | margin: "-50px auto 0", 14 | borderRadius: "50%", 15 | overflow: "hidden", 16 | padding: "0", 17 | boxShadow: 18 | "0 16px 38px -12px rgba(" + 19 | hexToRgb(blackColor) + 20 | ", 0.56), 0 4px 25px 0px rgba(" + 21 | hexToRgb(blackColor) + 22 | ", 0.12), 0 8px 10px -5px rgba(" + 23 | hexToRgb(blackColor) + 24 | ", 0.2)", 25 | "&$cardAvatarPlain": { 26 | marginTop: "0" 27 | } 28 | }, 29 | cardAvatarPlain: {} 30 | }; 31 | 32 | export default cardAvatarStyle; 33 | -------------------------------------------------------------------------------- /src/views/Dashboard/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route } from "react-router-dom"; 3 | import ZoneStatus from "views/Dashboard/ZoneStatus.js"; 4 | import PoolStatus from "views/Dashboard/PoolStatus.js"; 5 | import CellStatus from "views/Dashboard/CellStatus.js"; 6 | 7 | export default function Dashboard(props){ 8 | return ( 9 |
10 | React.createElement(ZoneStatus, { 11 | ...childrenProps, 12 | ...props, 13 | })}/> 14 | React.createElement(PoolStatus, { 15 | ...childrenProps, 16 | ...props, 17 | })}/> 18 | 19 | React.createElement(CellStatus, { 20 | ...childrenProps, 21 | ...props, 22 | })}/> 23 | 24 |
25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | blackColor, 3 | whiteColor, 4 | hexToRgb 5 | } from "assets/jss/material-dashboard-react.js"; 6 | 7 | const cardStyle = { 8 | card: { 9 | border: "0", 10 | marginBottom: "30px", 11 | marginTop: "30px", 12 | borderRadius: "6px", 13 | color: "rgba(" + hexToRgb(blackColor) + ", 0.87)", 14 | background: whiteColor, 15 | width: "100%", 16 | boxShadow: "0 1px 4px 0 rgba(" + hexToRgb(blackColor) + ", 0.14)", 17 | position: "relative", 18 | display: "flex", 19 | flexDirection: "column", 20 | minWidth: "0", 21 | wordWrap: "break-word", 22 | fontSize: ".875rem" 23 | }, 24 | cardPlain: { 25 | background: "transparent", 26 | boxShadow: "none" 27 | }, 28 | cardProfile: { 29 | marginTop: "30px", 30 | textAlign: "center" 31 | }, 32 | cardChart: { 33 | "& p": { 34 | marginTop: "0px", 35 | paddingTop: "0px" 36 | } 37 | } 38 | }; 39 | 40 | export default cardStyle; 41 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/tooltipStyle.js: -------------------------------------------------------------------------------- 1 | import { blackColor, hexToRgb } from "assets/jss/material-dashboard-react.js"; 2 | 3 | const tooltipStyle = { 4 | tooltip: { 5 | padding: "10px 15px", 6 | minWidth: "130px", 7 | lineHeight: "1.7em", 8 | border: "none", 9 | borderRadius: "3px", 10 | boxShadow: 11 | "0 8px 10px 1px rgba(" + 12 | hexToRgb(blackColor) + 13 | ", 0.14), 0 3px 14px 2px rgba(" + 14 | hexToRgb(blackColor) + 15 | ", 0.12), 0 5px 5px -3px rgba(" + 16 | hexToRgb(blackColor) + 17 | ", 0.2)", 18 | maxWidth: "200px", 19 | textAlign: "center", 20 | fontFamily: '"Helvetica Neue",Helvetica,Arial,sans-serif', 21 | fontSize: "12px", 22 | fontStyle: "normal", 23 | fontWeight: "400", 24 | textShadow: "none", 25 | textTransform: "none", 26 | letterSpacing: "normal", 27 | wordBreak: "normal", 28 | wordSpacing: "normal", 29 | wordWrap: "normal", 30 | whiteSpace: "normal", 31 | lineBreak: "auto" 32 | } 33 | }; 34 | export default tooltipStyle; 35 | -------------------------------------------------------------------------------- /src/views/Dashboard/PieChart.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // react plugin for creating charts 3 | import { Pie } from 'react-chartjs-2'; 4 | 5 | export default function PieChart(props){ 6 | const { series } = props; 7 | var labels = []; 8 | var valueList = []; 9 | var colorList = []; 10 | series.forEach( slice => { 11 | labels.push(slice.label); 12 | valueList.push(slice.value); 13 | colorList.push(slice.color); 14 | }); 15 | 16 | const chartData = { 17 | labels: labels, 18 | datasets: [ 19 | { 20 | data: valueList, 21 | backgroundColor: colorList, 22 | borderWidth: 1, 23 | hoverBorderWidth: 0, 24 | } 25 | ], 26 | }; 27 | 28 | const chartOptions = { 29 | cutoutPercentage: 5, 30 | legend: { 31 | display: false, 32 | }, 33 | layout: { 34 | padding: { 35 | left: 0, 36 | right: 0, 37 | top: 0, 38 | bottom: 0 39 | } 40 | } 41 | }; 42 | 43 | return ; 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Project Nano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE-tim.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Creative Tim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/views/iconsStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | boxShadow, 3 | whiteColor, 4 | grayColor, 5 | hexToRgb 6 | } from "assets/jss/material-dashboard-react.js"; 7 | 8 | const iconsStyle = { 9 | iframe: { 10 | width: "100%", 11 | height: "500px", 12 | border: "0", 13 | ...boxShadow 14 | }, 15 | iframeContainer: { 16 | margin: "0 -20px 0" 17 | }, 18 | cardCategoryWhite: { 19 | "&,& a,& a:hover,& a:focus": { 20 | color: "rgba(" + hexToRgb(whiteColor) + ",.62)", 21 | margin: "0", 22 | fontSize: "14px", 23 | marginTop: "0", 24 | marginBottom: "0" 25 | }, 26 | "& a,& a:hover,& a:focus": { 27 | color: whiteColor 28 | } 29 | }, 30 | cardTitleWhite: { 31 | color: whiteColor, 32 | marginTop: "0px", 33 | minHeight: "auto", 34 | fontWeight: "300", 35 | fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", 36 | marginBottom: "3px", 37 | textDecoration: "none", 38 | "& small": { 39 | color: grayColor[1], 40 | fontWeight: "400", 41 | lineHeight: "1" 42 | } 43 | } 44 | }; 45 | 46 | export default iconsStyle; 47 | -------------------------------------------------------------------------------- /src/variables/general.js: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // Tasks for TasksCard - see Dashboard view 3 | // ############################# 4 | 5 | var bugs = [ 6 | 'Sign contract for "What are conference organizers afraid of?"', 7 | "Lines From Great Russian Literature? Or E-mails From My Boss?", 8 | "Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit", 9 | "Create 4 Invisible User Experiences you Never Knew About" 10 | ]; 11 | var website = [ 12 | "Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit", 13 | 'Sign contract for "What are conference organizers afraid of?"' 14 | ]; 15 | var server = [ 16 | "Lines From Great Russian Literature? Or E-mails From My Boss?", 17 | "Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit", 18 | 'Sign contract for "What are conference organizers afraid of?"' 19 | ]; 20 | 21 | module.exports = { 22 | // these 3 are used to create the tasks lists in TasksCard - Dashboard view 23 | bugs, 24 | website, 25 | server 26 | }; 27 | -------------------------------------------------------------------------------- /src/components/Card/CardBody.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | // @material-ui/core components 7 | import { makeStyles } from "@material-ui/core/styles"; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import styles from "assets/jss/material-dashboard-react/components/cardBodyStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function CardBody(props) { 16 | const classes = useStyles(); 17 | const { className, children, plain, profile, ...rest } = props; 18 | const cardBodyClasses = classNames({ 19 | [classes.cardBody]: true, 20 | [classes.cardBodyPlain]: plain, 21 | [classes.cardBodyProfile]: profile, 22 | [className]: className !== undefined 23 | }); 24 | return ( 25 |
26 | {children} 27 |
28 | ); 29 | } 30 | 31 | CardBody.propTypes = { 32 | className: PropTypes.string, 33 | plain: PropTypes.bool, 34 | profile: PropTypes.bool, 35 | children: PropTypes.node 36 | }; 37 | -------------------------------------------------------------------------------- /src/components/CustomButtons/IconButton.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import Tooltip from "@material-ui/core/Tooltip"; 4 | import InnerButton from "@material-ui/core/IconButton"; 5 | 6 | export default function IconButton(props){ 7 | const { label, icon, href, placement, color, onClick } = props; 8 | let tooltipPlacement, iconColor; 9 | if(placement){ 10 | tooltipPlacement = placement; 11 | }else{ 12 | tooltipPlacement = 'top'; 13 | } 14 | if(color){ 15 | iconColor = color; 16 | }else{ 17 | iconColor = 'inherit'; 18 | } 19 | let innerButton; 20 | if (onClick){ 21 | innerButton = {React.createElement(icon)}; 22 | }else if (href){ 23 | innerButton = {React.createElement(icon)}; 24 | }else{ 25 | innerButton = {React.createElement(icon)}; 26 | } 27 | 28 | return ( 29 | 33 | {innerButton} 34 | 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /src/components/Card/CardAvatar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | // @material-ui/core components 7 | import { makeStyles } from "@material-ui/core/styles"; 8 | // @material-ui/icons 9 | // core components 10 | 11 | import styles from "assets/jss/material-dashboard-react/components/cardAvatarStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function CardAvatar(props) { 16 | const classes = useStyles(); 17 | const { children, className, plain, profile, ...rest } = props; 18 | const cardAvatarClasses = classNames({ 19 | [classes.cardAvatar]: true, 20 | [classes.cardAvatarProfile]: profile, 21 | [classes.cardAvatarPlain]: plain, 22 | [className]: className !== undefined 23 | }); 24 | return ( 25 |
26 | {children} 27 |
28 | ); 29 | } 30 | 31 | CardAvatar.propTypes = { 32 | children: PropTypes.node.isRequired, 33 | className: PropTypes.string, 34 | profile: PropTypes.bool, 35 | plain: PropTypes.bool 36 | }; 37 | -------------------------------------------------------------------------------- /src/components/Card/Card.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | // @material-ui/core components 7 | import { makeStyles } from "@material-ui/core/styles"; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import styles from "assets/jss/material-dashboard-react/components/cardStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function Card(props) { 16 | const classes = useStyles(); 17 | const { className, children, plain, profile, chart, ...rest } = props; 18 | const cardClasses = classNames({ 19 | [classes.card]: true, 20 | [classes.cardPlain]: plain, 21 | [classes.cardProfile]: profile, 22 | [classes.cardChart]: chart, 23 | [className]: className !== undefined 24 | }); 25 | return ( 26 |
27 | {children} 28 |
29 | ); 30 | } 31 | 32 | Card.propTypes = { 33 | className: PropTypes.string, 34 | plain: PropTypes.bool, 35 | profile: PropTypes.bool, 36 | chart: PropTypes.bool, 37 | children: PropTypes.node 38 | }; 39 | -------------------------------------------------------------------------------- /src/views/Dashboard/HorizontalBarChart.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // react plugin for creating charts 3 | import { HorizontalBar } from 'react-chartjs-2'; 4 | import { blackColor, whiteColor } from "assets/jss/material-dashboard-react.js"; 5 | 6 | export default function HorizontalBarChart(props){ 7 | const { label, series, light } = props; 8 | const brushColor = light ? blackColor : whiteColor; 9 | var datasets = []; 10 | series.forEach( slice => { 11 | datasets.push({ 12 | label: slice.label, 13 | data: [slice.value], 14 | backgroundColor: slice.color, 15 | borderColor: brushColor, 16 | borderWidth: 1, 17 | stack: 'default', 18 | barPercentage: 0.5, 19 | maxBarThickness: 20, 20 | }); 21 | }); 22 | 23 | const chartData = { 24 | labels: [label], 25 | datasets: datasets, 26 | }; 27 | 28 | const chartOptions = { 29 | scales: { 30 | xAxes: [{ 31 | stacked: true, 32 | display: false, 33 | }], 34 | yAxes: [{ 35 | display: false, 36 | }], 37 | }, 38 | legend: { 39 | display: false, 40 | }, 41 | }; 42 | 43 | return ; 44 | } 45 | -------------------------------------------------------------------------------- /src/components/Card/CardIcon.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | // @material-ui/core components 7 | import { makeStyles } from "@material-ui/core/styles"; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import styles from "assets/jss/material-dashboard-react/components/cardIconStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function CardIcon(props) { 16 | const classes = useStyles(); 17 | const { className, children, color, ...rest } = props; 18 | const cardIconClasses = classNames({ 19 | [classes.cardIcon]: true, 20 | [classes[color + "CardHeader"]]: color, 21 | [className]: className !== undefined 22 | }); 23 | return ( 24 |
25 | {children} 26 |
27 | ); 28 | } 29 | 30 | CardIcon.propTypes = { 31 | className: PropTypes.string, 32 | color: PropTypes.oneOf([ 33 | "warning", 34 | "success", 35 | "danger", 36 | "info", 37 | "primary", 38 | "rose" 39 | ]), 40 | children: PropTypes.node 41 | }; 42 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/typographyStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | primaryColor, 4 | infoColor, 5 | successColor, 6 | warningColor, 7 | dangerColor, 8 | grayColor 9 | } from "assets/jss/material-dashboard-react.js"; 10 | 11 | const typographyStyle = { 12 | defaultFontStyle: { 13 | ...defaultFont, 14 | fontSize: "14px" 15 | }, 16 | defaultHeaderMargins: { 17 | marginTop: "20px", 18 | marginBottom: "10px" 19 | }, 20 | quote: { 21 | padding: "10px 20px", 22 | margin: "0 0 20px", 23 | fontSize: "17.5px", 24 | borderLeft: "5px solid " + grayColor[10] 25 | }, 26 | quoteText: { 27 | margin: "0 0 10px", 28 | fontStyle: "italic" 29 | }, 30 | quoteAuthor: { 31 | display: "block", 32 | fontSize: "80%", 33 | lineHeight: "1.42857143", 34 | color: grayColor[1] 35 | }, 36 | mutedText: { 37 | color: grayColor[1] 38 | }, 39 | primaryText: { 40 | color: primaryColor[0] 41 | }, 42 | infoText: { 43 | color: infoColor[0] 44 | }, 45 | successText: { 46 | color: successColor[0] 47 | }, 48 | warningText: { 49 | color: warningColor[0] 50 | }, 51 | dangerText: { 52 | color: dangerColor[0] 53 | } 54 | }; 55 | 56 | export default typographyStyle; 57 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/footerStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | container, 4 | primaryColor, 5 | grayColor 6 | } from "assets/jss/material-dashboard-react.js"; 7 | 8 | const footerStyle = { 9 | block: { 10 | color: "inherit", 11 | padding: "15px", 12 | textTransform: "uppercase", 13 | borderRadius: "3px", 14 | textDecoration: "none", 15 | position: "relative", 16 | display: "block", 17 | ...defaultFont, 18 | fontWeight: "500", 19 | fontSize: "12px" 20 | }, 21 | left: { 22 | float: "left!important", 23 | display: "block" 24 | }, 25 | right: { 26 | padding: "15px 0", 27 | margin: "0", 28 | fontSize: "14px", 29 | float: "right!important" 30 | }, 31 | footer: { 32 | bottom: "0", 33 | borderTop: "1px solid " + grayColor[11], 34 | padding: "15px 0", 35 | ...defaultFont 36 | }, 37 | container, 38 | a: { 39 | color: primaryColor, 40 | textDecoration: "none", 41 | backgroundColor: "transparent" 42 | }, 43 | list: { 44 | marginBottom: "0", 45 | padding: "0", 46 | marginTop: "0" 47 | }, 48 | inlineBlock: { 49 | display: "inline-block", 50 | padding: "0px", 51 | width: "auto" 52 | } 53 | }; 54 | export default footerStyle; 55 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardFooterStyle.js: -------------------------------------------------------------------------------- 1 | import { grayColor } from "assets/jss/material-dashboard-react.js"; 2 | 3 | const cardFooterStyle = { 4 | cardFooter: { 5 | padding: "0", 6 | paddingTop: "10px", 7 | margin: "0 15px 10px", 8 | borderRadius: "0", 9 | justifyContent: "space-between", 10 | alignItems: "center", 11 | display: "flex", 12 | backgroundColor: "transparent", 13 | border: "0" 14 | }, 15 | cardFooterProfile: { 16 | marginTop: "-15px" 17 | }, 18 | cardFooterPlain: { 19 | paddingLeft: "5px", 20 | paddingRight: "5px", 21 | backgroundColor: "transparent" 22 | }, 23 | cardFooterStats: { 24 | borderTop: "1px solid " + grayColor[10], 25 | marginTop: "20px", 26 | "& svg": { 27 | position: "relative", 28 | top: "4px", 29 | marginRight: "3px", 30 | marginLeft: "3px", 31 | width: "16px", 32 | height: "16px" 33 | }, 34 | "& .fab,& .fas,& .far,& .fal,& .material-icons": { 35 | fontSize: "16px", 36 | position: "relative", 37 | top: "4px", 38 | marginRight: "3px", 39 | marginLeft: "3px" 40 | } 41 | }, 42 | cardFooterChart: { 43 | borderTop: "1px solid " + grayColor[10] 44 | } 45 | }; 46 | 47 | export default cardFooterStyle; 48 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/checkboxAdnRadioStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | primaryColor, 3 | blackColor, 4 | hexToRgb 5 | } from "assets/jss/material-dashboard-react.js"; 6 | 7 | const checkboxAdnRadioStyle = { 8 | root: { 9 | padding: "13px", 10 | "&:hover": { 11 | backgroundColor: "unset" 12 | } 13 | }, 14 | labelRoot: { 15 | marginLeft: "-14px" 16 | }, 17 | checked: { 18 | color: primaryColor[0] + "!important" 19 | }, 20 | checkedIcon: { 21 | width: "20px", 22 | height: "20px", 23 | border: "1px solid rgba(" + hexToRgb(blackColor) + ", .54)", 24 | borderRadius: "3px" 25 | }, 26 | uncheckedIcon: { 27 | width: "0px", 28 | height: "0px", 29 | padding: "10px", 30 | border: "1px solid rgba(" + hexToRgb(blackColor) + ", .54)", 31 | borderRadius: "3px" 32 | }, 33 | radio: { 34 | color: primaryColor[0] + "!important" 35 | }, 36 | radioChecked: { 37 | width: "20px", 38 | height: "20px", 39 | border: "1px solid " + primaryColor[0], 40 | borderRadius: "50%" 41 | }, 42 | radioUnchecked: { 43 | width: "0px", 44 | height: "0px", 45 | padding: "10px", 46 | border: "1px solid rgba(" + hexToRgb(blackColor) + ", .54)", 47 | borderRadius: "50%" 48 | } 49 | }; 50 | 51 | export default checkboxAdnRadioStyle; 52 | -------------------------------------------------------------------------------- /src/components/Card/CardFooter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | // @material-ui/core components 7 | import { makeStyles } from "@material-ui/core/styles"; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import styles from "assets/jss/material-dashboard-react/components/cardFooterStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function CardFooter(props) { 16 | const classes = useStyles(); 17 | const { className, children, plain, profile, stats, chart, ...rest } = props; 18 | const cardFooterClasses = classNames({ 19 | [classes.cardFooter]: true, 20 | [classes.cardFooterPlain]: plain, 21 | [classes.cardFooterProfile]: profile, 22 | [classes.cardFooterStats]: stats, 23 | [classes.cardFooterChart]: chart, 24 | [className]: className !== undefined 25 | }); 26 | return ( 27 |
28 | {children} 29 |
30 | ); 31 | } 32 | 33 | CardFooter.propTypes = { 34 | className: PropTypes.string, 35 | plain: PropTypes.bool, 36 | profile: PropTypes.bool, 37 | stats: PropTypes.bool, 38 | chart: PropTypes.bool, 39 | children: PropTypes.node 40 | }; 41 | -------------------------------------------------------------------------------- /src/components/Card/CardHeader.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | // @material-ui/core components 7 | import { makeStyles } from "@material-ui/core/styles"; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import styles from "assets/jss/material-dashboard-react/components/cardHeaderStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function CardHeader(props) { 16 | const classes = useStyles(); 17 | const { className, children, color, plain, stats, icon, ...rest } = props; 18 | const cardHeaderClasses = classNames({ 19 | [classes.cardHeader]: true, 20 | [classes[color + "CardHeader"]]: color, 21 | [classes.cardHeaderPlain]: plain, 22 | [classes.cardHeaderStats]: stats, 23 | [classes.cardHeaderIcon]: icon, 24 | [className]: className !== undefined 25 | }); 26 | return ( 27 |
28 | {children} 29 |
30 | ); 31 | } 32 | 33 | CardHeader.propTypes = { 34 | className: PropTypes.string, 35 | color: PropTypes.oneOf([ 36 | "warning", 37 | "success", 38 | "danger", 39 | "info", 40 | "primary", 41 | "rose" 42 | ]), 43 | plain: PropTypes.bool, 44 | stats: PropTypes.bool, 45 | icon: PropTypes.bool, 46 | children: PropTypes.node 47 | }; 48 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/tasksStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | primaryColor, 4 | dangerColor, 5 | grayColor 6 | } from "assets/jss/material-dashboard-react.js"; 7 | import tooltipStyle from "assets/jss/material-dashboard-react/tooltipStyle.js"; 8 | import checkboxAdnRadioStyle from "assets/jss/material-dashboard-react/checkboxAdnRadioStyle.js"; 9 | const tasksStyle = { 10 | ...tooltipStyle, 11 | ...checkboxAdnRadioStyle, 12 | table: { 13 | marginBottom: "0", 14 | overflow: "visible" 15 | }, 16 | tableRow: { 17 | position: "relative", 18 | borderBottom: "1px solid " + grayColor[5] 19 | }, 20 | tableActions: { 21 | display: "flex", 22 | border: "none", 23 | padding: "12px 8px !important", 24 | verticalAlign: "middle" 25 | }, 26 | tableCell: { 27 | ...defaultFont, 28 | padding: "8px", 29 | verticalAlign: "middle", 30 | border: "none", 31 | lineHeight: "1.42857143", 32 | fontSize: "14px" 33 | }, 34 | tableCellRTL: { 35 | textAlign: "right" 36 | }, 37 | tableActionButton: { 38 | width: "27px", 39 | height: "27px", 40 | padding: "0" 41 | }, 42 | tableActionButtonIcon: { 43 | width: "17px", 44 | height: "17px" 45 | }, 46 | edit: { 47 | backgroundColor: "transparent", 48 | color: primaryColor[0], 49 | boxShadow: "none" 50 | }, 51 | close: { 52 | backgroundColor: "transparent", 53 | color: dangerColor[0], 54 | boxShadow: "none" 55 | } 56 | }; 57 | export default tasksStyle; 58 | -------------------------------------------------------------------------------- /src/views/Instances/Instances.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route } from "react-router-dom"; 3 | import InstancesInScope from "views/Instances/InstancesInScope.js"; 4 | // import ControlInstance from "views/Instances/ControlInstance.js"; 5 | import InstanceStatus from "views/Instances/InstanceStatus.js"; 6 | import Snapshots from "views/Instances/Snapshots.js"; 7 | import Details from "views/Instances/Details.js"; 8 | import AllInstances from "views/Instances/AllInstances.js"; 9 | import SecurityPolicies from "views/Instances/SecurityPolicies.js"; 10 | 11 | export default function Instances(props){ 12 | return ( 13 |
14 | React.createElement(AllInstances, props)}/> 15 | React.createElement(InstancesInScope, props)}/> 16 | React.createElement(InstanceStatus, { 17 | ...childrenProps, 18 | ...props, 19 | })}/> 20 | React.createElement(Snapshots, { 21 | ...childrenProps, 22 | ...props, 23 | })}/> 24 | React.createElement(Details, { 25 | ...childrenProps, 26 | ...props, 27 | })}/> 28 | React.createElement(SecurityPolicies, { 29 | ...childrenProps, 30 | ...props, 31 | })}/> 32 |
33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/customInputStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | primaryColor, 3 | dangerColor, 4 | successColor, 5 | grayColor, 6 | defaultFont 7 | } from "assets/jss/material-dashboard-react.js"; 8 | 9 | const customInputStyle = { 10 | disabled: { 11 | "&:before": { 12 | backgroundColor: "transparent !important" 13 | } 14 | }, 15 | underline: { 16 | "&:hover:not($disabled):before,&:before": { 17 | borderColor: grayColor[4] + " !important", 18 | borderWidth: "1px !important" 19 | }, 20 | "&:after": { 21 | borderColor: primaryColor[0] 22 | } 23 | }, 24 | underlineError: { 25 | "&:after": { 26 | borderColor: dangerColor[0] 27 | } 28 | }, 29 | underlineSuccess: { 30 | "&:after": { 31 | borderColor: successColor[0] 32 | } 33 | }, 34 | labelRoot: { 35 | ...defaultFont, 36 | color: grayColor[3] + " !important", 37 | fontWeight: "400", 38 | fontSize: "14px", 39 | lineHeight: "1.42857", 40 | letterSpacing: "unset" 41 | }, 42 | labelRootError: { 43 | color: dangerColor[0] 44 | }, 45 | labelRootSuccess: { 46 | color: successColor[0] 47 | }, 48 | feedback: { 49 | position: "absolute", 50 | top: "18px", 51 | right: "0", 52 | zIndex: "2", 53 | display: "block", 54 | width: "24px", 55 | height: "24px", 56 | textAlign: "center", 57 | pointerEvents: "none" 58 | }, 59 | marginTop: { 60 | marginTop: "16px" 61 | }, 62 | formControl: { 63 | paddingBottom: "10px", 64 | margin: "27px 0 0 0", 65 | position: "relative", 66 | verticalAlign: "unset" 67 | } 68 | }; 69 | 70 | export default customInputStyle; 71 | -------------------------------------------------------------------------------- /src/views/DiskImages/SyncDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { syncDiskImages } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Sync Local Disk Images', 8 | content: 'Are you sure to synchronize local disk images', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '同步本地磁盘镜像', 14 | content: '是否同步本地磁盘镜像文件', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function SyncDialog(props){ 21 | const { lang, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const content = texts.content; 27 | const onSyncFail = msg =>{ 28 | setOperatable(true); 29 | setPrompt(msg); 30 | } 31 | 32 | const closeDialog = ()=>{ 33 | setPrompt(''); 34 | onCancel(); 35 | } 36 | 37 | const onSyncSuccess = () =>{ 38 | setOperatable(true); 39 | setPrompt(''); 40 | onSuccess(); 41 | } 42 | 43 | const handleConfirm = () =>{ 44 | setOperatable(false); 45 | syncDiskImages(onSyncSuccess, onSyncFail); 46 | } 47 | 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleConfirm, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/views/MediaImages/SyncDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { syncMediaImages } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Sync Local Media Images', 8 | content: 'Are you sure to synchronize local media images', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '同步本地光盘镜像', 14 | content: '是否同步本地光盘镜像文件', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function SyncDialog(props){ 21 | const { lang, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const content = texts.content; 27 | const onSyncFail = msg =>{ 28 | setOperatable(true); 29 | setPrompt(msg); 30 | } 31 | 32 | const closeDialog = ()=>{ 33 | setPrompt(''); 34 | onCancel(); 35 | } 36 | 37 | const onSyncSuccess = () =>{ 38 | setOperatable(true); 39 | setPrompt(''); 40 | onSuccess(); 41 | } 42 | 43 | const handleConfirm = () =>{ 44 | setOperatable(false); 45 | syncMediaImages(onSyncSuccess, onSyncFail); 46 | } 47 | 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleConfirm, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/views/DiskImages/DeleteDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { deleteDiskImage } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Delete Disk Image', 8 | content: 'Are you sure to delete disk image ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '删除磁盘镜像', 14 | content: '是否删除磁盘镜像 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function DeleteDialog(props){ 21 | const { lang, imageID, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const content = texts.content + imageID; 27 | const onDeleteFail = (msg) =>{ 28 | setOperatable(true); 29 | setPrompt(msg); 30 | } 31 | 32 | const closeDialog = ()=>{ 33 | setPrompt(''); 34 | onCancel(); 35 | } 36 | 37 | const onDeleteSuccess = () =>{ 38 | setOperatable(true); 39 | setPrompt(''); 40 | onSuccess(imageID); 41 | } 42 | 43 | const handleConfirm = () =>{ 44 | setOperatable(false); 45 | deleteDiskImage(imageID, onDeleteSuccess, onDeleteFail); 46 | } 47 | 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleConfirm, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/views/Instances/DeleteDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { deleteInstance } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Delete Instance', 8 | content: 'Are you sure to delete instance ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '删除云主机', 14 | content: '是否删除云主机 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function DeleteDialog(props){ 21 | const { lang, instanceID, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const content = texts.content + instanceID; 27 | const onDeleteFail = (msg) =>{ 28 | setOperatable(true); 29 | setPrompt(msg); 30 | } 31 | 32 | const closeDialog = ()=>{ 33 | setPrompt(''); 34 | onCancel(); 35 | } 36 | 37 | const onDeleteSuccess = () =>{ 38 | setOperatable(true); 39 | setPrompt(''); 40 | onSuccess(instanceID); 41 | } 42 | 43 | const handleConfirm = () =>{ 44 | setOperatable(false); 45 | deleteInstance(instanceID, onDeleteSuccess, onDeleteFail); 46 | } 47 | 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleConfirm, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/views/MediaImages/DeleteDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { deleteMediaImage } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Delete Media Image', 8 | content: 'Are you sure to delete media image ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '删除光盘镜像', 14 | content: '是否删除光盘镜像 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function DeleteDialog(props){ 21 | const { lang, imageID, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const content = texts.content + imageID; 27 | const onDeleteFail = msg =>{ 28 | setOperatable(true); 29 | setPrompt(msg); 30 | } 31 | 32 | const closeDialog = ()=>{ 33 | setPrompt(''); 34 | onCancel(); 35 | } 36 | 37 | const onDeleteSuccess = () =>{ 38 | setOperatable(true); 39 | setPrompt(''); 40 | onSuccess(imageID); 41 | } 42 | 43 | const handleConfirm = () =>{ 44 | setOperatable(false); 45 | deleteMediaImage(imageID, onDeleteSuccess, onDeleteFail); 46 | } 47 | 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleConfirm, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/views/AddressPools/DeleteDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { deleteNetworkPool } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Delete Address Pool', 8 | content: 'Are you sure to delete address pool ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '删除地址资源池', 14 | content: '是否删除地址资源池 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function DeleteDialog(props){ 21 | const { lang, pool, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const onDeleteFail = msg =>{ 27 | setOperatable(true); 28 | setPrompt(msg); 29 | } 30 | 31 | const closeDialog = ()=>{ 32 | setPrompt(''); 33 | onCancel(); 34 | } 35 | 36 | const onDeleteSuccess = poolName =>{ 37 | setOperatable(true); 38 | setPrompt(''); 39 | onSuccess(poolName); 40 | } 41 | 42 | const handleDelete = () =>{ 43 | setOperatable(false); 44 | deleteNetworkPool(pool, onDeleteSuccess, onDeleteFail); 45 | } 46 | 47 | const content = texts.content + pool; 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleDelete, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/views/ComputePools/DeleteDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { deleteComputePool } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Delete Pool', 8 | content: 'Are you sure to delete compute pool ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '删除资源池', 14 | content: '是否删除计算资源池 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | const DeleteDialog = (props) =>{ 21 | const { lang, pool, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const content = texts.content + pool; 27 | const onDeleteFail = msg =>{ 28 | setOperatable(true); 29 | setPrompt(msg); 30 | } 31 | 32 | const closeDialog = ()=>{ 33 | setPrompt(''); 34 | onCancel(); 35 | } 36 | 37 | const onDeleteSuccess = poolName =>{ 38 | setOperatable(true); 39 | setPrompt(''); 40 | onSuccess(poolName); 41 | } 42 | 43 | const handleConfirm = () =>{ 44 | setOperatable(false); 45 | deleteComputePool(pool, onDeleteSuccess, onDeleteFail); 46 | } 47 | 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleConfirm, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | 65 | export default DeleteDialog; 66 | -------------------------------------------------------------------------------- /src/views/SystemTemplates/DeleteDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { deleteSystemTemplate } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Delete System Template', 8 | content: 'Are you sure to delete template ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '删除系统模板', 14 | content: '是否删除系统模板 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function DeleteDialog(props){ 21 | const { lang, templateID, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const onDeleteFail = msg =>{ 27 | setOperatable(true); 28 | setPrompt(msg); 29 | } 30 | 31 | const closeDialog = ()=>{ 32 | setPrompt(''); 33 | onCancel(); 34 | } 35 | 36 | const onDeleteSuccess = id =>{ 37 | setOperatable(true); 38 | setPrompt(''); 39 | onSuccess(templateID); 40 | } 41 | 42 | const handleConfirm = () =>{ 43 | setOperatable(false); 44 | deleteSystemTemplate(templateID, onDeleteSuccess, onDeleteFail); 45 | } 46 | 47 | const content = texts.content + templateID; 48 | 49 | const buttons = [ 50 | { 51 | color: 'transparent', 52 | label: texts.cancel, 53 | onClick: closeDialog, 54 | }, 55 | { 56 | color: 'info', 57 | label: texts.confirm, 58 | onClick: handleConfirm, 59 | }, 60 | ]; 61 | 62 | return ; 64 | }; 65 | -------------------------------------------------------------------------------- /src/views/StoragePools/DeleteDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { deleteStoragePool } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Delete Storage Pool', 8 | content: 'Are you sure to delete storage pool ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '删除存储资源池', 14 | content: '是否删除存储资源池 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | const DeleteDialog = (props) =>{ 21 | const { lang, pool, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const onDeleteFail = msg =>{ 27 | setOperatable(true); 28 | setPrompt(msg); 29 | } 30 | 31 | const closeDialog = ()=>{ 32 | setPrompt(''); 33 | onCancel(); 34 | } 35 | 36 | const onDeleteSuccess = poolName =>{ 37 | setOperatable(true); 38 | setPrompt(''); 39 | onSuccess(poolName); 40 | } 41 | 42 | const handleDelete = () =>{ 43 | setOperatable(false); 44 | deleteStoragePool(pool, onDeleteSuccess, onDeleteFail); 45 | } 46 | 47 | const content = texts.content + pool; 48 | 49 | const buttons = [ 50 | { 51 | color: 'transparent', 52 | label: texts.cancel, 53 | onClick: closeDialog, 54 | }, 55 | { 56 | color: 'info', 57 | label: texts.confirm, 58 | onClick: handleDelete, 59 | }, 60 | ]; 61 | 62 | return ; 64 | }; 65 | 66 | export default DeleteDialog; 67 | -------------------------------------------------------------------------------- /src/views/PolicyGroups/DeletePolicyDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { deleteSecurityPolicyGroup } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Delete Security Policy', 8 | content: 'Are you sure to delete security policy ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '删除安全策略', 14 | content: '是否删除安全策略 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function DeletePolicyDialog(props){ 21 | const { lang, policyID, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const onDeleteFail = msg =>{ 27 | setOperatable(true); 28 | setPrompt(msg); 29 | } 30 | 31 | const closeDialog = ()=>{ 32 | setPrompt(''); 33 | onCancel(); 34 | } 35 | 36 | const onDeleteSuccess = () =>{ 37 | setOperatable(true); 38 | setPrompt(''); 39 | onSuccess(policyID); 40 | } 41 | 42 | const handleDelete = () =>{ 43 | setOperatable(false); 44 | deleteSecurityPolicyGroup(policyID, onDeleteSuccess, onDeleteFail); 45 | } 46 | 47 | const content = texts.content + policyID; 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleDelete, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/views/Users/Main.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PersonIcon from '@material-ui/icons/Person'; 3 | import GroupIcon from '@material-ui/icons/Group'; 4 | import ListAltIcon from '@material-ui/icons/ListAlt'; 5 | 6 | import Tabs from "components/CustomTabs/CustomTabs.js"; 7 | import UserTab from "views/Users/UserTab"; 8 | import GroupTab from "views/Users/GroupTab"; 9 | import GroupMemberTab from "views/Users/GroupMemberTab"; 10 | import RoleTab from "views/Users/RoleTab"; 11 | 12 | const i18n = { 13 | 'en':{ 14 | title: 'Permissions', 15 | user: 'Users', 16 | group: 'User Groups', 17 | role: 'Roles', 18 | }, 19 | 'cn':{ 20 | title: '权限管理', 21 | user: '用户', 22 | group: '用户组', 23 | role: '角色', 24 | }, 25 | } 26 | 27 | const MutableGroupTab = props => { 28 | const { lang } = props; 29 | const [ group, setGroup ] = React.useState(''); 30 | if(group){ 31 | return setGroup('')}/>; 32 | }else{ 33 | return ; 34 | } 35 | } 36 | 37 | export default function Main(props){ 38 | const { lang } = props; 39 | const texts = i18n[lang]; 40 | return ( 41 | , 49 | }, 50 | { 51 | tabName: texts.group, 52 | tabIcon: GroupIcon, 53 | tabContent: , 54 | }, 55 | { 56 | tabName: texts.role, 57 | tabIcon: ListAltIcon, 58 | tabContent: , 59 | }, 60 | ]} 61 | /> 62 | ) 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/views/ComputeCells/RemoveDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { removeComputeCell } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Remove Cell', 8 | content: 'Are you sure to remove compute cell ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '移除资源节点', 14 | content: '是否移除资源节点 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | const RemoveDialog = (props) =>{ 21 | const { lang, pool, cell, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const content = texts.content + cell; 27 | const onRemoveFail = (msg) =>{ 28 | setOperatable(true); 29 | setPrompt(msg); 30 | } 31 | 32 | const closeDialog = ()=>{ 33 | setPrompt(''); 34 | onCancel(); 35 | } 36 | 37 | const onRemoveSuccess = (poolName, cellName) =>{ 38 | setOperatable(true); 39 | setPrompt(''); 40 | onSuccess(cellName); 41 | } 42 | 43 | const handleConfirm = () =>{ 44 | setOperatable(false); 45 | removeComputeCell(pool, cell, onRemoveSuccess, onRemoveFail); 46 | } 47 | 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleConfirm, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | 65 | export default RemoveDialog; 66 | -------------------------------------------------------------------------------- /src/views/Instances/ResetSecretDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { resetMonitorSecret } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Reset Monitor Secret', 8 | content: 'Are you sure to reset monitor secret of instance ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '重置监控密码', 14 | content: '是否重置云主机监控密码 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function ResetMonitorSecretDialog(props){ 21 | const { lang, guestID, guestName, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const content = texts.content + guestName; 27 | const onDeleteFail = msg =>{ 28 | setOperatable(true); 29 | setPrompt(msg); 30 | } 31 | 32 | const closeDialog = ()=>{ 33 | setPrompt(''); 34 | onCancel(); 35 | } 36 | 37 | const onDeleteSuccess = () =>{ 38 | setOperatable(true); 39 | setPrompt(''); 40 | onSuccess(guestID); 41 | } 42 | 43 | const handleConfirm = () =>{ 44 | setOperatable(false); 45 | resetMonitorSecret(guestID, onDeleteSuccess, onDeleteFail); 46 | } 47 | 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleConfirm, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/tableStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | warningColor, 3 | primaryColor, 4 | dangerColor, 5 | successColor, 6 | infoColor, 7 | roseColor, 8 | grayColor, 9 | defaultFont 10 | } from "assets/jss/material-dashboard-react.js"; 11 | 12 | const tableStyle = theme => ({ 13 | warningTableHeader: { 14 | color: warningColor[0] 15 | }, 16 | primaryTableHeader: { 17 | color: primaryColor[0] 18 | }, 19 | dangerTableHeader: { 20 | color: dangerColor[0] 21 | }, 22 | successTableHeader: { 23 | color: successColor[0] 24 | }, 25 | infoTableHeader: { 26 | color: infoColor[0] 27 | }, 28 | roseTableHeader: { 29 | color: roseColor[0] 30 | }, 31 | grayTableHeader: { 32 | color: grayColor[0] 33 | }, 34 | table: { 35 | marginBottom: "0", 36 | width: "100%", 37 | maxWidth: "100%", 38 | backgroundColor: "transparent", 39 | borderSpacing: "0", 40 | borderCollapse: "collapse" 41 | }, 42 | tableHeadCell: { 43 | color: "inherit", 44 | ...defaultFont, 45 | "&, &$tableCell": { 46 | fontSize: "1em" 47 | } 48 | }, 49 | tableCell: { 50 | ...defaultFont, 51 | lineHeight: "1.42857143", 52 | padding: "12px 8px", 53 | verticalAlign: "middle", 54 | fontSize: "0.8125rem" 55 | }, 56 | tableResponsive: { 57 | width: "100%", 58 | marginTop: theme.spacing(3), 59 | overflowX: "auto" 60 | }, 61 | tableHeadRow: { 62 | height: "56px", 63 | color: "inherit", 64 | display: "table-row", 65 | outline: "none", 66 | verticalAlign: "middle" 67 | }, 68 | tableBodyRow: { 69 | height: "48px", 70 | color: "inherit", 71 | display: "table-row", 72 | outline: "none", 73 | verticalAlign: "middle" 74 | } 75 | }); 76 | 77 | export default tableStyle; 78 | -------------------------------------------------------------------------------- /src/views/Instances/DeleteSnapshotDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { deleteInstanceSnapshot } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Delete Snapshot', 8 | content: 'Are you sure to delete snapshot ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '删除云主机快照', 14 | content: '是否删除云主机快照 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function DeleteSnapshotDialog(props){ 21 | const { lang, instanceID, snapshotName, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const content = texts.content + snapshotName; 27 | const onDeleteFail = (msg) =>{ 28 | setOperatable(true); 29 | setPrompt(msg); 30 | } 31 | 32 | const closeDialog = ()=>{ 33 | setPrompt(''); 34 | onCancel(); 35 | } 36 | 37 | const onDeleteSuccess = () =>{ 38 | setOperatable(true); 39 | setPrompt(''); 40 | onSuccess(snapshotName); 41 | } 42 | 43 | const handleConfirm = () =>{ 44 | setOperatable(false); 45 | deleteInstanceSnapshot(instanceID, snapshotName, onDeleteSuccess, onDeleteFail); 46 | } 47 | 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleConfirm, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/views/Instances/RevertSnapshotDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { restoreInstanceSnapshot } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Revert Snapshot', 8 | content: 'Are you sure to revert snapshot ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '恢复云主机快照', 14 | content: '是否恢复云主机快照 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function RevertSnapshotDialog(props){ 21 | const { lang, instanceID, snapshotName, open, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const content = texts.content + snapshotName; 27 | const onRevertFail = (msg) =>{ 28 | setOperatable(true); 29 | setPrompt(msg); 30 | } 31 | 32 | const closeDialog = ()=>{ 33 | setPrompt(''); 34 | onCancel(); 35 | } 36 | 37 | const onRevertSuccess = () =>{ 38 | setOperatable(true); 39 | setPrompt(''); 40 | onSuccess(snapshotName); 41 | } 42 | 43 | const handleConfirm = () =>{ 44 | setOperatable(false); 45 | restoreInstanceSnapshot(instanceID, snapshotName, onRevertSuccess, onRevertFail); 46 | } 47 | 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleConfirm, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/views/AddressPools/RemoveRangeDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { removeAddressRange } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Remove Address Range', 8 | content: 'Are you sure to remove address range ', 9 | cancel: 'Cancel', 10 | confirm: 'Confirm', 11 | }, 12 | 'cn':{ 13 | title: '删除地址段', 14 | content: '是否删除地址段 ', 15 | cancel: '取消', 16 | confirm: '确定', 17 | }, 18 | } 19 | 20 | export default function RemoveDialog(props){ 21 | const { lang, open, poolName, rangeType, startAddress, onSuccess, onCancel } = props; 22 | const [ operatable, setOperatable ] = React.useState(true); 23 | const [ prompt, setPrompt ] = React.useState(''); 24 | const texts = i18n[lang]; 25 | const title = texts.title; 26 | const onRemoveFail = msg =>{ 27 | setOperatable(true); 28 | setPrompt(msg); 29 | } 30 | 31 | const closeDialog = ()=>{ 32 | setPrompt(''); 33 | onCancel(); 34 | } 35 | 36 | const onRemoveSuccess = () =>{ 37 | setOperatable(true); 38 | setPrompt(''); 39 | onSuccess(rangeType, startAddress); 40 | } 41 | 42 | const handleRemove = () =>{ 43 | setOperatable(false); 44 | removeAddressRange(poolName, rangeType, startAddress, onRemoveSuccess, onRemoveFail); 45 | } 46 | 47 | const content = texts.content + startAddress; 48 | const buttons = [ 49 | { 50 | color: 'transparent', 51 | label: texts.cancel, 52 | onClick: closeDialog, 53 | }, 54 | { 55 | color: 'info', 56 | label: texts.confirm, 57 | onClick: handleRemove, 58 | }, 59 | ]; 60 | 61 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/customTabsStyle.js: -------------------------------------------------------------------------------- 1 | import { hexToRgb, whiteColor } from "assets/jss/material-dashboard-react.js"; 2 | 3 | const customTabsStyle = { 4 | cardTitle: { 5 | float: "left", 6 | padding: "10px 10px 10px 0px", 7 | lineHeight: "24px" 8 | }, 9 | cardTitleRTL: { 10 | float: "right", 11 | padding: "10px 0px 10px 10px !important" 12 | }, 13 | displayNone: { 14 | display: "none !important" 15 | }, 16 | tabsRoot: { 17 | minHeight: "unset !important", 18 | overflowX: "visible", 19 | "& $tabRootButton": { 20 | fontSize: "0.875rem" 21 | } 22 | }, 23 | tabRootButton: { 24 | minHeight: "unset !important", 25 | minWidth: "unset !important", 26 | width: "unset !important", 27 | height: "unset !important", 28 | maxWidth: "unset !important", 29 | maxHeight: "unset !important", 30 | padding: "10px 15px", 31 | borderRadius: "3px", 32 | lineHeight: "24px", 33 | border: "0 !important", 34 | color: whiteColor + " !important", 35 | marginLeft: "4px", 36 | "&:last-child": { 37 | marginLeft: "0px" 38 | } 39 | }, 40 | tabSelected: { 41 | backgroundColor: "rgba(" + hexToRgb(whiteColor) + ", 0.2)", 42 | transition: "0.2s background-color 0.1s" 43 | }, 44 | tabWrapper: { 45 | display: "inline-block", 46 | minHeight: "unset !important", 47 | minWidth: "unset !important", 48 | width: "unset !important", 49 | height: "unset !important", 50 | maxWidth: "unset !important", 51 | maxHeight: "unset !important", 52 | fontWeight: "500", 53 | fontSize: "12px", 54 | marginTop: "1px", 55 | "& > svg,& > .material-icons": { 56 | verticalAlign: "middle", 57 | margin: "-1px 5px 0 0 !important" 58 | } 59 | } 60 | }; 61 | 62 | export default customTabsStyle; 63 | -------------------------------------------------------------------------------- /src/views/Instances/RemoveRuleDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { removeGuestSecurityRule } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Remove Security Policy Rule', 8 | content: 'Are you sure to remove ', 9 | content2: 'th rule', 10 | cancel: 'Cancel', 11 | confirm: 'Confirm', 12 | }, 13 | 'cn':{ 14 | title: '删除安全规则', 15 | content: '是否删除第 ', 16 | content2: '个安全规则', 17 | cancel: '取消', 18 | confirm: '确定', 19 | }, 20 | } 21 | 22 | export default function RemoveDialog(props){ 23 | const { lang, open, guestID, index, onSuccess, onCancel } = props; 24 | const [ operatable, setOperatable ] = React.useState(true); 25 | const [ prompt, setPrompt ] = React.useState(''); 26 | const texts = i18n[lang]; 27 | const title = texts.title; 28 | const onRemoveFail = msg =>{ 29 | setOperatable(true); 30 | setPrompt(msg); 31 | } 32 | 33 | const closeDialog = ()=>{ 34 | setPrompt(''); 35 | onCancel(); 36 | } 37 | 38 | const onRemoveSuccess = () =>{ 39 | setOperatable(true); 40 | setPrompt(''); 41 | onSuccess(guestID, index); 42 | } 43 | 44 | const handleRemove = () =>{ 45 | setOperatable(false); 46 | removeGuestSecurityRule(guestID, index, onRemoveSuccess, onRemoveFail); 47 | } 48 | 49 | const content = texts.content + (index + 1) + texts.content2; 50 | const buttons = [ 51 | { 52 | color: 'transparent', 53 | label: texts.cancel, 54 | onClick: closeDialog, 55 | }, 56 | { 57 | color: 'info', 58 | label: texts.confirm, 59 | onClick: handleRemove, 60 | }, 61 | ]; 62 | 63 | return ; 65 | }; 66 | -------------------------------------------------------------------------------- /src/views/PolicyGroups/RemoveRuleDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { removeSecurityPolicyRule } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Remove Security Policy Rule', 8 | content: 'Are you sure to remove ', 9 | content2: 'th rule', 10 | cancel: 'Cancel', 11 | confirm: 'Confirm', 12 | }, 13 | 'cn':{ 14 | title: '删除安全规则', 15 | content: '是否删除第 ', 16 | content2: '个安全规则', 17 | cancel: '取消', 18 | confirm: '确定', 19 | }, 20 | } 21 | 22 | export default function RemoveDialog(props){ 23 | const { lang, open, policyID, index, onSuccess, onCancel } = props; 24 | const [ operatable, setOperatable ] = React.useState(true); 25 | const [ prompt, setPrompt ] = React.useState(''); 26 | const texts = i18n[lang]; 27 | const title = texts.title; 28 | const onRemoveFail = msg =>{ 29 | setOperatable(true); 30 | setPrompt(msg); 31 | } 32 | 33 | const closeDialog = ()=>{ 34 | setPrompt(''); 35 | onCancel(); 36 | } 37 | 38 | const onRemoveSuccess = () =>{ 39 | setOperatable(true); 40 | setPrompt(''); 41 | onSuccess(policyID, index); 42 | } 43 | 44 | const handleRemove = () =>{ 45 | setOperatable(false); 46 | removeSecurityPolicyRule(policyID, index, onRemoveSuccess, onRemoveFail); 47 | } 48 | 49 | const content = texts.content + (index + 1) + texts.content2; 50 | const buttons = [ 51 | { 52 | color: 'transparent', 53 | label: texts.cancel, 54 | onClick: closeDialog, 55 | }, 56 | { 57 | color: 'info', 58 | label: texts.confirm, 59 | onClick: handleRemove, 60 | }, 61 | ]; 62 | 63 | return ; 65 | }; 66 | -------------------------------------------------------------------------------- /src/components/Table/OperableTable.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | import Table from "@material-ui/core/Table"; 6 | import TableHead from "@material-ui/core/TableHead"; 7 | import TableRow from "@material-ui/core/TableRow"; 8 | import TableBody from "@material-ui/core/TableBody"; 9 | import TableCell from "@material-ui/core/TableCell"; 10 | // core components 11 | import styles from "assets/jss/material-dashboard-react/components/tableStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function OperableTable(props){ 16 | const classes = useStyles(); 17 | const { color, headers, rows } = props; 18 | return ( 19 |
20 | 21 | 22 | 23 | {headers.map((prop, key) => { 24 | return ( 25 | 29 | {prop} 30 | 31 | ); 32 | })} 33 | 34 | 35 | 36 | {rows} 37 | 38 |
39 |
40 | ) 41 | } 42 | 43 | OperableTable.defaultProps = { 44 | color: "gray" 45 | }; 46 | 47 | OperableTable.propTypes = { 48 | color: PropTypes.oneOf([ 49 | "warning", 50 | "primary", 51 | "danger", 52 | "success", 53 | "info", 54 | "rose", 55 | "gray" 56 | ]), 57 | headers: PropTypes.arrayOf(PropTypes.node), 58 | rows: PropTypes.arrayOf(PropTypes.object), 59 | }; 60 | -------------------------------------------------------------------------------- /src/components/Snackbar/SnackbarContent.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import classNames from "classnames"; 4 | // @material-ui/core components 5 | import { makeStyles } from "@material-ui/core/styles"; 6 | import Snack from "@material-ui/core/SnackbarContent"; 7 | import IconButton from "@material-ui/core/IconButton"; 8 | // @material-ui/icons 9 | import Close from "@material-ui/icons/Close"; 10 | // core components 11 | import styles from "assets/jss/material-dashboard-react/components/snackbarContentStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function SnackbarContent(props) { 16 | const classes = useStyles(); 17 | const { message, color, close, icon, rtlActive } = props; 18 | var action = []; 19 | const messageClasses = classNames({ 20 | [classes.iconMessage]: icon !== undefined 21 | }); 22 | if (close !== undefined) { 23 | action = [ 24 | 30 | 31 | 32 | ]; 33 | } 34 | return ( 35 | 38 | {icon !== undefined ? : null} 39 | {message} 40 | 41 | } 42 | classes={{ 43 | root: classes.root + " " + classes[color], 44 | message: classes.message, 45 | action: classNames({ [classes.actionRTL]: rtlActive }) 46 | }} 47 | action={action} 48 | /> 49 | ); 50 | } 51 | 52 | SnackbarContent.propTypes = { 53 | message: PropTypes.node.isRequired, 54 | color: PropTypes.oneOf(["info", "success", "warning", "danger", "primary"]), 55 | close: PropTypes.bool, 56 | icon: PropTypes.object, 57 | rtlActive: PropTypes.bool 58 | }; 59 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/headerStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | container, 3 | defaultFont, 4 | primaryColor, 5 | defaultBoxShadow, 6 | infoColor, 7 | successColor, 8 | warningColor, 9 | dangerColor, 10 | whiteColor, 11 | grayColor 12 | } from "assets/jss/material-dashboard-react.js"; 13 | 14 | const headerStyle = () => ({ 15 | appBar: { 16 | backgroundColor: "transparent", 17 | boxShadow: "none", 18 | borderBottom: "0", 19 | marginBottom: "0", 20 | position: "absolute", 21 | width: "100%", 22 | paddingTop: "10px", 23 | zIndex: "1029", 24 | color: grayColor[7], 25 | border: "0", 26 | borderRadius: "3px", 27 | padding: "10px 0", 28 | transition: "all 150ms ease 0s", 29 | minHeight: "50px", 30 | display: "block" 31 | }, 32 | container: { 33 | ...container, 34 | minHeight: "50px" 35 | }, 36 | flex: { 37 | flex: 1 38 | }, 39 | title: { 40 | ...defaultFont, 41 | letterSpacing: "unset", 42 | lineHeight: "30px", 43 | fontSize: "18px", 44 | borderRadius: "3px", 45 | textTransform: "none", 46 | color: "inherit", 47 | margin: "0", 48 | "&:hover,&:focus": { 49 | background: "transparent" 50 | } 51 | }, 52 | appResponsive: { 53 | top: "8px" 54 | }, 55 | primary: { 56 | backgroundColor: primaryColor[0], 57 | color: whiteColor, 58 | ...defaultBoxShadow 59 | }, 60 | info: { 61 | backgroundColor: infoColor[0], 62 | color: whiteColor, 63 | ...defaultBoxShadow 64 | }, 65 | success: { 66 | backgroundColor: successColor[0], 67 | color: whiteColor, 68 | ...defaultBoxShadow 69 | }, 70 | warning: { 71 | backgroundColor: warningColor[0], 72 | color: whiteColor, 73 | ...defaultBoxShadow 74 | }, 75 | danger: { 76 | backgroundColor: dangerColor[0], 77 | color: whiteColor, 78 | ...defaultBoxShadow 79 | } 80 | }); 81 | 82 | export default headerStyle; 83 | -------------------------------------------------------------------------------- /src/views/Dashboard/MultiBarCard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @material-ui/core 3 | import { makeStyles } from "@material-ui/core/styles"; 4 | import Typography from '@material-ui/core/Typography'; 5 | import Box from '@material-ui/core/Box'; 6 | 7 | import Card from "components/Card/Card.js"; 8 | import CardHeader from "components/Card/CardHeader.js"; 9 | import CardBody from "components/Card/CardBody.js"; 10 | import MultiBarChart from "views/Dashboard/MultiBarChart.js"; 11 | import { grayColor } from "assets/jss/material-dashboard-react.js"; 12 | import dashboardStyles from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; 13 | 14 | export default function MultiBarCard(props){ 15 | const {title, color, series, displayValue, minTickStep } = props; 16 | var styles = { 17 | ...dashboardStyles, 18 | topDivider: { 19 | borderTop: "1px solid " + grayColor[10], 20 | }, 21 | }; 22 | var lastValueLabels = []; 23 | series.forEach( ({ data, label }) => { 24 | const lastValue = data[data.length - 1]; 25 | let valueString; 26 | if (displayValue){ 27 | valueString = displayValue(lastValue); 28 | }else{ 29 | valueString = lastValue.toString(); 30 | } 31 | lastValueLabels.push(label + ': ' + valueString); 32 | }); 33 | 34 | const useStyles = makeStyles(styles); 35 | const classes = useStyles(); 36 | return ( 37 | 38 | 39 | 40 | 41 | 42 | 43 | {title} 44 | 45 | 46 | 47 | {lastValueLabels.join(' / ')} 48 | 49 | 50 | 51 | 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /src/components/CustomButtons/Button.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | 7 | // material-ui components 8 | import { makeStyles } from "@material-ui/core/styles"; 9 | import Button from "@material-ui/core/Button"; 10 | 11 | import styles from "assets/jss/material-dashboard-react/components/buttonStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function RegularButton(props) { 16 | const classes = useStyles(); 17 | const { 18 | color, 19 | round, 20 | children, 21 | disabled, 22 | simple, 23 | size, 24 | block, 25 | link, 26 | justIcon, 27 | className, 28 | muiClasses, 29 | ...rest 30 | } = props; 31 | const btnClasses = classNames({ 32 | [classes.button]: true, 33 | [classes[size]]: size, 34 | [classes[color]]: color, 35 | [classes.round]: round, 36 | [classes.disabled]: disabled, 37 | [classes.simple]: simple, 38 | [classes.block]: block, 39 | [classes.link]: link, 40 | [classes.justIcon]: justIcon, 41 | [className]: className 42 | }); 43 | return ( 44 | 47 | ); 48 | } 49 | 50 | RegularButton.propTypes = { 51 | color: PropTypes.oneOf([ 52 | "primary", 53 | "info", 54 | "success", 55 | "warning", 56 | "danger", 57 | "rose", 58 | "white", 59 | "transparent" 60 | ]), 61 | size: PropTypes.oneOf(["sm", "lg"]), 62 | simple: PropTypes.bool, 63 | round: PropTypes.bool, 64 | disabled: PropTypes.bool, 65 | block: PropTypes.bool, 66 | link: PropTypes.bool, 67 | justIcon: PropTypes.bool, 68 | className: PropTypes.string, 69 | // use this to pass the classes props from Material-UI 70 | muiClasses: PropTypes.object, 71 | children: PropTypes.node 72 | }; 73 | -------------------------------------------------------------------------------- /src/views/Dashboard/StackedBarCard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @material-ui/core 3 | import { makeStyles } from "@material-ui/core/styles"; 4 | import Typography from '@material-ui/core/Typography'; 5 | import Box from '@material-ui/core/Box'; 6 | 7 | import Card from "components/Card/Card.js"; 8 | import CardHeader from "components/Card/CardHeader.js"; 9 | import CardBody from "components/Card/CardBody.js"; 10 | import StackedBarChart from "views/Dashboard/StackedBarChart.js"; 11 | import { grayColor } from "assets/jss/material-dashboard-react.js"; 12 | import dashboardStyles from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; 13 | 14 | export default function StackedBarCard(props){ 15 | const {title, color, series, displayValue, minTickStep } = props; 16 | var styles = { 17 | ...dashboardStyles, 18 | topDivider: { 19 | borderTop: "1px solid " + grayColor[10], 20 | }, 21 | }; 22 | var lastValueLabels = []; 23 | series.forEach( ({ data, label }) => { 24 | const lastValue = data[data.length - 1]; 25 | let valueString; 26 | if (displayValue){ 27 | valueString = displayValue(lastValue); 28 | }else{ 29 | valueString = lastValue.toString(); 30 | } 31 | lastValueLabels.push(label + ': ' + valueString); 32 | }); 33 | 34 | const useStyles = makeStyles(styles); 35 | const classes = useStyles(); 36 | return ( 37 | 38 | 39 | 40 | 41 | 42 | 43 | {title} 44 | 45 | 46 | 47 | {lastValueLabels.join(' / ')} 48 | 49 | 50 | 51 | 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /src/views/Dashboard/LineCard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @material-ui/core 3 | import { makeStyles } from "@material-ui/core/styles"; 4 | import Typography from '@material-ui/core/Typography'; 5 | import Box from '@material-ui/core/Box'; 6 | 7 | import Card from "components/Card/Card.js"; 8 | import CardHeader from "components/Card/CardHeader.js"; 9 | import CardBody from "components/Card/CardBody.js"; 10 | import LineChart from "views/Dashboard/LineChart.js"; 11 | import { grayColor } from "assets/jss/material-dashboard-react.js"; 12 | import dashboardStyles from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; 13 | 14 | export default function LineCard(props){ 15 | const {title, color, series, displayValue, minTickStep, maxValue } = props; 16 | var styles = { 17 | ...dashboardStyles, 18 | topDivider: { 19 | borderTop: "1px solid " + grayColor[10], 20 | }, 21 | }; 22 | var lastValueLabels = []; 23 | series.forEach( ({ data, label }) => { 24 | const lastValue = data[data.length - 1]; 25 | let valueString; 26 | if (displayValue){ 27 | valueString = displayValue(lastValue); 28 | }else{ 29 | valueString = lastValue.toString(); 30 | } 31 | lastValueLabels.push(label + ': ' + valueString); 32 | }); 33 | 34 | const useStyles = makeStyles(styles); 35 | const classes = useStyles(); 36 | return ( 37 | 38 | 39 | 40 | 41 | 42 | 43 | {title} 44 | 45 | 46 | 47 | {lastValueLabels.join(' / ') + ' / ' + maxValue.toString()} 48 | 49 | 50 | 51 | 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /src/views/Dashboard/SeriesLabels.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { makeStyles } from "@material-ui/core/styles"; 3 | import Typography from '@material-ui/core/Typography'; 4 | import Box from '@material-ui/core/Box'; 5 | 6 | export default function SeriesLabels(props){ 7 | const {title, series, valueName, colorName, labelName, displayValue, baseClass } = props; 8 | let styles = {}; 9 | if (baseClass){ 10 | styles.title = { 11 | ...baseClass, 12 | } 13 | }else { 14 | styles.title = {}; 15 | } 16 | 17 | series.forEach( (slice, index) => { 18 | const seriesName = 'series-' + index.toString(); 19 | if (baseClass){ 20 | styles[seriesName] = { 21 | ...baseClass, 22 | color: slice[colorName], 23 | }; 24 | }else{ 25 | styles[seriesName] = { 26 | color: slice[colorName], 27 | }; 28 | } 29 | 30 | }); 31 | 32 | const useStyles = makeStyles(styles); 33 | const classes = useStyles(); 34 | return ( 35 | 36 | 37 | 38 | {title} 39 | 40 | 41 | { 42 | series.map((slice, index) =>{ 43 | const sliceValue = slice[valueName]; 44 | const sliceLabel = slice[labelName]; 45 | const sliceClassName = 'series-' + index.toString(); 46 | let valueLabel; 47 | if (displayValue){ 48 | valueLabel = displayValue(sliceValue); 49 | }else{ 50 | valueLabel = sliceValue.toString(); 51 | } 52 | return( 53 | 54 | 55 | {sliceLabel} 56 | 57 | 58 | {': ' + valueLabel} 59 | 60 | 61 | ) 62 | }) 63 | } 64 | 65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable*/ 2 | import React from "react"; 3 | import PropTypes from "prop-types"; 4 | // @material-ui/core components 5 | import { makeStyles } from "@material-ui/core/styles"; 6 | import ListItem from "@material-ui/core/ListItem"; 7 | import List from "@material-ui/core/List"; 8 | // core components 9 | import styles from "assets/jss/material-dashboard-react/components/footerStyle.js"; 10 | 11 | const useStyles = makeStyles(styles); 12 | 13 | export default function Footer(props) { 14 | const classes = useStyles(); 15 | return ( 16 | 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/views/dashboardStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | successColor, 3 | whiteColor, 4 | grayColor, 5 | hexToRgb 6 | } from "assets/jss/material-dashboard-react.js"; 7 | 8 | const dashboardStyle = { 9 | successText: { 10 | color: successColor[0] 11 | }, 12 | upArrowCardCategory: { 13 | width: "16px", 14 | height: "16px" 15 | }, 16 | stats: { 17 | color: grayColor[0], 18 | display: "inline-flex", 19 | fontSize: "12px", 20 | lineHeight: "22px", 21 | "& svg": { 22 | top: "4px", 23 | width: "16px", 24 | height: "16px", 25 | position: "relative", 26 | marginRight: "3px", 27 | marginLeft: "3px" 28 | }, 29 | "& .fab,& .fas,& .far,& .fal,& .material-icons": { 30 | top: "4px", 31 | fontSize: "16px", 32 | position: "relative", 33 | marginRight: "3px", 34 | marginLeft: "3px" 35 | } 36 | }, 37 | cardCategory: { 38 | color: grayColor[0], 39 | margin: "0", 40 | fontSize: "14px", 41 | marginTop: "0", 42 | paddingTop: "10px", 43 | marginBottom: "0" 44 | }, 45 | cardCategoryWhite: { 46 | color: "rgba(" + hexToRgb(whiteColor) + ",.62)", 47 | margin: "0", 48 | fontSize: "14px", 49 | marginTop: "0", 50 | marginBottom: "0" 51 | }, 52 | cardTitle: { 53 | color: grayColor[2], 54 | marginTop: "0px", 55 | minHeight: "auto", 56 | fontWeight: "300", 57 | fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", 58 | marginBottom: "3px", 59 | textDecoration: "none", 60 | "& small": { 61 | color: grayColor[1], 62 | fontWeight: "400", 63 | lineHeight: "1" 64 | } 65 | }, 66 | cardTitleWhite: { 67 | color: whiteColor, 68 | marginTop: "0px", 69 | minHeight: "auto", 70 | fontWeight: "300", 71 | fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", 72 | marginBottom: "3px", 73 | textDecoration: "none", 74 | "& small": { 75 | color: grayColor[1], 76 | fontWeight: "400", 77 | lineHeight: "1" 78 | } 79 | } 80 | }; 81 | 82 | export default dashboardStyle; 83 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/views/rtlStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | successColor, 3 | whiteColor, 4 | grayColor, 5 | hexToRgb 6 | } from "assets/jss/material-dashboard-react.js"; 7 | 8 | const rtlStyle = { 9 | successText: { 10 | color: successColor[0] 11 | }, 12 | upArrowCardCategory: { 13 | width: "16px", 14 | height: "16px" 15 | }, 16 | stats: { 17 | color: grayColor[0], 18 | display: "inline-flex", 19 | fontSize: "12px", 20 | lineHeight: "22px", 21 | "& svg": { 22 | top: "4px", 23 | width: "16px", 24 | height: "16px", 25 | position: "relative", 26 | marginRight: "3px", 27 | marginLeft: "3px" 28 | }, 29 | "& .fab,& .fas,& .far,& .fal,& .material-icons": { 30 | top: "4px", 31 | fontSize: "16px", 32 | position: "relative", 33 | marginRight: "3px", 34 | marginLeft: "3px" 35 | } 36 | }, 37 | cardCategory: { 38 | color: grayColor[0], 39 | margin: "0", 40 | fontSize: "14px", 41 | marginTop: "0", 42 | paddingTop: "10px", 43 | marginBottom: "0" 44 | }, 45 | cardCategoryWhite: { 46 | color: "rgba(" + hexToRgb(whiteColor) + ",.62)", 47 | margin: "0", 48 | fontSize: "14px", 49 | marginTop: "0", 50 | marginBottom: "0", 51 | "& a": { 52 | color: whiteColor 53 | } 54 | }, 55 | cardTitle: { 56 | color: grayColor[2], 57 | marginTop: "0px", 58 | minHeight: "auto", 59 | fontWeight: "300", 60 | fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", 61 | marginBottom: "3px", 62 | textDecoration: "none", 63 | "& small": { 64 | color: grayColor[1], 65 | fontWeight: "400", 66 | lineHeight: "1" 67 | } 68 | }, 69 | cardTitleWhite: { 70 | color: whiteColor, 71 | marginTop: "0px", 72 | minHeight: "auto", 73 | fontWeight: "300", 74 | fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", 75 | marginBottom: "3px", 76 | textDecoration: "none", 77 | "& small": { 78 | color: grayColor[1], 79 | fontWeight: "400", 80 | lineHeight: "1" 81 | } 82 | } 83 | }; 84 | 85 | export default rtlStyle; 86 | -------------------------------------------------------------------------------- /src/views/Dashboard/MultiBarChart.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // react plugin for creating charts 3 | import { Bar } from 'react-chartjs-2'; 4 | // @material-ui/core 5 | import { blackColor, whiteColor } from "assets/jss/material-dashboard-react.js"; 6 | 7 | export default function MultiBarChart(props){ 8 | const DefaultMaxTicks = 10; 9 | const { series, minTickStep, displayValue, light, maxTicks } = props; 10 | const maxTicksValue = maxTicks? maxTicks : DefaultMaxTicks; 11 | const brushColor = light ? blackColor : whiteColor; 12 | const dataCount = series[0].data.length; 13 | const labels = new Array(dataCount).fill(''); 14 | const minValue = 0; 15 | var maxValue = minTickStep; 16 | var datasets = []; 17 | series.forEach( dataSeries => { 18 | dataSeries.data.forEach( value =>{ 19 | maxValue = Math.max(maxValue, value); 20 | }) 21 | datasets.push({ 22 | data: dataSeries.data, 23 | label: dataSeries.label, 24 | backgroundColor: dataSeries.color, 25 | barPercentage: 0.6, 26 | borderColor: brushColor, 27 | borderWidth: 1, 28 | }) 29 | }); 30 | 31 | const chartData = { 32 | labels: labels, 33 | datasets: datasets, 34 | }; 35 | 36 | let tickStep; 37 | if (maxValue <= maxTicksValue * minTickStep){ 38 | tickStep = minTickStep; 39 | }else{ 40 | tickStep = Math.ceil(maxValue / maxTicksValue / minTickStep) * minTickStep; 41 | } 42 | 43 | const chartOptions = { 44 | scales: { 45 | xAxes: [{ 46 | display: false, 47 | }], 48 | yAxes: [{ 49 | gridLines:{ 50 | borderDash: [2, 4], 51 | color: brushColor, 52 | zeroLineColor: brushColor, 53 | zeroLineWidth: 2, 54 | }, 55 | ticks: { 56 | stepSize: tickStep, 57 | fontColor: brushColor, 58 | suggestedMax: maxValue, 59 | suggestedMin: minValue, 60 | callback: value => { 61 | if (displayValue){ 62 | return displayValue(value); 63 | }else { 64 | return value.toString(); 65 | } 66 | } 67 | }, 68 | }], 69 | }, 70 | legend: { 71 | display: false, 72 | }, 73 | }; 74 | return ; 75 | } 76 | -------------------------------------------------------------------------------- /src/views/Instances/VncDisplay.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RFB from '@novnc/novnc'; 3 | 4 | export default function VncDisplay(props){ 5 | const { url, password, callback, onFocusChanged } = props; 6 | const canvas = React.createRef(); 7 | const [ connection, setConnection ] = React.useState(null); 8 | const [ mounted, setMounted ] = React.useState(false); 9 | const [ initialed, setInitialed ] = React.useState(false); 10 | 11 | const activateFocus = () =>{ 12 | if (!mounted){ 13 | return; 14 | } 15 | if (!connection){ 16 | return; 17 | } 18 | connection.focus(); 19 | onFocusChanged(true); 20 | }; 21 | 22 | const deactivateFocus = () =>{ 23 | if (!mounted){ 24 | return; 25 | } 26 | if (!connection){ 27 | return; 28 | } 29 | connection.blur(); 30 | onFocusChanged(false); 31 | }; 32 | 33 | const sendEmergencyKeys = () =>{ 34 | if (!mounted){ 35 | return; 36 | } 37 | if (!connection){ 38 | return; 39 | } 40 | connection.sendCtrlAltDel(); 41 | }; 42 | 43 | const clickScreen = e => { 44 | e.preventDefault(); 45 | activateFocus(); 46 | } 47 | 48 | React.useEffect(()=>{ 49 | if (!canvas ||!canvas.current || !url || !password){ 50 | return; 51 | } 52 | 53 | setMounted(true); 54 | if (!initialed){ 55 | const options = { 56 | credentials: { 57 | password: password, 58 | }, 59 | clipViewport: true, 60 | focusOnClick: false, 61 | // scaleViewport: true, 62 | qualityLevel: 8, 63 | }; 64 | 65 | 66 | var conn = new RFB(canvas.current, url, options); 67 | // const disconnect = () =>{ 68 | // conn.disconnect(); 69 | // setConnection(null); 70 | // } 71 | // conn.focus(); 72 | setConnection(conn); 73 | setInitialed(true); 74 | } 75 | 76 | return () => { 77 | // disconnect(); 78 | setMounted(false); 79 | } 80 | }, [canvas, password, url, initialed]); 81 | 82 | //render 83 | //bind callback 84 | callback.onEmergency = sendEmergencyKeys; 85 | return ( 86 |
92 | ) 93 | } 94 | -------------------------------------------------------------------------------- /src/components/Table/ObjectTable.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | import Table from "@material-ui/core/Table"; 6 | import TableHead from "@material-ui/core/TableHead"; 7 | import TableRow from "@material-ui/core/TableRow"; 8 | import TableBody from "@material-ui/core/TableBody"; 9 | import TableCell from "@material-ui/core/TableCell"; 10 | // core components 11 | import styles from "assets/jss/material-dashboard-react/components/tableStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function ObjectTable(props){ 16 | const classes = useStyles(); 17 | const { color, headers, rows } = props; 18 | return ( 19 |
20 | 21 | 22 | 23 | {headers.map((prop, key) => { 24 | return ( 25 | 29 | {prop} 30 | 31 | ); 32 | })} 33 | 34 | 35 | 36 | { 37 | rows.map((row, rowKey) => ( 38 | 39 | { 40 | row.map((cell, cellKey) => ( 41 | 42 | {cell} 43 | 44 | )) 45 | } 46 | 47 | )) 48 | } 49 | 50 |
51 |
52 | ) 53 | } 54 | 55 | ObjectTable.defaultProps = { 56 | color: "gray" 57 | }; 58 | 59 | ObjectTable.propTypes = { 60 | color: PropTypes.oneOf([ 61 | "warning", 62 | "primary", 63 | "danger", 64 | "success", 65 | "info", 66 | "rose", 67 | "gray" 68 | ]), 69 | headers: PropTypes.arrayOf(PropTypes.node), 70 | rows: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.node)), 71 | }; 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nano-portal", 3 | "version": "1.4.0", 4 | "description": "Project-Nano manage portal", 5 | "service": { 6 | "host": "", 7 | "port": 5870, 8 | "debug": false 9 | }, 10 | "private": false, 11 | "dependencies": { 12 | "@material-ui/core": "^4.11.0", 13 | "@material-ui/icons": "^4.11.0", 14 | "@material-ui/lab": "4.0.0-alpha.61", 15 | "@novnc/novnc": "^1.2.0", 16 | "axios": "^0.19.2", 17 | "chart.js": "^2.9.4", 18 | "chartjs-plugin-streaming": "^1.8.0", 19 | "classnames": "^2.2.6", 20 | "dateformat": "^3.0.3", 21 | "history": "^4.10.1", 22 | "perfect-scrollbar": "^1.5.0", 23 | "prop-types": "^15.7.2", 24 | "react": "^16.14.0", 25 | "react-chartjs-2": "^2.11.1", 26 | "react-dom": "^16.14.0", 27 | "react-router-dom": "^5.2.0", 28 | "react-scripts": "^3.4.4", 29 | "react-swipeable-views": "^0.13.9", 30 | "typeface-roboto": "^0.0.75" 31 | }, 32 | "scripts": { 33 | "start": "react-scripts start", 34 | "build": "react-scripts build", 35 | "test": "react-scripts test --env=jsdom", 36 | "eject": "react-scripts eject", 37 | "install:clean": "rm -rf node_modules/ && rm -rf package-lock.json && npm install && npm start", 38 | "lint:check": "eslint . --ext=js,jsx; exit 0", 39 | "lint:fix": "eslint . --ext=js,jsx --fix; exit 0", 40 | "build-package-css": "cp src/assets/css/material-dashboard-react.css dist/material-dashboard-react.css", 41 | "build-package": "npm run build-package-css && babel src --out-dir dist" 42 | }, 43 | "repository": { 44 | "type": "git", 45 | "url": "git+https://github.com/project-nano" 46 | }, 47 | "keywords": [], 48 | "author": "Akumas <6408741@qq.com> (https://nanos.cloud/)", 49 | "license": "MIT", 50 | "bugs": { 51 | "url": "https://github.com/project-nano/releases/issues" 52 | }, 53 | "homepage": "https://nanos.cloud/", 54 | "optionalDependencies": { 55 | "@types/googlemaps": "3.37.3", 56 | "@types/markerclustererplus": "2.1.33", 57 | "ajv": "6.10.2", 58 | "typescript": "3.5.3" 59 | }, 60 | "devDependencies": { 61 | "eslint-config-prettier": "6.0.0", 62 | "eslint-plugin-prettier": "3.1.0", 63 | "gulp": "4.0.2", 64 | "gulp-append-prepend": "1.0.8", 65 | "prettier": "1.18.2" 66 | }, 67 | "browserslist": { 68 | "production": [ 69 | ">0.2%", 70 | "not dead", 71 | "not op_mini all" 72 | ], 73 | "development": [] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/components/Snackbar/Snackbar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import classNames from "classnames"; 3 | import PropTypes from "prop-types"; 4 | // @material-ui/core components 5 | import { makeStyles } from "@material-ui/core/styles"; 6 | import Snack from "@material-ui/core/Snackbar"; 7 | import IconButton from "@material-ui/core/IconButton"; 8 | // @material-ui/icons 9 | import Close from "@material-ui/icons/Close"; 10 | // core components 11 | import styles from "assets/jss/material-dashboard-react/components/snackbarContentStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function Snackbar(props) { 16 | const classes = useStyles(); 17 | const { message, color, close, icon, place, open, rtlActive } = props; 18 | var action = []; 19 | const messageClasses = classNames({ 20 | [classes.iconMessage]: icon !== undefined 21 | }); 22 | if (close !== undefined) { 23 | action = [ 24 | props.closeNotification()} 30 | > 31 | 32 | 33 | ]; 34 | } 35 | return ( 36 | 49 | {icon !== undefined ? : null} 50 | {message} 51 |
52 | } 53 | action={action} 54 | ContentProps={{ 55 | classes: { 56 | root: classes.root + " " + classes[color], 57 | message: classes.message, 58 | action: classNames({ [classes.actionRTL]: rtlActive }) 59 | } 60 | }} 61 | /> 62 | ); 63 | } 64 | 65 | Snackbar.propTypes = { 66 | message: PropTypes.node.isRequired, 67 | color: PropTypes.oneOf(["info", "success", "warning", "danger", "primary"]), 68 | close: PropTypes.bool, 69 | icon: PropTypes.object, 70 | place: PropTypes.oneOf(["tl", "tr", "tc", "br", "bl", "bc"]), 71 | open: PropTypes.bool, 72 | rtlActive: PropTypes.bool, 73 | closeNotification: PropTypes.func 74 | }; 75 | -------------------------------------------------------------------------------- /src/components/Dialog/CustomDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @material-ui/core components 3 | import Box from "@material-ui/core/Box"; 4 | import Grid from "@material-ui/core/Grid"; 5 | import Dialog from '@material-ui/core/Dialog'; 6 | import DialogActions from '@material-ui/core/DialogActions'; 7 | import DialogContent from '@material-ui/core/DialogContent'; 8 | import DialogTitle from '@material-ui/core/DialogTitle'; 9 | import CircularProgress from '@material-ui/core/CircularProgress'; 10 | import Backdrop from 'components/Backdrop/Backdrop'; 11 | import Button from "components/CustomButtons/Button.js"; 12 | import GridItem from "components/Grid/GridItem.js"; 13 | import SnackbarContent from "components/Snackbar/SnackbarContent.js"; 14 | 15 | export default function CustomDialog(props){ 16 | const { open, size, operatable, promptPosition, prompt, title, content, buttons, hideBackdrop} = props; 17 | let promptElement; 18 | if (prompt){ 19 | promptElement = ( 20 | 21 | 22 | 23 | ); 24 | }else{ 25 | promptElement = ; 26 | } 27 | let contentElement; 28 | if ('top' === promptPosition){ 29 | contentElement = ( 30 | 31 | {promptElement} 32 | 33 | {content} 34 | 35 | 36 | ); 37 | }else{ 38 | contentElement = ( 39 | 40 | 41 | {content} 42 | 43 | {promptElement} 44 | 45 | ); 46 | } 47 | var operates = []; 48 | if (operatable){ 49 | buttons.forEach((button, key) => { 50 | operates.push( 51 | 54 | ) 55 | }); 56 | } 57 | return ( 58 | 63 | {title} 64 | 65 | 66 | 67 | 68 | {contentElement} 69 | 70 | 71 | 72 | {operates} 73 | 74 | 75 | 76 | ) 77 | }; 78 | -------------------------------------------------------------------------------- /src/components/Table/Table.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | import Table from "@material-ui/core/Table"; 6 | import TableHead from "@material-ui/core/TableHead"; 7 | import TableRow from "@material-ui/core/TableRow"; 8 | import TableBody from "@material-ui/core/TableBody"; 9 | import TableCell from "@material-ui/core/TableCell"; 10 | // core components 11 | import styles from "assets/jss/material-dashboard-react/components/tableStyle.js"; 12 | 13 | const useStyles = makeStyles(styles); 14 | 15 | export default function CustomTable(props) { 16 | const classes = useStyles(); 17 | const { tableHead, tableData, tableHeaderColor } = props; 18 | return ( 19 |
20 | 21 | {tableHead !== undefined ? ( 22 | 23 | 24 | {tableHead.map((prop, key) => { 25 | return ( 26 | 30 | {prop} 31 | 32 | ); 33 | })} 34 | 35 | 36 | ) : null} 37 | 38 | {tableData.map((prop, key) => { 39 | return ( 40 | 41 | {prop.map((prop, key) => { 42 | return ( 43 | 44 | {prop} 45 | 46 | ); 47 | })} 48 | 49 | ); 50 | })} 51 | 52 |
53 |
54 | ); 55 | } 56 | 57 | CustomTable.defaultProps = { 58 | tableHeaderColor: "gray" 59 | }; 60 | 61 | CustomTable.propTypes = { 62 | tableHeaderColor: PropTypes.oneOf([ 63 | "warning", 64 | "primary", 65 | "danger", 66 | "success", 67 | "info", 68 | "rose", 69 | "gray" 70 | ]), 71 | tableHead: PropTypes.arrayOf(PropTypes.string), 72 | tableData: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)) 73 | }; 74 | -------------------------------------------------------------------------------- /src/views/Dashboard/StackedBarChart.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // react plugin for creating charts 3 | import { Bar } from 'react-chartjs-2'; 4 | // @material-ui/core 5 | import { blackColor, whiteColor } from "assets/jss/material-dashboard-react.js"; 6 | 7 | export default function StackedBarChart(props){ 8 | const DefaultMaxTicks = 10; 9 | const { series, minTickStep, displayValue, light, maxTicks } = props; 10 | const maxTicksValue = maxTicks? maxTicks : DefaultMaxTicks; 11 | const brushColor = light ? blackColor : whiteColor; 12 | const dataCount = series[0].data.length; 13 | const labels = new Array(dataCount).fill(''); 14 | const minValue = 0; 15 | var maxValue = minTickStep; 16 | var datasets = []; 17 | series.forEach( dataSeries => { 18 | datasets.push({ 19 | data: dataSeries.data, 20 | label: dataSeries.label, 21 | backgroundColor: dataSeries.color, 22 | barPercentage: 0.6, 23 | borderColor: brushColor, 24 | borderWidth: 1, 25 | stack: 'default', 26 | }) 27 | }); 28 | 29 | //max value for summary 30 | for (var i = 0; i < dataCount; i++){ 31 | var total = 0; 32 | for (var j = 0; j < series.length; j++){ 33 | total += series[j].data[i]; 34 | } 35 | maxValue = Math.max(maxValue, total); 36 | } 37 | 38 | const chartData = { 39 | labels: labels, 40 | datasets: datasets, 41 | }; 42 | 43 | let tickStep; 44 | if (maxValue <= maxTicksValue * minTickStep){ 45 | tickStep = minTickStep; 46 | }else{ 47 | tickStep = Math.ceil(maxValue / maxTicksValue / minTickStep) * minTickStep; 48 | } 49 | 50 | const chartOptions = { 51 | scales: { 52 | xAxes: [{ 53 | display: false, 54 | }], 55 | yAxes: [{ 56 | stacked: true, 57 | gridLines:{ 58 | borderDash: [2, 4], 59 | color: brushColor, 60 | zeroLineColor: brushColor, 61 | zeroLineWidth: 2, 62 | }, 63 | ticks: { 64 | stepSize: tickStep, 65 | fontColor: brushColor, 66 | suggestedMax: maxValue, 67 | suggestedMin: minValue, 68 | callback: value => { 69 | if (displayValue){ 70 | return displayValue(value); 71 | }else { 72 | return value.toString(); 73 | } 74 | } 75 | }, 76 | }], 77 | }, 78 | legend: { 79 | display: false, 80 | }, 81 | }; 82 | return ; 83 | } 84 | -------------------------------------------------------------------------------- /src/views/Users/DeleteUserDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @material-ui/core components 3 | import Grid from "@material-ui/core/Grid"; 4 | import Dialog from '@material-ui/core/Dialog'; 5 | import DialogActions from '@material-ui/core/DialogActions'; 6 | import DialogContent from '@material-ui/core/DialogContent'; 7 | import DialogTitle from '@material-ui/core/DialogTitle'; 8 | 9 | // dashboard components 10 | import Button from "components/CustomButtons/Button.js"; 11 | import GridItem from "components/Grid/GridItem.js"; 12 | import SnackbarContent from "components/Snackbar/SnackbarContent.js"; 13 | import { deleteUser } from 'nano_api.js'; 14 | 15 | const i18n = { 16 | 'en':{ 17 | title: 'Delete User', 18 | content: 'Are you sure to delete user ', 19 | cancel: 'Cancel', 20 | confirm: 'Confirm', 21 | }, 22 | 'cn':{ 23 | title: '删除用户', 24 | content: '是否删除用户 ', 25 | cancel: '取消', 26 | confirm: '确定', 27 | }, 28 | } 29 | 30 | export default function DeleteUserDialog(props){ 31 | const { lang, name, open, onSuccess, onCancel } = props; 32 | const [ error, setError ] = React.useState(''); 33 | const texts = i18n[lang]; 34 | const onDeleteFail = (msg) =>{ 35 | setError(msg); 36 | } 37 | 38 | const closeDialog = ()=>{ 39 | setError(''); 40 | onCancel(); 41 | } 42 | 43 | const onDeleteSuccess = (name) =>{ 44 | setError(''); 45 | onSuccess(name); 46 | } 47 | 48 | const confirmDelete = () =>{ 49 | deleteUser(name, onDeleteSuccess, onDeleteFail); 50 | } 51 | 52 | 53 | //begin render 54 | let prompt; 55 | if (!error || '' === error){ 56 | prompt = ; 57 | }else{ 58 | prompt = ( 59 | 60 | 61 | 62 | ); 63 | } 64 | 65 | return ( 66 | 72 | {texts.title} 73 | 74 | 75 | 76 | {texts.content + name} 77 | 78 | {prompt} 79 | 80 | 81 | 82 | 85 | 88 | 89 | 90 | ) 91 | }; 92 | -------------------------------------------------------------------------------- /src/views/Users/RemoveGroupDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @material-ui/core components 3 | import Grid from "@material-ui/core/Grid"; 4 | import Dialog from '@material-ui/core/Dialog'; 5 | import DialogActions from '@material-ui/core/DialogActions'; 6 | import DialogContent from '@material-ui/core/DialogContent'; 7 | import DialogTitle from '@material-ui/core/DialogTitle'; 8 | 9 | // dashboard components 10 | import Button from "components/CustomButtons/Button.js"; 11 | import GridItem from "components/Grid/GridItem.js"; 12 | import SnackbarContent from "components/Snackbar/SnackbarContent.js"; 13 | import { removeGroup } from 'nano_api.js'; 14 | 15 | const i18n = { 16 | 'en':{ 17 | title: 'Remove Group', 18 | content: 'Are you sure to remove group ', 19 | cancel: 'Cancel', 20 | confirm: 'Confirm', 21 | }, 22 | 'cn':{ 23 | title: '删除用户组', 24 | content: '是否删除用户组 ', 25 | cancel: '取消', 26 | confirm: '确定', 27 | }, 28 | } 29 | 30 | export default function RemoveGroupDialog(props){ 31 | const { lang, name, open, onSuccess, onCancel } = props; 32 | const [ error, setError ] = React.useState(''); 33 | const texts = i18n[lang]; 34 | const onRemoveFail = (msg) =>{ 35 | setError(msg); 36 | } 37 | 38 | const closeDialog = ()=>{ 39 | setError(''); 40 | onCancel(); 41 | } 42 | 43 | const onRemoveSuccess = name =>{ 44 | setError(''); 45 | onSuccess(name); 46 | } 47 | 48 | const confirmRemove = () =>{ 49 | removeGroup(name, onRemoveSuccess, onRemoveFail); 50 | } 51 | 52 | //begin render 53 | let prompt; 54 | if (!error || '' === error){ 55 | prompt = ; 56 | }else{ 57 | prompt = ( 58 | 59 | 60 | 61 | ); 62 | } 63 | 64 | return ( 65 | 71 | {texts.title} 72 | 73 | 74 | 75 | {texts.content + name} 76 | 77 | {prompt} 78 | 79 | 80 | 81 | 84 | 87 | 88 | 89 | ) 90 | }; 91 | -------------------------------------------------------------------------------- /src/views/Users/RemoveRoleDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @material-ui/core components 3 | import Grid from "@material-ui/core/Grid"; 4 | import Dialog from '@material-ui/core/Dialog'; 5 | import DialogActions from '@material-ui/core/DialogActions'; 6 | import DialogContent from '@material-ui/core/DialogContent'; 7 | import DialogTitle from '@material-ui/core/DialogTitle'; 8 | 9 | // dashboard components 10 | import Button from "components/CustomButtons/Button.js"; 11 | import GridItem from "components/Grid/GridItem.js"; 12 | import SnackbarContent from "components/Snackbar/SnackbarContent.js"; 13 | import { removeRole } from 'nano_api.js'; 14 | 15 | const i18n = { 16 | 'en':{ 17 | title: 'Remove Role', 18 | content: 'Are you sure to remove role ', 19 | cancel: 'Cancel', 20 | confirm: 'Confirm', 21 | }, 22 | 'cn':{ 23 | title: '删除角色', 24 | content: '是否删除角色 ', 25 | cancel: '取消', 26 | confirm: '确定', 27 | }, 28 | } 29 | 30 | export default function RemoveRoleDialog(props){ 31 | const { lang, name, open, onSuccess, onCancel } = props; 32 | const [ error, setError ] = React.useState(''); 33 | const texts = i18n[lang]; 34 | const onRemoveFail = (msg) =>{ 35 | setError(msg); 36 | } 37 | 38 | const closeDialog = ()=>{ 39 | setError(''); 40 | onCancel(); 41 | } 42 | 43 | const onRemoveSuccess = (name) =>{ 44 | setError(''); 45 | onSuccess(name); 46 | } 47 | 48 | const confirmRemove = () =>{ 49 | removeRole(name, onRemoveSuccess, onRemoveFail); 50 | } 51 | 52 | 53 | //begin render 54 | let prompt; 55 | if (!error || '' === error){ 56 | prompt = ; 57 | }else{ 58 | prompt = ( 59 | 60 | 61 | 62 | ); 63 | } 64 | 65 | return ( 66 | 72 | {texts.title} 73 | 74 | 75 | 76 | {texts.content + name} 77 | 78 | {prompt} 79 | 80 | 81 | 82 | 85 | 88 | 89 | 90 | ) 91 | }; 92 | -------------------------------------------------------------------------------- /src/components/CustomInput/CustomInput.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import classNames from "classnames"; 3 | import PropTypes from "prop-types"; 4 | // @material-ui/core components 5 | import { makeStyles } from "@material-ui/core/styles"; 6 | import FormControl from "@material-ui/core/FormControl"; 7 | import InputLabel from "@material-ui/core/InputLabel"; 8 | import Input from "@material-ui/core/Input"; 9 | // @material-ui/icons 10 | import Clear from "@material-ui/icons/Clear"; 11 | import Check from "@material-ui/icons/Check"; 12 | // core components 13 | import styles from "assets/jss/material-dashboard-react/components/customInputStyle.js"; 14 | 15 | const useStyles = makeStyles(styles); 16 | 17 | export default function CustomInput(props) { 18 | const classes = useStyles(); 19 | const { 20 | formControlProps, 21 | labelText, 22 | id, 23 | labelProps, 24 | inputProps, 25 | error, 26 | success 27 | } = props; 28 | 29 | const labelClasses = classNames({ 30 | [" " + classes.labelRootError]: error, 31 | [" " + classes.labelRootSuccess]: success && !error 32 | }); 33 | const underlineClasses = classNames({ 34 | [classes.underlineError]: error, 35 | [classes.underlineSuccess]: success && !error, 36 | [classes.underline]: true 37 | }); 38 | const marginTop = classNames({ 39 | [classes.marginTop]: labelText === undefined 40 | }); 41 | return ( 42 | 46 | {labelText !== undefined ? ( 47 | 52 | {labelText} 53 | 54 | ) : null} 55 | 64 | {error ? ( 65 | 66 | ) : success ? ( 67 | 68 | ) : null} 69 | 70 | ); 71 | } 72 | 73 | CustomInput.propTypes = { 74 | labelText: PropTypes.node, 75 | labelProps: PropTypes.object, 76 | id: PropTypes.string, 77 | inputProps: PropTypes.object, 78 | formControlProps: PropTypes.object, 79 | error: PropTypes.bool, 80 | success: PropTypes.bool 81 | }; 82 | -------------------------------------------------------------------------------- /src/views/Users/RemoveMemberDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @material-ui/core components 3 | import Grid from "@material-ui/core/Grid"; 4 | import Dialog from '@material-ui/core/Dialog'; 5 | import DialogActions from '@material-ui/core/DialogActions'; 6 | import DialogContent from '@material-ui/core/DialogContent'; 7 | import DialogTitle from '@material-ui/core/DialogTitle'; 8 | 9 | // dashboard components 10 | import Button from "components/CustomButtons/Button.js"; 11 | import GridItem from "components/Grid/GridItem.js"; 12 | import SnackbarContent from "components/Snackbar/SnackbarContent.js"; 13 | import { removeGroupMember } from 'nano_api.js'; 14 | 15 | const i18n = { 16 | 'en':{ 17 | title: 'Remove Group Member', 18 | content: 'Are you sure to remove member ', 19 | cancel: 'Cancel', 20 | confirm: 'Confirm', 21 | }, 22 | 'cn':{ 23 | title: '删除成员', 24 | content: '是否删除用户组成员 ', 25 | cancel: '取消', 26 | confirm: '确定', 27 | }, 28 | } 29 | 30 | export default function RemoveMemberDialog(props){ 31 | const { lang, group, member, open, onSuccess, onCancel } = props; 32 | const [ error, setError ] = React.useState(''); 33 | const texts = i18n[lang]; 34 | const onRemoveFail = (msg) =>{ 35 | setError(msg); 36 | } 37 | 38 | const closeDialog = ()=>{ 39 | setError(''); 40 | onCancel(); 41 | } 42 | 43 | const onRemoveSuccess = () =>{ 44 | setError(''); 45 | onSuccess(member, group); 46 | } 47 | 48 | const confirmRemove = () =>{ 49 | removeGroupMember(group, member, onRemoveSuccess, onRemoveFail); 50 | } 51 | 52 | //begin render 53 | let prompt; 54 | if (!error || '' === error){ 55 | prompt = ; 56 | }else{ 57 | prompt = ( 58 | 59 | 60 | 61 | ); 62 | } 63 | 64 | return ( 65 | 71 | {texts.title} 72 | 73 | 74 | 75 | {texts.content + member} 76 | 77 | {prompt} 78 | 79 | 80 | 81 | 84 | 87 | 88 | 89 | ) 90 | }; 91 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/views/Instances/ShrinkDiskSizeDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomDialog from "components/Dialog/CustomDialog.js"; 3 | import { shrinkInstanceDisk } from 'nano_api.js'; 4 | 5 | const i18n = { 6 | 'en':{ 7 | title: 'Shrink Disk Size', 8 | content1: 'Are you sure to shrink size of ', 9 | content2: ' ? it will take a long time, please be patient and ignore the timeout warning.', 10 | cancel: 'Cancel', 11 | confirm: 'Confirm', 12 | systemDisk: 'System Disk', 13 | dataDisk: 'Data Disk', 14 | }, 15 | 'cn':{ 16 | title: '压缩磁盘容量', 17 | content1: '是否压缩 ', 18 | content2: ' 的磁盘空间,这会占用很长时间,请忽略超时提示并耐心等待', 19 | cancel: '取消', 20 | confirm: '确定', 21 | systemDisk: '系统磁盘', 22 | dataDisk: '数据磁盘', 23 | }, 24 | } 25 | 26 | export default function ShrinkDiskSizeDialog(props){ 27 | const { lang, instanceID, index, open, onSuccess, onCancel } = props; 28 | const [ operatable, setOperatable ] = React.useState(true); 29 | const [ prompt, setPrompt ] = React.useState(''); 30 | const [ mounted, setMounted ] = React.useState(false); 31 | const texts = i18n[lang]; 32 | const title = texts.title; 33 | 34 | const onShrinkFail = React.useCallback(msg =>{ 35 | if(!mounted){ 36 | return; 37 | } 38 | setOperatable(true); 39 | setPrompt(msg); 40 | }, [mounted]); 41 | 42 | const closeDialog = () =>{ 43 | setPrompt(''); 44 | onCancel(); 45 | } 46 | 47 | const onShrinkSuccess = diskIndex =>{ 48 | if(!mounted){ 49 | return; 50 | } 51 | setOperatable(true); 52 | setPrompt(''); 53 | onSuccess(diskIndex, instanceID); 54 | } 55 | 56 | const handleConfirm = () =>{ 57 | setPrompt(''); 58 | setOperatable(false); 59 | shrinkInstanceDisk(instanceID, index, onShrinkSuccess, onShrinkFail); 60 | } 61 | 62 | React.useEffect(()=>{ 63 | if (!open){ 64 | return; 65 | } 66 | setMounted(true); 67 | return ()=> setMounted(false); 68 | }, [open]); 69 | 70 | let content; 71 | if (0 === index){ 72 | content = texts.content1 + texts.systemDisk + texts.content2; 73 | }else{ 74 | content = texts.content1 + texts.dataDisk + index.toString() + texts.content2; 75 | } 76 | const buttons = [ 77 | { 78 | color: 'transparent', 79 | label: texts.cancel, 80 | onClick: closeDialog, 81 | }, 82 | { 83 | color: 'info', 84 | label: texts.confirm, 85 | onClick: handleConfirm, 86 | }, 87 | ]; 88 | return ; 90 | }; 91 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require("gulp"); 2 | const gap = require("gulp-append-prepend"); 3 | 4 | gulp.task("licenses", async function() { 5 | // this is to add Creative Tim licenses in the production mode for the minified js 6 | gulp 7 | .src("build/static/js/*chunk.js", { base: "./" }) 8 | .pipe( 9 | gap.prependText(`/*! 10 | 11 | ========================================================= 12 | * Material Dashboard React - v1.8.0 13 | ========================================================= 14 | 15 | * Product Page: https://www.creative-tim.com/product/now-ui-kit-pro-react 16 | * Copyright 2019 Creative Tim (http://www.creative-tim.com) 17 | 18 | * Coded by Creative Tim 19 | 20 | ========================================================= 21 | 22 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 23 | 24 | */`) 25 | ) 26 | .pipe(gulp.dest("./", { overwrite: true })); 27 | 28 | // this is to add Creative Tim licenses in the production mode for the minified html 29 | gulp 30 | .src("build/index.html", { base: "./" }) 31 | .pipe( 32 | gap.prependText(``) 48 | ) 49 | .pipe(gulp.dest("./", { overwrite: true })); 50 | 51 | // this is to add Creative Tim licenses in the production mode for the minified css 52 | gulp 53 | .src("build/static/css/*chunk.css", { base: "./" }) 54 | .pipe( 55 | gap.prependText(`/*! 56 | 57 | ========================================================= 58 | * Material Dashboard React - v1.8.0 59 | ========================================================= 60 | 61 | * Product Page: https://www.creative-tim.com/product/now-ui-kit-pro-react 62 | * Copyright 2019 Creative Tim (http://www.creative-tim.com) 63 | 64 | * Coded by Creative Tim 65 | 66 | ========================================================= 67 | 68 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 69 | 70 | */`) 71 | ) 72 | .pipe(gulp.dest("./", { overwrite: true })); 73 | return; 74 | }); 75 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 34 | 35 | 36 | 41 | 42 | 51 | Project Nano 52 | 53 | 54 | 55 |
56 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/views/Logs/BatchDeleteDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @material-ui/core components 3 | import Grid from "@material-ui/core/Grid"; 4 | import Dialog from '@material-ui/core/Dialog'; 5 | import DialogActions from '@material-ui/core/DialogActions'; 6 | import DialogContent from '@material-ui/core/DialogContent'; 7 | import DialogTitle from '@material-ui/core/DialogTitle'; 8 | 9 | // dashboard components 10 | import Button from "components/CustomButtons/Button.js"; 11 | import GridItem from "components/Grid/GridItem.js"; 12 | import SnackbarContent from "components/Snackbar/SnackbarContent.js"; 13 | import { deleteLog } from 'nano_api.js'; 14 | 15 | const i18n = { 16 | 'en':{ 17 | title: 'Batch Deleting Log', 18 | content1: 'Are you sure to delete ', 19 | content2: ' log(s)', 20 | cancel: 'Cancel', 21 | confirm: 'Confirm', 22 | }, 23 | 'cn':{ 24 | title: '批量删除日志', 25 | content1: '是否删除 ', 26 | content2: ' 条日志', 27 | cancel: '取消', 28 | confirm: '确定', 29 | }, 30 | } 31 | 32 | export default function BatchDeleteDialog(props){ 33 | const { lang, targets, open, onSuccess, onCancel } = props; 34 | const count = targets.length; 35 | const [ error, setError ] = React.useState(''); 36 | const texts = i18n[lang]; 37 | const onDeleteFail = (msg) =>{ 38 | setError(msg); 39 | } 40 | 41 | const closeDialog = ()=>{ 42 | setError(''); 43 | onCancel(); 44 | } 45 | 46 | const onDeleteSuccess = () =>{ 47 | setError(''); 48 | onSuccess(count); 49 | } 50 | 51 | const confirmDelete = () =>{ 52 | deleteLog(targets, onDeleteSuccess, onDeleteFail); 53 | } 54 | 55 | //begin render 56 | let prompt; 57 | if (!error || '' === error){ 58 | prompt = ; 59 | }else{ 60 | prompt = ( 61 | 62 | 63 | 64 | ); 65 | } 66 | 67 | return ( 68 | 74 | {texts.title} 75 | 76 | 77 | 78 | {texts.content1 + count.toString() + texts.content2} 79 | 80 | {prompt} 81 | 82 | 83 | 84 | 87 | 90 | 91 | 92 | ) 93 | }; 94 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [1.4.0] - 2023-11-02 4 | 5 | ### 新增 6 | 7 | - 登录页面增加系统错误提示 8 | - package.json新增参数service.host/service.port用于设置跨域时的后端服务地址 9 | - 未添加资源节点时进行提示 10 | 11 | ### 修改 12 | 13 | - 切换到yarn编译 14 | - 更新版权日期 15 | - 仅允许.iso文件上传为光盘镜像 16 | - 仅允许.qcow2文件上传为磁盘镜像 17 | - 新建云主机时优化参数配置界面,允许从Frontend接口读取用户自定最大值 18 | - 新建云主机时,默认选择第一个资源池和系统模板 19 | 20 | ### Added 21 | 22 | - Prompt on login page when system error 23 | - Add service.host/service.port to 'package.json' to configure CORS backend connnection 24 | - Prompt add first resource node if not available 25 | 26 | ### Changed 27 | 28 | - Migrate to yarn 29 | - Copyright updated to current year 30 | - Only allow '.iso' files to upload for media images 31 | - Only allow '.qcow2' files to upload for disk images 32 | - Read cores/memory/disk limit via frontend on page of creating instances 33 | - Optimize core/memory/disk configure in creating page 34 | - Using first pool by default when creating instance 35 | - Using first system template by default when creating instance 36 | 37 | ## [1.3.1] - 2021-02-22 38 | 39 | ### Added 40 | 41 | - Set auto start in guest detail 42 | - Search and pagination in instance list 43 | 44 | ### Changed 45 | 46 | - Display hosting cell IP instead of name if available 47 | 48 | ## [1.3.0] - 2020-11-21 49 | 50 | ### Added 51 | 52 | - Synchronize disk/media images from local storage 53 | - Guest policy rule management 54 | - Security policies management 55 | - Creating instances with security policy 56 | - Allocate addresses using Cloud-Init 57 | 58 | ### Changed 59 | 60 | - Optimize the control page of the instance 61 | - Update npm dependencies 62 | 63 | ### Fixed 64 | 65 | - Can't send ctrl+alt+delete on the monitor page 66 | - Unstable key focus on the monitor page 67 | - Images list crashed when no tags available 68 | 69 | ## [1.2.0] - 2020-04-27 70 | 71 | ### Added 72 | 73 | - System templates management 74 | - Create guest using templates 75 | - Change the storage path of Cell 76 | - Reset monitor secret of guest 77 | 78 | ### Changed 79 | 80 | - Optimize most pages for data list and dialog 81 | - Optimize keyboard focus for vnc page 82 | 83 | ### Fixed 84 | 85 | - Wrong disk space when value is integer 86 | - Set admin password fail 87 | - Crash when observing resource of guest without qga installed 88 | 89 | ## [1.1.0] - 2020-01-01 90 | 91 | ### Added 92 | 93 | - Version and manual on Navbar 94 | - LanguageSelector on Login/Initial page 95 | 96 | ### Changed 97 | 98 | - Logo / Sidebar background 99 | 100 | ### Fixed 101 | 102 | - Login / Initial on small devices 103 | - Prompt wrong message when create/reset instance success 104 | -------------------------------------------------------------------------------- /src/views/Dashboard/PieCard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // react plugin for creating charts 3 | // @material-ui/core 4 | import { makeStyles } from "@material-ui/core/styles"; 5 | import Typography from '@material-ui/core/Typography'; 6 | import Box from '@material-ui/core/Box'; 7 | 8 | import Card from "components/Card/Card.js"; 9 | import CardHeader from "components/Card/CardHeader.js"; 10 | import CardBody from "components/Card/CardBody.js"; 11 | import PieChart from "views/Dashboard/PieChart.js"; 12 | import { grayColor } from "assets/jss/material-dashboard-react.js"; 13 | import dashboardStyles from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; 14 | 15 | export default function PieCard(props){ 16 | const {title, series, displayValue } = props; 17 | var total = 0; 18 | var chartStyles = { 19 | ...dashboardStyles, 20 | }; 21 | 22 | series.forEach( (slice, index) => { 23 | //slice => {value, color, label} 24 | total += slice.value; 25 | const seriesName = 'series-' + index.toString(); 26 | chartStyles[seriesName] = { 27 | ...dashboardStyles.cardCategory, 28 | color: slice.color, 29 | }; 30 | }); 31 | 32 | chartStyles.topDivider = { 33 | borderTop: "1px solid " + grayColor[10], 34 | } 35 | 36 | const useStyles = makeStyles(chartStyles); 37 | const classes = useStyles(); 38 | let totalLabel; 39 | if(displayValue){ 40 | totalLabel = displayValue(total); 41 | }else{ 42 | totalLabel = total.toString(); 43 | } 44 | return ( 45 | 46 | 47 | 48 | {title + ': ' + totalLabel} 49 | 50 | 51 | 52 | 53 | 54 | 55 | { 56 | series.map((slice, index) =>{ 57 | let valueLabel; 58 | if (displayValue){ 59 | valueLabel = displayValue(slice.value); 60 | }else{ 61 | valueLabel = slice.value.toString(); 62 | } 63 | return( 64 | 65 | 66 | {slice.label} 67 | 68 | 69 | {': ' + valueLabel} 70 | 71 | 72 | ) 73 | }) 74 | } 75 | 76 | 77 | 78 | 79 | ) 80 | } 81 | -------------------------------------------------------------------------------- /src/views/Dashboard/LineChart.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // react plugin for creating charts 3 | import { Line } from 'react-chartjs-2'; 4 | // @material-ui/core 5 | import { blackColor, whiteColor } from "assets/jss/material-dashboard-react.js"; 6 | 7 | export default function LineChart(props){ 8 | const DefaultMaxTicks = 10; 9 | const { series, minTickStep, displayValue, light, maxTicks, maxValue } = props; 10 | const maxTicksValue = maxTicks? maxTicks : DefaultMaxTicks; 11 | const brushColor = light ? blackColor : whiteColor; 12 | const dataCount = series[0].data.length; 13 | const labels = new Array(dataCount).fill(''); 14 | const minValue = 0; 15 | const maxValueFixed = maxValue? true: false; 16 | let maxValueOfAll; 17 | if(maxValueFixed){ 18 | maxValueOfAll = maxValue; 19 | }else{ 20 | maxValueOfAll = minTickStep; 21 | } 22 | 23 | var datasets = []; 24 | series.forEach( dataSeries => { 25 | if (!maxValueFixed){ 26 | dataSeries.data.forEach( value =>{ 27 | maxValueOfAll = Math.max(maxValueOfAll, value); 28 | }) 29 | } 30 | datasets.push({ 31 | data: dataSeries.data, 32 | label: dataSeries.label, 33 | pointBackgroundColor: dataSeries.color, 34 | pointBorderColor: dataSeries.color, 35 | pointRadius: 5, 36 | borderColor: brushColor, 37 | borderWidth: 4, 38 | lineTension: 0.0, 39 | }) 40 | }); 41 | 42 | const chartData = { 43 | labels: labels, 44 | datasets: datasets, 45 | }; 46 | 47 | let tickStep; 48 | if (maxValueOfAll <= maxTicksValue * minTickStep){ 49 | tickStep = minTickStep; 50 | }else{ 51 | tickStep = Math.ceil(maxValueOfAll / maxTicksValue / minTickStep) * minTickStep; 52 | } 53 | 54 | const chartOptions = { 55 | scales: { 56 | xAxes: [{ 57 | gridLines:{ 58 | drawBorder: false, 59 | lineWidth: 0, 60 | zeroLineColor: brushColor, 61 | zeroLineWidth: 2, 62 | }, 63 | }], 64 | yAxes: [{ 65 | gridLines:{ 66 | borderDash: [2, 4], 67 | color: brushColor, 68 | zeroLineColor: brushColor, 69 | zeroLineWidth: 2, 70 | drawBorder: false, 71 | }, 72 | ticks: { 73 | stepSize: tickStep, 74 | fontColor: brushColor, 75 | suggestedMax: maxValueOfAll, 76 | suggestedMin: minValue, 77 | callback: value => { 78 | if (displayValue){ 79 | return displayValue(value); 80 | }else { 81 | return value.toString(); 82 | } 83 | } 84 | }, 85 | }], 86 | }, 87 | legend: { 88 | display: false, 89 | }, 90 | }; 91 | return ; 92 | } 93 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/headerLinksStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | dangerColor, 4 | whiteColor 5 | } from "assets/jss/material-dashboard-react.js"; 6 | 7 | import dropdownStyle from "assets/jss/material-dashboard-react/dropdownStyle.js"; 8 | 9 | const headerLinksStyle = theme => ({ 10 | ...dropdownStyle(theme), 11 | search: { 12 | "& > div": { 13 | marginTop: "0" 14 | }, 15 | [theme.breakpoints.down("sm")]: { 16 | margin: "10px 15px !important", 17 | float: "none !important", 18 | paddingTop: "1px", 19 | paddingBottom: "1px", 20 | padding: "0!important", 21 | width: "60%", 22 | marginTop: "40px", 23 | "& input": { 24 | color: whiteColor 25 | } 26 | } 27 | }, 28 | linkText: { 29 | zIndex: "4", 30 | ...defaultFont, 31 | fontSize: "14px", 32 | margin: "0px" 33 | }, 34 | buttonLink: { 35 | [theme.breakpoints.down("sm")]: { 36 | display: "flex", 37 | margin: "10px 15px 0", 38 | width: "-webkit-fill-available", 39 | "& svg": { 40 | width: "24px", 41 | height: "30px", 42 | marginRight: "15px", 43 | marginLeft: "-15px" 44 | }, 45 | "& .fab,& .fas,& .far,& .fal,& .material-icons": { 46 | fontSize: "24px", 47 | lineHeight: "30px", 48 | width: "24px", 49 | height: "30px", 50 | marginRight: "15px", 51 | marginLeft: "-15px" 52 | }, 53 | "& > span": { 54 | justifyContent: "flex-start", 55 | width: "100%" 56 | } 57 | } 58 | }, 59 | searchButton: { 60 | [theme.breakpoints.down("sm")]: { 61 | top: "-50px !important", 62 | marginRight: "22px", 63 | float: "right" 64 | } 65 | }, 66 | margin: { 67 | zIndex: "4", 68 | margin: "0" 69 | }, 70 | searchIcon: { 71 | width: "17px", 72 | zIndex: "4" 73 | }, 74 | notifications: { 75 | zIndex: "4", 76 | [theme.breakpoints.up("md")]: { 77 | position: "absolute", 78 | top: "2px", 79 | border: "1px solid " + whiteColor, 80 | right: "4px", 81 | fontSize: "9px", 82 | background: dangerColor[0], 83 | color: whiteColor, 84 | minWidth: "16px", 85 | height: "16px", 86 | borderRadius: "10px", 87 | textAlign: "center", 88 | lineHeight: "16px", 89 | verticalAlign: "middle", 90 | display: "block" 91 | }, 92 | [theme.breakpoints.down("sm")]: { 93 | ...defaultFont, 94 | fontSize: "14px", 95 | marginRight: "8px" 96 | } 97 | }, 98 | manager: { 99 | [theme.breakpoints.down("sm")]: { 100 | width: "100%" 101 | }, 102 | display: "inline-block" 103 | }, 104 | searchWrapper: { 105 | [theme.breakpoints.down("sm")]: { 106 | width: "-webkit-fill-available", 107 | margin: "10px 15px 0" 108 | }, 109 | display: "inline-block" 110 | } 111 | }); 112 | 113 | export default headerLinksStyle; 114 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/rtlHeaderLinksStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | dangerColor, 4 | whiteColor 5 | } from "assets/jss/material-dashboard-react.js"; 6 | 7 | import dropdownStyle from "assets/jss/material-dashboard-react/dropdownStyle.js"; 8 | 9 | const headerLinksStyle = theme => ({ 10 | ...dropdownStyle(theme), 11 | search: { 12 | "& > div": { 13 | marginTop: "0" 14 | }, 15 | [theme.breakpoints.down("sm")]: { 16 | margin: "10px 15px !important", 17 | float: "none !important", 18 | paddingTop: "1px", 19 | paddingBottom: "1px", 20 | padding: "0!important", 21 | width: "60%", 22 | marginTop: "40px", 23 | "& input": { 24 | color: whiteColor 25 | } 26 | } 27 | }, 28 | linkText: { 29 | zIndex: "4", 30 | ...defaultFont, 31 | fontSize: "14px", 32 | margin: "0px" 33 | }, 34 | buttonLink: { 35 | [theme.breakpoints.down("sm")]: { 36 | display: "flex", 37 | margin: "10px 15px 0", 38 | width: "-webkit-fill-available", 39 | "& svg": { 40 | width: "24px", 41 | height: "30px", 42 | marginRight: "15px", 43 | marginLeft: "-15px" 44 | }, 45 | "& .fab,& .fas,& .far,& .fal,& .material-icons": { 46 | fontSize: "24px", 47 | lineHeight: "30px", 48 | width: "24px", 49 | height: "30px", 50 | marginRight: "15px", 51 | marginLeft: "-15px" 52 | }, 53 | "& > span": { 54 | justifyContent: "flex-start", 55 | width: "100%" 56 | } 57 | } 58 | }, 59 | searchButton: { 60 | [theme.breakpoints.down("sm")]: { 61 | top: "-50px !important", 62 | marginRight: "22px", 63 | float: "right" 64 | } 65 | }, 66 | margin: { 67 | zIndex: "4", 68 | margin: "0" 69 | }, 70 | searchIcon: { 71 | width: "17px", 72 | zIndex: "4" 73 | }, 74 | notifications: { 75 | zIndex: "4", 76 | [theme.breakpoints.up("md")]: { 77 | position: "absolute", 78 | top: "2px", 79 | border: "1px solid " + whiteColor, 80 | right: "4px", 81 | fontSize: "9px", 82 | background: dangerColor[0], 83 | color: whiteColor, 84 | minWidth: "16px", 85 | height: "16px", 86 | borderRadius: "10px", 87 | textAlign: "center", 88 | lineHeight: "16px", 89 | verticalAlign: "middle", 90 | display: "block" 91 | }, 92 | [theme.breakpoints.down("sm")]: { 93 | ...defaultFont, 94 | fontSize: "14px", 95 | marginRight: "8px" 96 | } 97 | }, 98 | manager: { 99 | [theme.breakpoints.down("sm")]: { 100 | width: "100%" 101 | }, 102 | display: "inline-block" 103 | }, 104 | searchWrapper: { 105 | [theme.breakpoints.down("sm")]: { 106 | width: "-webkit-fill-available", 107 | margin: "10px 15px 0" 108 | }, 109 | display: "inline-block" 110 | } 111 | }); 112 | 113 | export default headerLinksStyle; 114 | -------------------------------------------------------------------------------- /src/components/Language/Selector.js: -------------------------------------------------------------------------------- 1 | 2 | import React from "react"; 3 | import Typography from '@material-ui/core/Typography'; 4 | import Button from "components/CustomButtons/Button.js"; 5 | import Menu from '@material-ui/core/Menu'; 6 | import MenuItem from '@material-ui/core/MenuItem'; 7 | import ChatBubbleOutlineIcon from '@material-ui/icons/ChatBubbleOutline'; 8 | import { changeLanguage } from 'utils.js'; 9 | 10 | class Selector extends React.Component{ 11 | constructor(props) { 12 | super(props); 13 | this.openMenu = this.openMenu.bind(this); 14 | this.closeMenu = this.closeMenu.bind(this); 15 | this.languages = [ 16 | { 17 | locale: 'cn', 18 | name: '简体中文', 19 | }, 20 | { 21 | locale: 'en', 22 | name: 'English', 23 | }, 24 | ]; 25 | const { lang, setLang, ...rest } = props; 26 | this.restProps = rest; 27 | this.changeLanguage = setLang; 28 | let initialText; 29 | this.languages.forEach(current => { 30 | if(lang === current.locale){ 31 | initialText = current.name; 32 | } 33 | }) 34 | 35 | this.state = { 36 | language: lang, 37 | anchorEl: null, 38 | displayText: initialText, 39 | }; 40 | } 41 | 42 | updateLanguage(lang){ 43 | this.languages.forEach(current => { 44 | if(lang === current.locale){ 45 | this.setState({ 46 | displayText: current.name, 47 | anchorEl: null, 48 | }) 49 | } 50 | }) 51 | changeLanguage(lang); 52 | this.changeLanguage(lang); 53 | } 54 | 55 | openMenu(event){ 56 | this.setState({ 57 | anchorEl: event.currentTarget, 58 | }); 59 | } 60 | 61 | closeMenu() { 62 | this.setState({ 63 | anchorEl: null, 64 | }); 65 | } 66 | 67 | render(){ 68 | const currentLang = this.state.language; 69 | const { buttonClass, ...others } = this.restProps; 70 | return ( 71 | 72 | 76 | 82 | { 83 | this.languages.map((lang) => ( 84 | { 88 | this.updateLanguage(lang.locale); 89 | }} 90 | > 91 | 92 | {lang.name} 93 | 94 | 95 | )) 96 | } 97 | 98 | 99 | ); 100 | } 101 | } 102 | 103 | export default Selector; 104 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/snackbarContentStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | primaryBoxShadow, 4 | infoBoxShadow, 5 | successBoxShadow, 6 | warningBoxShadow, 7 | dangerBoxShadow, 8 | roseBoxShadow, 9 | whiteColor, 10 | blackColor, 11 | grayColor, 12 | infoColor, 13 | successColor, 14 | dangerColor, 15 | roseColor, 16 | primaryColor, 17 | warningColor, 18 | hexToRgb 19 | } from "assets/jss/material-dashboard-react.js"; 20 | 21 | const snackbarContentStyle = { 22 | root: { 23 | ...defaultFont, 24 | flexWrap: "unset", 25 | position: "relative", 26 | padding: "20px 15px", 27 | lineHeight: "20px", 28 | marginBottom: "20px", 29 | fontSize: "14px", 30 | backgroundColor: whiteColor, 31 | color: grayColor[7], 32 | borderRadius: "3px", 33 | minWidth: "unset", 34 | maxWidth: "unset", 35 | boxShadow: 36 | "0 12px 20px -10px rgba(" + 37 | hexToRgb(whiteColor) + 38 | ", 0.28), 0 4px 20px 0px rgba(" + 39 | hexToRgb(blackColor) + 40 | ", 0.12), 0 7px 8px -5px rgba(" + 41 | hexToRgb(whiteColor) + 42 | ", 0.2)" 43 | }, 44 | top20: { 45 | top: "20px" 46 | }, 47 | top40: { 48 | top: "40px" 49 | }, 50 | info: { 51 | backgroundColor: infoColor[3], 52 | color: whiteColor, 53 | ...infoBoxShadow 54 | }, 55 | success: { 56 | backgroundColor: successColor[3], 57 | color: whiteColor, 58 | ...successBoxShadow 59 | }, 60 | warning: { 61 | backgroundColor: warningColor[3], 62 | color: whiteColor, 63 | ...warningBoxShadow 64 | }, 65 | danger: { 66 | backgroundColor: dangerColor[3], 67 | color: whiteColor, 68 | ...dangerBoxShadow 69 | }, 70 | primary: { 71 | backgroundColor: primaryColor[3], 72 | color: whiteColor, 73 | ...primaryBoxShadow 74 | }, 75 | rose: { 76 | backgroundColor: roseColor[3], 77 | color: whiteColor, 78 | ...roseBoxShadow 79 | }, 80 | message: { 81 | padding: "0", 82 | display: "block", 83 | maxWidth: "89%" 84 | }, 85 | close: { 86 | width: "11px", 87 | height: "11px" 88 | }, 89 | iconButton: { 90 | width: "24px", 91 | height: "24px", 92 | padding: "0px" 93 | }, 94 | icon: { 95 | display: "block", 96 | left: "15px", 97 | position: "absolute", 98 | top: "50%", 99 | marginTop: "-15px", 100 | width: "30px", 101 | height: "30px" 102 | }, 103 | infoIcon: { 104 | color: infoColor[3] 105 | }, 106 | successIcon: { 107 | color: successColor[3] 108 | }, 109 | warningIcon: { 110 | color: warningColor[3] 111 | }, 112 | dangerIcon: { 113 | color: dangerColor[3] 114 | }, 115 | primaryIcon: { 116 | color: primaryColor[3] 117 | }, 118 | roseIcon: { 119 | color: roseColor[3] 120 | }, 121 | iconMessage: { 122 | paddingLeft: "50px", 123 | display: "block" 124 | }, 125 | actionRTL: { 126 | marginLeft: "-8px", 127 | marginRight: "auto" 128 | } 129 | }; 130 | 131 | export default snackbarContentStyle; 132 | -------------------------------------------------------------------------------- /src/components/CustomTabs/CustomTabs.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | 7 | // material-ui components 8 | import { makeStyles } from "@material-ui/core/styles"; 9 | import Tabs from "@material-ui/core/Tabs"; 10 | import Tab from "@material-ui/core/Tab"; 11 | // core components 12 | import Card from "components/Card/Card.js"; 13 | import CardBody from "components/Card/CardBody.js"; 14 | import CardHeader from "components/Card/CardHeader.js"; 15 | 16 | import styles from "assets/jss/material-dashboard-react/components/customTabsStyle.js"; 17 | 18 | const useStyles = makeStyles(styles); 19 | 20 | export default function CustomTabs(props) { 21 | const [value, setValue] = React.useState(0); 22 | const handleChange = (event, value) => { 23 | setValue(value); 24 | }; 25 | const classes = useStyles(); 26 | const { headerColor, plainTabs, tabs, title, rtlActive } = props; 27 | const cardTitle = classNames({ 28 | [classes.cardTitle]: true, 29 | [classes.cardTitleRTL]: rtlActive 30 | }); 31 | return ( 32 | 33 | 34 | {title !== undefined ?
{title}
: null} 35 | 46 | {tabs.map((prop, key) => { 47 | var icon = {}; 48 | if (prop.tabIcon) { 49 | icon = { 50 | icon: 51 | }; 52 | } 53 | return ( 54 | 64 | ); 65 | })} 66 | 67 |
68 | 69 | {tabs.map((prop, key) => { 70 | if (key === value) { 71 | return
{prop.tabContent}
; 72 | } 73 | return null; 74 | })} 75 |
76 |
77 | ); 78 | } 79 | 80 | CustomTabs.propTypes = { 81 | headerColor: PropTypes.oneOf([ 82 | "warning", 83 | "success", 84 | "danger", 85 | "info", 86 | "primary", 87 | "rose" 88 | ]), 89 | title: PropTypes.string, 90 | tabs: PropTypes.arrayOf( 91 | PropTypes.shape({ 92 | tabName: PropTypes.string.isRequired, 93 | tabIcon: PropTypes.object, 94 | tabContent: PropTypes.node.isRequired 95 | }) 96 | ), 97 | rtlActive: PropTypes.bool, 98 | plainTabs: PropTypes.bool 99 | }; 100 | -------------------------------------------------------------------------------- /src/views/Instances/CreateSnapshotDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import InputList from "components/CustomInput/InputList"; 3 | import CustomDialog from "components/Dialog/CustomDialog.js"; 4 | import { createInstanceSnapshot } from 'nano_api.js'; 5 | 6 | const i18n = { 7 | 'en':{ 8 | title: 'Create Snapshot', 9 | name: "Name", 10 | description: 'Description', 11 | cancel: 'Cancel', 12 | confirm: 'Confirm', 13 | }, 14 | 'cn':{ 15 | title: '创建云主机快照', 16 | name: "名称", 17 | description: '描述', 18 | cancel: '取消', 19 | confirm: '确定', 20 | }, 21 | } 22 | 23 | export default function CreateSnapshotDialog(props){ 24 | const defaultValues = { 25 | name: '', 26 | description: '', 27 | }; 28 | const { lang, open, instanceID, onSuccess, onCancel } = props; 29 | const [ operatable, setOperatable ] = React.useState(true); 30 | const [ prompt, setPrompt ] = React.useState(''); 31 | const [ request, setRequest ] = React.useState(defaultValues); 32 | const texts = i18n[lang]; 33 | const title = texts.title; 34 | 35 | const onCreateFail = (msg) =>{ 36 | setOperatable(true); 37 | setPrompt(msg); 38 | } 39 | const resetDialog = () =>{ 40 | setPrompt(''); 41 | setRequest(defaultValues); 42 | }; 43 | 44 | const closeDialog = ()=>{ 45 | resetDialog(); 46 | onCancel(); 47 | } 48 | 49 | const onCreateSuccess = snapshotName =>{ 50 | setOperatable(true); 51 | resetDialog(); 52 | onSuccess(snapshotName); 53 | } 54 | 55 | const handleConfirm = () =>{ 56 | setPrompt(''); 57 | setOperatable(false); 58 | if(!request.name){ 59 | onCreateFail('must specify snapshot name'); 60 | return; 61 | } 62 | if(!request.description){ 63 | onCreateFail('must specify description'); 64 | return; 65 | } 66 | 67 | createInstanceSnapshot(instanceID, request.name, request.description, onCreateSuccess, onCreateFail); 68 | } 69 | 70 | const handleRequestPropsChanged = name => e =>{ 71 | var value = e.target.value 72 | setRequest(previous => ({ 73 | ...previous, 74 | [name]: value, 75 | })); 76 | }; 77 | 78 | const inputs = [ 79 | { 80 | type: "text", 81 | label: texts.name, 82 | onChange: handleRequestPropsChanged('name'), 83 | value: request.name, 84 | required: true, 85 | oneRow: true, 86 | xs: 12, 87 | sm: 6, 88 | md: 4, 89 | }, 90 | { 91 | type: "text", 92 | label: texts.description, 93 | onChange: handleRequestPropsChanged('description'), 94 | value: request.description, 95 | required: true, 96 | oneRow: true, 97 | xs: 12, 98 | sm: 10, 99 | md: 8, 100 | }, 101 | ] 102 | 103 | const content = 104 | 105 | const buttons = [ 106 | { 107 | color: 'transparent', 108 | label: texts.cancel, 109 | onClick: closeDialog, 110 | }, 111 | { 112 | color: 'info', 113 | label: texts.confirm, 114 | onClick: handleConfirm, 115 | }, 116 | ]; 117 | 118 | return ; 120 | }; 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nano管理门户页面文件 / Nano Web Portal Pages 2 | 3 | [版本历史/ChangeLog](CHANGELOG.md) 4 | 5 | [English Version](#introduce) 6 | 7 | ## 说明 8 | 9 | 本项目是[Nano FrontEnd](https://github.com/project-nano/frontend)提供的管理门户页面源代码,编译完成后的页面文件部署在FrontEnd模块的**web_root**目录下提供服务。 10 | 11 | 编译要求 12 | - Node.js 16 13 | - yarn 1.22或以上 14 | 15 | 执行以下指令准备项目用于调试或者编译 16 | 17 | ``` 18 | $git clone https://github.com/project-nano/portal.git 19 | $cd portal 20 | $yarn install 21 | ``` 22 | 23 | ### 开发调试 24 | 25 | 配合FrontEnd的CORS配置开关,Portal页面能够在本地启动并连接远程FrontEnd服务进行调试。配置方法见[FrontEnd页面](https://github.com/project-nano/frontend) 26 | 27 | FrontEnd服务启动后,修改package.json的service.host和service.port两个参数,指向FrontEnd服务端口,执行以下指令 28 | 29 | ``` 30 | $yarn start 31 | ``` 32 | 33 | 成功后可以使用浏览器访问localhost:3000调试页面 34 | 35 | ### 编译部署 36 | 37 | 执行以下指令进行编译 38 | ``` 39 | $yarn build 40 | ``` 41 | 42 | 编译结果会输出到build子目录,将所有内容复制到FrontEnd模块的**web_root**即可(需要重启模块) 43 | 44 | #### 配置说明 45 | 46 | Portal使用'package.json'进行配置,修改后需要重新启动或者编译生效 47 | 48 | | 配置项 | 值类型 | 默认值 | 说明 | 49 | | ------------ | ------ | ------ | -------------------------------------------- | 50 | | service.host | 字符串 | | FrontEnd服务主机地址,默认为空,使用本地连接 | 51 | | service.port | 整型 | 5870 | FrontEnd服务端口,仅当host不为空时生效 | 52 | | version | 字符串 | 1.4.0 | 页面显示的版本号 | 53 | 54 | ## Introduce 55 | 56 | This project is the source code of the Web portal provided by [Nano FrontEnd](https://github.com/project-nano/frontend). The compiled page files are deployed in the **web_root** directory of the FrontEnd module. 57 | 58 | Compile requirements 59 | - Node.js 16 60 | - yarn 1.22 or above 61 | 62 | execute the following instructions to prepare the project 63 | 64 | ``` 65 | $ git clone https://github.com/project-nano/portal.git 66 | $ cd portal 67 | $ yarn install 68 | ``` 69 | 70 | ### Development 71 | 72 | Work with the CORS option of the FrontEnd configure, a local portal could connect to the remote FrontEnd service for debugging. See details at [FrontEnd page](https://github.com/project-nano/frontend) 73 | 74 | When the FrontEnd started, change the parameters of 'service.host' and 'service.port' in "package.json" to the FrontEnd service. 75 | 76 | execute the following instruction 77 | 78 | ``` 79 | $ yarn start 80 | ``` 81 | 82 | When page started, you can use a browser to access the localhost:3000. 83 | 84 | ### Deployment 85 | 86 | Execute the following instruction for building: 87 | ``` 88 | $ yarn build 89 | ``` 90 | 91 | The compiled files are output to the directory: '.\build'. 92 | Copy all content to the **web_root** of the FrontEnd module (restart required). 93 | 94 | #### Configuration Instructions 95 | 96 | Portal uses 'package.json' for configuration. 97 | Restart or compile is required for modification to take effect. 98 | 99 | | Option | Value type | Default value | Description | 100 | | ------------------- | ---------- | ------------- | ------------------------------------------------------ | 101 | | service.host | String | | FrontEnd service host address, default is empty, using local connection | 102 | | service.port | Integer | 5870 | FrontEnd service port, only effective when host is not empty | 103 | | version | String | 1.4.0 | Version number displayed on the page | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | ========================================================= 4 | * Material Dashboard React - v1.8.0 5 | ========================================================= 6 | 7 | * Product Page: https://www.creative-tim.com/product/material-dashboard-react 8 | * Copyright 2019 Creative Tim (https://www.creative-tim.com) 9 | * Licensed under MIT (https://github.com/creativetimofficial/material-dashboard-react/blob/master/LICENSE.md) 10 | 11 | * Coded by Creative Tim 12 | 13 | ========================================================= 14 | 15 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 16 | 17 | */ 18 | import React from "react"; 19 | import ReactDOM from "react-dom"; 20 | import {createTheme} from '@material-ui/core/styles'; 21 | import ThemeProvider from '@material-ui/styles/ThemeProvider'; 22 | 23 | import { createBrowserHistory } from "history"; 24 | import { Router, Route, Switch, Redirect } from "react-router-dom"; 25 | import Danger from "components/Typography/Danger.js"; 26 | import { primaryColor, infoColor, dangerColor } from "assets/jss/material-dashboard-react.js"; 27 | // core components 28 | import Admin from "layouts/Admin.js"; 29 | import Login from "views/Login/Login.js"; 30 | import Initial from "views/Login/Initial.js"; 31 | import ControlInstance from "views/Instances/ControlInstance.js"; 32 | import { getLanguage } from "utils.js"; 33 | 34 | import "assets/css/material-dashboard-react.css?v=1.8.0"; 35 | 36 | class ErrorBoundary extends React.Component { 37 | constructor(props) { 38 | super(props); 39 | this.state = { 40 | hasError: false, 41 | error: null, 42 | errorStack: null, 43 | }; 44 | } 45 | 46 | static getDerivedStateFromError(error) { 47 | // 更新 state 使下一次渲染能够显示降级后的 UI 48 | return { 49 | hasError: true, 50 | error: error.message, 51 | errorStack: error.stack, 52 | }; 53 | } 54 | 55 | render() { 56 | if (this.state.hasError){ 57 | return {this.state.error}; 58 | } 59 | 60 | return this.props.children; 61 | } 62 | } 63 | 64 | const history = createBrowserHistory(); 65 | const mainTheme = createTheme({ 66 | palette: { 67 | primary: { 68 | light: primaryColor[1], 69 | main: primaryColor[0], 70 | }, 71 | secondary:{ 72 | light: infoColor[1], 73 | main: infoColor[0], 74 | }, 75 | error:{ 76 | light: dangerColor[1], 77 | main: dangerColor[0], 78 | }, 79 | }, 80 | }); 81 | 82 | function LanguageProvider(props){ 83 | const [ lang, setLang ] = React.useState(getLanguage()); 84 | 85 | return ( 86 | 87 | 88 | 89 | 90 | }/> 91 | }/> 92 | }/> 93 | }/> 94 | 95 | 96 | 97 | 98 | 99 | ) 100 | } 101 | 102 | ReactDOM.render( 103 | , 104 | document.getElementById("root") 105 | ); 106 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/dropdownStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | primaryColor, 3 | whiteColor, 4 | primaryBoxShadow, 5 | defaultFont, 6 | blackColor, 7 | grayColor, 8 | hexToRgb 9 | } from "assets/jss/material-dashboard-react.js"; 10 | 11 | const dropdownStyle = theme => ({ 12 | buttonLink: { 13 | [theme.breakpoints.down("md")]: { 14 | display: "flex", 15 | marginLeft: "30px", 16 | width: "auto" 17 | } 18 | }, 19 | links: { 20 | width: "20px", 21 | height: "20px", 22 | zIndex: "4", 23 | [theme.breakpoints.down("md")]: { 24 | display: "block", 25 | width: "30px", 26 | height: "30px", 27 | color: grayColor[9], 28 | marginRight: "15px" 29 | } 30 | }, 31 | linkText: { 32 | zIndex: "4", 33 | ...defaultFont, 34 | fontSize: "14px" 35 | }, 36 | popperClose: { 37 | pointerEvents: "none" 38 | }, 39 | pooperResponsive: { 40 | [theme.breakpoints.down("md")]: { 41 | zIndex: "1640", 42 | position: "static", 43 | float: "none", 44 | width: "auto", 45 | marginTop: "0", 46 | backgroundColor: "transparent", 47 | border: "0", 48 | WebkitBoxShadow: "none", 49 | boxShadow: "none", 50 | color: "black" 51 | } 52 | }, 53 | popperNav: { 54 | [theme.breakpoints.down("sm")]: { 55 | position: "static !important", 56 | left: "unset !important", 57 | top: "unset !important", 58 | transform: "none !important", 59 | willChange: "unset !important", 60 | "& > div": { 61 | boxShadow: "none !important", 62 | marginLeft: "0rem", 63 | marginRight: "0rem", 64 | transition: "none !important", 65 | marginTop: "0px !important", 66 | marginBottom: "0px !important", 67 | padding: "0px !important", 68 | backgroundColor: "transparent !important", 69 | "& ul li": { 70 | color: whiteColor + " !important", 71 | margin: "10px 15px 0!important", 72 | padding: "10px 15px !important", 73 | "&:hover": { 74 | backgroundColor: "hsla(0,0%,78%,.2)", 75 | boxShadow: "none" 76 | } 77 | } 78 | } 79 | } 80 | }, 81 | dropdown: { 82 | borderRadius: "3px", 83 | border: "0", 84 | boxShadow: "0 2px 5px 0 rgba(" + hexToRgb(blackColor) + ", 0.26)", 85 | top: "100%", 86 | zIndex: "1000", 87 | minWidth: "160px", 88 | padding: "5px 0", 89 | margin: "2px 0 0", 90 | fontSize: "14px", 91 | textAlign: "left", 92 | listStyle: "none", 93 | backgroundColor: whiteColor, 94 | WebkitBackgroundClip: "padding-box", 95 | backgroundClip: "padding-box" 96 | }, 97 | dropdownItem: { 98 | ...defaultFont, 99 | fontSize: "13px", 100 | padding: "10px 20px", 101 | margin: "0 5px", 102 | borderRadius: "2px", 103 | WebkitTransition: "all 150ms linear", 104 | MozTransition: "all 150ms linear", 105 | OTransition: "all 150ms linear", 106 | MsTransition: "all 150ms linear", 107 | transition: "all 150ms linear", 108 | display: "block", 109 | clear: "both", 110 | fontWeight: "400", 111 | lineHeight: "1.42857143", 112 | color: grayColor[8], 113 | whiteSpace: "nowrap", 114 | height: "unset", 115 | minHeight: "unset", 116 | "&:hover": { 117 | backgroundColor: primaryColor[0], 118 | color: whiteColor, 119 | ...primaryBoxShadow 120 | } 121 | } 122 | }); 123 | 124 | export default dropdownStyle; 125 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/components/cardHeaderStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | warningCardHeader, 3 | successCardHeader, 4 | dangerCardHeader, 5 | infoCardHeader, 6 | primaryCardHeader, 7 | roseCardHeader, 8 | whiteColor 9 | } from "assets/jss/material-dashboard-react.js"; 10 | 11 | const cardHeaderStyle = { 12 | cardHeader: { 13 | padding: "0.75rem 1.25rem", 14 | marginBottom: "0", 15 | borderBottom: "none", 16 | background: "transparent", 17 | zIndex: "3 !important", 18 | "&$cardHeaderPlain,&$cardHeaderIcon,&$cardHeaderStats,&$warningCardHeader,&$successCardHeader,&$dangerCardHeader,&$infoCardHeader,&$primaryCardHeader,&$roseCardHeader": { 19 | margin: "0 15px", 20 | padding: "0", 21 | position: "relative", 22 | color: whiteColor 23 | }, 24 | "&:first-child": { 25 | borderRadius: "calc(.25rem - 1px) calc(.25rem - 1px) 0 0" 26 | }, 27 | "&$warningCardHeader,&$successCardHeader,&$dangerCardHeader,&$infoCardHeader,&$primaryCardHeader,&$roseCardHeader": { 28 | "&:not($cardHeaderIcon)": { 29 | borderRadius: "3px", 30 | marginTop: "-20px", 31 | padding: "15px" 32 | } 33 | }, 34 | "&$cardHeaderStats svg": { 35 | fontSize: "36px", 36 | lineHeight: "56px", 37 | textAlign: "center", 38 | width: "36px", 39 | height: "36px", 40 | margin: "10px 10px 4px" 41 | }, 42 | "&$cardHeaderStats i,&$cardHeaderStats .material-icons": { 43 | fontSize: "36px", 44 | lineHeight: "56px", 45 | width: "56px", 46 | height: "56px", 47 | textAlign: "center", 48 | overflow: "unset", 49 | marginBottom: "1px" 50 | }, 51 | "&$cardHeaderStats$cardHeaderIcon": { 52 | textAlign: "right" 53 | } 54 | }, 55 | cardHeaderPlain: { 56 | marginLeft: "0px !important", 57 | marginRight: "0px !important" 58 | }, 59 | cardHeaderStats: { 60 | "& $cardHeaderIcon": { 61 | textAlign: "right" 62 | }, 63 | "& h1,& h2,& h3,& h4,& h5,& h6": { 64 | margin: "0 !important" 65 | } 66 | }, 67 | cardHeaderIcon: { 68 | "&$warningCardHeader,&$successCardHeader,&$dangerCardHeader,&$infoCardHeader,&$primaryCardHeader,&$roseCardHeader": { 69 | background: "transparent", 70 | boxShadow: "none" 71 | }, 72 | "& i,& .material-icons": { 73 | width: "33px", 74 | height: "33px", 75 | textAlign: "center", 76 | lineHeight: "33px" 77 | }, 78 | "& svg": { 79 | width: "24px", 80 | height: "24px", 81 | textAlign: "center", 82 | lineHeight: "33px", 83 | margin: "5px 4px 0px" 84 | } 85 | }, 86 | warningCardHeader: { 87 | color: whiteColor, 88 | "&:not($cardHeaderIcon)": { 89 | ...warningCardHeader 90 | } 91 | }, 92 | successCardHeader: { 93 | color: whiteColor, 94 | "&:not($cardHeaderIcon)": { 95 | ...successCardHeader 96 | } 97 | }, 98 | dangerCardHeader: { 99 | color: whiteColor, 100 | "&:not($cardHeaderIcon)": { 101 | ...dangerCardHeader 102 | } 103 | }, 104 | infoCardHeader: { 105 | color: whiteColor, 106 | "&:not($cardHeaderIcon)": { 107 | ...infoCardHeader 108 | } 109 | }, 110 | primaryCardHeader: { 111 | color: whiteColor, 112 | "&:not($cardHeaderIcon)": { 113 | ...primaryCardHeader 114 | } 115 | }, 116 | roseCardHeader: { 117 | color: whiteColor, 118 | "&:not($cardHeaderIcon)": { 119 | ...roseCardHeader 120 | } 121 | } 122 | }; 123 | 124 | export default cardHeaderStyle; 125 | -------------------------------------------------------------------------------- /src/assets/img/nano_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/views/Instances/ModifyNameDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import InputList from "components/CustomInput/InputList"; 3 | import CustomDialog from "components/Dialog/CustomDialog.js"; 4 | import { modifyInstanceName } from 'nano_api.js'; 5 | 6 | const i18n = { 7 | 'en':{ 8 | title: 'Modify Instance Name', 9 | current: 'Current Name', 10 | new: 'New Name', 11 | cancel: 'Cancel', 12 | confirm: 'Confirm', 13 | }, 14 | 'cn':{ 15 | title: '修改云主机名称', 16 | current: '当前云主机名', 17 | new: '新主机名', 18 | cancel: '取消', 19 | confirm: '确定', 20 | }, 21 | } 22 | 23 | export default function ModifyNameDialog(props){ 24 | const defaultValues = { 25 | name: '', 26 | }; 27 | const { lang, open, instanceID, current, onSuccess, onCancel } = props; 28 | const currentName = current ? current.name.slice(current.group.length + 1) : ''; 29 | const [ operatable, setOperatable ] = React.useState(true); 30 | const [ prompt, setPrompt ] = React.useState(''); 31 | const [ mounted, setMounted ] = React.useState(false); 32 | const [ request, setRequest ] = React.useState(defaultValues); 33 | 34 | const texts = i18n[lang]; 35 | const title = texts.title; 36 | const onModifyFail = React.useCallback(msg =>{ 37 | if(!mounted){ 38 | return; 39 | } 40 | setOperatable(true); 41 | setPrompt(msg); 42 | }, [mounted]); 43 | 44 | const resetDialog = () =>{ 45 | setPrompt(''); 46 | setRequest(defaultValues); 47 | }; 48 | 49 | const closeDialog = ()=>{ 50 | resetDialog(); 51 | onCancel(); 52 | } 53 | 54 | const onModifySuccess = instanceName =>{ 55 | if(!mounted){ 56 | return; 57 | } 58 | setOperatable(true); 59 | resetDialog(); 60 | onSuccess(instanceName, instanceID); 61 | } 62 | 63 | const handleConfirm = () =>{ 64 | setPrompt(''); 65 | setOperatable(false); 66 | if(!request.name){ 67 | onModifyFail('must specify new instance name'); 68 | return; 69 | } 70 | const newName = [current.group, request.name].join('.'); 71 | 72 | if(currentName === newName){ 73 | onModifyFail('no need to modify'); 74 | return; 75 | } 76 | 77 | modifyInstanceName(instanceID, newName, onModifySuccess, onModifyFail); 78 | } 79 | 80 | const handleRequestPropsChanged = name => e =>{ 81 | if(!mounted){ 82 | return; 83 | } 84 | var value = e.target.value 85 | setRequest(previous => ({ 86 | ...previous, 87 | [name]: value, 88 | })); 89 | }; 90 | 91 | React.useEffect(()=>{ 92 | if (!open){ 93 | return; 94 | } 95 | setMounted(true); 96 | return ()=> setMounted(false); 97 | }, [open]); 98 | 99 | const inputs = [ 100 | { 101 | type: "text", 102 | label: texts.current, 103 | value: currentName, 104 | disabled: true, 105 | oneRow: true, 106 | xs: 12, 107 | sm: 6, 108 | md: 4, 109 | }, 110 | { 111 | type: "text", 112 | label: texts.new, 113 | onChange: handleRequestPropsChanged('name'), 114 | value: request.name, 115 | required: true, 116 | oneRow: true, 117 | xs: 12, 118 | sm: 10, 119 | md: 8, 120 | }, 121 | ]; 122 | 123 | const buttons = [ 124 | { 125 | color: 'transparent', 126 | label: texts.cancel, 127 | onClick: closeDialog, 128 | }, 129 | { 130 | color: 'info', 131 | label: texts.confirm, 132 | onClick: handleConfirm, 133 | }, 134 | ]; 135 | 136 | const content = 137 | return ; 139 | }; 140 | -------------------------------------------------------------------------------- /src/views/Instances/ModifyCoresDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import InputList from "components/CustomInput/InputList"; 3 | import CustomDialog from "components/Dialog/CustomDialog.js"; 4 | import { modifyInstanceCores } from 'nano_api.js'; 5 | 6 | const i18n = { 7 | 'en':{ 8 | title: 'Modify Instance Cores', 9 | current: 'Current Cores', 10 | new: 'New Cores', 11 | cancel: 'Cancel', 12 | confirm: 'Confirm', 13 | }, 14 | 'cn':{ 15 | title: '修改核心数', 16 | current: '当前核心数', 17 | new: '新核心数', 18 | cancel: '取消', 19 | confirm: '确定', 20 | }, 21 | } 22 | 23 | export default function ModifyCoresDialog(props){ 24 | const defaultValues = { 25 | cores: '', 26 | }; 27 | const { lang, open, instanceID, current, onSuccess, onCancel } = props; 28 | const currentCores = current ? current.cores : 0; 29 | const [ operatable, setOperatable ] = React.useState(true); 30 | const [ prompt, setPrompt ] = React.useState(''); 31 | const [ mounted, setMounted ] = React.useState(false); 32 | const [ request, setRequest ] = React.useState(defaultValues); 33 | 34 | const texts = i18n[lang]; 35 | const title = texts.title; 36 | 37 | const onModifyFail = React.useCallback(msg =>{ 38 | if(!mounted){ 39 | return; 40 | } 41 | setOperatable(true); 42 | setPrompt(msg); 43 | }, [mounted]); 44 | 45 | const resetDialog = () =>{ 46 | setPrompt(''); 47 | setRequest(defaultValues); 48 | }; 49 | 50 | const closeDialog = ()=>{ 51 | resetDialog(); 52 | onCancel(); 53 | } 54 | 55 | const onModifySuccess = cores =>{ 56 | if(!mounted){ 57 | return; 58 | } 59 | setOperatable(true); 60 | resetDialog(); 61 | onSuccess(cores, instanceID); 62 | } 63 | 64 | const handleConfirm = () =>{ 65 | if(!request.cores){ 66 | onModifyFail('must specify new instance cores'); 67 | return; 68 | } 69 | const newCores = Number.parseInt(request.cores); 70 | if(Number.isNaN(newCores)){ 71 | onModifyFail('invalid cores number: ' + request.cores); 72 | return; 73 | } 74 | 75 | if(currentCores === newCores){ 76 | onModifyFail('no need to modify'); 77 | return; 78 | } 79 | 80 | setPrompt(''); 81 | setOperatable(false); 82 | modifyInstanceCores(instanceID, newCores, onModifySuccess, onModifyFail); 83 | } 84 | 85 | const handleRequestPropsChanged = name => e =>{ 86 | if(!mounted){ 87 | return; 88 | } 89 | var value = e.target.value 90 | setRequest(previous => ({ 91 | ...previous, 92 | [name]: value, 93 | })); 94 | }; 95 | 96 | React.useEffect(()=>{ 97 | if (!open){ 98 | return; 99 | } 100 | setMounted(true); 101 | return ()=> setMounted(false); 102 | }, [open]); 103 | 104 | const inputs = [ 105 | { 106 | type: "text", 107 | label: texts.current, 108 | value: currentCores.toString(), 109 | disabled: true, 110 | oneRow: true, 111 | xs: 12, 112 | sm: 6, 113 | md: 4, 114 | }, 115 | { 116 | type: "text", 117 | label: texts.new, 118 | onChange: handleRequestPropsChanged('cores'), 119 | value: request.cores, 120 | required: true, 121 | oneRow: true, 122 | xs: 12, 123 | sm: 10, 124 | md: 8, 125 | }, 126 | ]; 127 | 128 | const buttons = [ 129 | { 130 | color: 'transparent', 131 | label: texts.cancel, 132 | onClick: closeDialog, 133 | }, 134 | { 135 | color: 'info', 136 | label: texts.confirm, 137 | onClick: handleConfirm, 138 | }, 139 | ]; 140 | 141 | const content = 142 | return ; 144 | 145 | }; 146 | -------------------------------------------------------------------------------- /src/components/LoggedUser/ModifyPasswordDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import InputList from "components/CustomInput/InputList"; 3 | import CustomDialog from "components/Dialog/CustomDialog.js"; 4 | import { changeUserPassword, writeLog } from 'nano_api.js'; 5 | 6 | const i18n = { 7 | 'en':{ 8 | title: 'Modify Password', 9 | current: 'Current Password', 10 | new: 'New Password', 11 | confirmNew: 'Confirm New Password', 12 | cancel: 'Cancel', 13 | confirm: 'Confirm', 14 | }, 15 | 'cn':{ 16 | title: '修改密码', 17 | current: '当前密码', 18 | new: '新密码', 19 | confirmNew: '确认新密码', 20 | cancel: '取消', 21 | confirm: '确认', 22 | } 23 | }; 24 | 25 | export default function ModifyPasswordDialog(props){ 26 | const defaultValues = { 27 | old: '', 28 | new: '', 29 | new2: '', 30 | } 31 | const { lang, user, open, onSuccess, onCancel } = props; 32 | const [ operatable, setOperatable ] = React.useState(true); 33 | const [ mounted, setMounted ] = React.useState(false); 34 | const [ prompt, setPrompt ] = React.useState(''); 35 | const [ request, setRequest ] = React.useState(defaultValues); 36 | 37 | const texts = i18n[lang]; 38 | const title = texts.title; 39 | 40 | const resetDialog = () =>{ 41 | setPrompt(''); 42 | setRequest(defaultValues); 43 | }; 44 | 45 | const closeDialog = ()=>{ 46 | resetDialog(); 47 | onCancel(); 48 | } 49 | 50 | const onModifyFail = msg =>{ 51 | if(!mounted){ 52 | return; 53 | } 54 | setOperatable(true); 55 | setPrompt(msg); 56 | } 57 | 58 | const onModifySuccess = () =>{ 59 | writeLog('change password of ' + user); 60 | if(!mounted){ 61 | return; 62 | } 63 | resetDialog(); 64 | setOperatable(true); 65 | onSuccess(); 66 | } 67 | 68 | const handleConfirm = () =>{ 69 | setOperatable(false); 70 | if ('' === request.old){ 71 | onModifyFail('previous password required'); 72 | return; 73 | } 74 | if ('' === request.new){ 75 | onModifyFail('new password required'); 76 | return; 77 | } 78 | if (request.new2 !== request.new){ 79 | onModifyFail('confirm password mismatched'); 80 | return; 81 | } 82 | changeUserPassword(user, request.old, request.new, onModifySuccess, onModifyFail); 83 | } 84 | 85 | const handleRequestPropsChanged = name => e =>{ 86 | var value = e.target.value 87 | setRequest(previous => ({ 88 | ...previous, 89 | [name]: value, 90 | })); 91 | }; 92 | 93 | React.useEffect(()=>{ 94 | setMounted(true); 95 | return () => { 96 | setMounted(false); 97 | } 98 | }, []); 99 | 100 | const inputs = [ 101 | { 102 | type: "password", 103 | label: texts.current, 104 | onChange: handleRequestPropsChanged('old'), 105 | value: request.old, 106 | required: true, 107 | xs: 12, 108 | }, 109 | { 110 | type: "password", 111 | label: texts.new, 112 | onChange: handleRequestPropsChanged('new'), 113 | value: request.new, 114 | required: true, 115 | xs: 12, 116 | }, 117 | { 118 | type: "password", 119 | label: texts.confirmNew, 120 | onChange: handleRequestPropsChanged('new2'), 121 | value: request.new2, 122 | required: true, 123 | xs: 12, 124 | }, 125 | ]; 126 | const content = 127 | 128 | const buttons = [ 129 | { 130 | color: 'transparent', 131 | label: texts.cancel, 132 | onClick: closeDialog, 133 | }, 134 | { 135 | color: 'info', 136 | label: texts.confirm, 137 | onClick: handleConfirm, 138 | }, 139 | ]; 140 | 141 | return ; 143 | } 144 | -------------------------------------------------------------------------------- /src/components/Navbars/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import classNames from "classnames"; 3 | import PropTypes from "prop-types"; 4 | import Box from '@material-ui/core/Box'; 5 | // @material-ui/core components 6 | import { makeStyles } from "@material-ui/core/styles"; 7 | import AppBar from "@material-ui/core/AppBar"; 8 | import Toolbar from "@material-ui/core/Toolbar"; 9 | import Link from '@material-ui/core/Link'; 10 | import Tooltip from "@material-ui/core/Tooltip"; 11 | import IconButton from "@material-ui/core/IconButton"; 12 | import Typography from '@material-ui/core/Typography'; 13 | import HelpOutlineIcon from '@material-ui/icons/HelpOutline'; 14 | import Hidden from "@material-ui/core/Hidden"; 15 | // @material-ui/icons 16 | import Menu from "@material-ui/icons/Menu"; 17 | // core components 18 | import AdminNavbarLinks from "./AdminNavbarLinks.js"; 19 | import Button from "components/CustomButtons/Button.js"; 20 | import LanguageSelector from "components/Language/Selector.js"; 21 | 22 | import styles from "assets/jss/material-dashboard-react/components/headerStyle.js"; 23 | import { getCurrentVersion } from 'nano_api.js'; 24 | 25 | const useStyles = makeStyles(styles); 26 | 27 | const i18n = { 28 | 'en':{ 29 | manual: 'Online Manual', 30 | manualURL: 'https://nanocloud.readthedocs.io/projects/guide/en/latest/', 31 | }, 32 | 'cn':{ 33 | manual: '在线文档', 34 | manualURL: 'https://nanocloud.readthedocs.io/projects/guide/zh_CN/latest/', 35 | } 36 | } 37 | 38 | export default function Header(props) { 39 | const classes = useStyles(); 40 | function makeBrand() { 41 | var name = ''; 42 | props.routes.every(prop => { 43 | if (window.location.href.indexOf(prop.layout + prop.path) !== -1) { 44 | name = prop.display[props.lang] 45 | return false; 46 | } 47 | return true; 48 | }); 49 | return name; 50 | } 51 | const { color, lang, setLang } = props; 52 | const texts = i18n[lang]; 53 | const appBarClasses = classNames({ 54 | [" " + classes[color]]: color 55 | }); 56 | 57 | let currentYear = new Date().getFullYear(); 58 | const version = ( 59 | 60 | 61 | {`Project Nano ${getCurrentVersion()} © 2018~${currentYear}`} 62 | 63 | 64 | ); 65 | 66 | const manualButton = ( 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | ) 75 | 76 | return ( 77 | 78 | 79 |
80 | {/* Here we create navbar brand, based on route name */} 81 | 84 |
85 | 86 | {version} 87 | {manualButton} 88 | 89 | 90 | 91 | 92 | 93 | 94 | 99 | 100 | 101 | 102 | 103 | 104 | ); 105 | } 106 | 107 | Header.propTypes = { 108 | color: PropTypes.oneOf(["primary", "info", "success", "warning", "danger"]), 109 | rtlActive: PropTypes.bool, 110 | handleDrawerToggle: PropTypes.func, 111 | routes: PropTypes.arrayOf(PropTypes.object) 112 | }; 113 | --------------------------------------------------------------------------------