├── .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 |
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 |
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 |
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 |
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 |
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 |
89 | )
90 | };
91 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
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 |
--------------------------------------------------------------------------------