mirror of
https://github.com/OpenNebula/one.git
synced 2025-02-16 09:57:23 +03:00
parent
5ac2aa9b76
commit
70564d9ac1
@ -1,2 +1 @@
|
||||
src/**/*.css
|
||||
*.md
|
||||
src/client/assets/languages/**
|
@ -1,14 +1,21 @@
|
||||
{
|
||||
"extends": [
|
||||
"./node_modules/jloboescalona-eslint-config/index.js",
|
||||
"plugin:cypress/recommended"
|
||||
],
|
||||
"parser": "babel-eslint",
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"plugins": [],
|
||||
"extends": [
|
||||
"plugin:react/recommended",
|
||||
"standard"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["react"],
|
||||
"rules": {
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
@ -21,6 +28,9 @@
|
||||
"default-case": 0
|
||||
},
|
||||
"settings": {
|
||||
"react" : {
|
||||
"version": "detect"
|
||||
},
|
||||
"import/resolver": {
|
||||
"alias": {
|
||||
"map": [
|
||||
|
@ -12,7 +12,8 @@
|
||||
"cypress:open": "cypress open",
|
||||
"cypress:run": "cypress run --headless --browser chrome --spec \"cypress/integration/**/*.spec.js\"",
|
||||
"pot": "node potfile.js",
|
||||
"po2json": "node po2json.js"
|
||||
"po2json": "node po2json.js",
|
||||
"lint": "eslint ./src/client/"
|
||||
},
|
||||
"author": "opennebula.io",
|
||||
"license": "ISC",
|
||||
@ -26,16 +27,20 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"cypress": "^5.3.0",
|
||||
"eslint": "^6.1.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-config-standard": "^14.1.1",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-import-resolver-webpack": "^0.13.0",
|
||||
"eslint-plugin-babel": "^5.3.1",
|
||||
"eslint-plugin-cypress": "^2.11.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-react": "^7.21.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.21.4",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"fireedge-genpotfile": "^1.0.0",
|
||||
"fireedge-pojson": "^1.0.2",
|
||||
"jloboescalona-eslint-config": "^1.1.0"
|
||||
"fireedge-pojson": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/cli": "^7.10.4",
|
||||
|
@ -1,11 +1,11 @@
|
||||
const CHANGE_ZONE = 'CHANGE_ZONE';
|
||||
const DISPLAY_LOADING = 'DISPLAY_LOADING';
|
||||
const TOGGLE_MENU = 'TOGGLE_MENU';
|
||||
const FIX_MENU = 'FIX_MENU';
|
||||
const CHANGE_ZONE = 'CHANGE_ZONE'
|
||||
const DISPLAY_LOADING = 'DISPLAY_LOADING'
|
||||
const TOGGLE_MENU = 'TOGGLE_MENU'
|
||||
const FIX_MENU = 'FIX_MENU'
|
||||
|
||||
const ENQUEUE_SNACKBAR = 'ENQUEUE_SNACKBAR';
|
||||
const CLOSE_SNACKBAR = 'CLOSE_SNACKBAR';
|
||||
const REMOVE_SNACKBAR = 'REMOVE_SNACKBAR';
|
||||
const ENQUEUE_SNACKBAR = 'ENQUEUE_SNACKBAR'
|
||||
const CLOSE_SNACKBAR = 'CLOSE_SNACKBAR'
|
||||
const REMOVE_SNACKBAR = 'REMOVE_SNACKBAR'
|
||||
|
||||
const Actions = {
|
||||
CHANGE_ZONE,
|
||||
@ -15,7 +15,7 @@ const Actions = {
|
||||
ENQUEUE_SNACKBAR,
|
||||
CLOSE_SNACKBAR,
|
||||
REMOVE_SNACKBAR
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Actions,
|
||||
@ -36,7 +36,7 @@ module.exports = {
|
||||
isFixed
|
||||
}),
|
||||
enqueueSnackbar: notification => {
|
||||
const key = notification.options && notification.options.key;
|
||||
const key = notification.options && notification.options.key
|
||||
|
||||
return {
|
||||
type: ENQUEUE_SNACKBAR,
|
||||
@ -44,7 +44,7 @@ module.exports = {
|
||||
...notification,
|
||||
key: key || new Date().getTime() + Math.random()
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
enqueueError: message => ({
|
||||
type: ENQUEUE_SNACKBAR,
|
||||
@ -71,4 +71,4 @@ module.exports = {
|
||||
type: REMOVE_SNACKBAR,
|
||||
key
|
||||
})
|
||||
};
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
const START_ONE_REQUEST = 'START_ONE_REQUEST';
|
||||
const SUCCESS_ONE_REQUEST = 'SUCCESS_ONE_REQUEST';
|
||||
const FAILURE_ONE_REQUEST = 'FAILURE_ONE_REQUEST';
|
||||
const START_ONE_REQUEST = 'START_ONE_REQUEST'
|
||||
const SUCCESS_ONE_REQUEST = 'SUCCESS_ONE_REQUEST'
|
||||
const FAILURE_ONE_REQUEST = 'FAILURE_ONE_REQUEST'
|
||||
|
||||
const Actions = {
|
||||
START_ONE_REQUEST,
|
||||
SUCCESS_ONE_REQUEST,
|
||||
FAILURE_ONE_REQUEST
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Actions,
|
||||
@ -101,4 +101,4 @@ module.exports = {
|
||||
type: FAILURE_ONE_REQUEST,
|
||||
payload: { error }
|
||||
})
|
||||
};
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
const START_AUTH = 'START_AUTH';
|
||||
const SELECT_FILTER_GROUP = 'SELECT_FILTER_GROUP';
|
||||
const SUCCESS_AUTH = 'SUCCESS_AUTH';
|
||||
const FAILURE_AUTH = 'FAILURE_AUTH';
|
||||
const LOGOUT = 'LOGOUT';
|
||||
const START_AUTH = 'START_AUTH'
|
||||
const SELECT_FILTER_GROUP = 'SELECT_FILTER_GROUP'
|
||||
const SUCCESS_AUTH = 'SUCCESS_AUTH'
|
||||
const FAILURE_AUTH = 'FAILURE_AUTH'
|
||||
const LOGOUT = 'LOGOUT'
|
||||
|
||||
const Actions = {
|
||||
START_AUTH,
|
||||
@ -10,7 +10,7 @@ const Actions = {
|
||||
SUCCESS_AUTH,
|
||||
FAILURE_AUTH,
|
||||
LOGOUT
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Actions,
|
||||
@ -32,4 +32,4 @@ module.exports = {
|
||||
logout: () => ({
|
||||
type: LOGOUT
|
||||
})
|
||||
};
|
||||
}
|
||||
|
@ -13,17 +13,17 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { StaticRouter, BrowserRouter } from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { StaticRouter, BrowserRouter } from 'react-router-dom'
|
||||
import { Provider } from 'react-redux'
|
||||
|
||||
import MuiProvider from 'client/providers/muiProvider';
|
||||
import NotistackProvider from 'client/providers/notistackProvider';
|
||||
import { TranslateProvider } from 'client/components/HOC';
|
||||
import MuiProvider from 'client/providers/muiProvider'
|
||||
import NotistackProvider from 'client/providers/notistackProvider'
|
||||
import { TranslateProvider } from 'client/components/HOC'
|
||||
|
||||
import Router from 'client/router';
|
||||
import Router from 'client/router'
|
||||
|
||||
const App = ({ location, context, store }) => (
|
||||
<MuiProvider>
|
||||
@ -45,18 +45,18 @@ const App = ({ location, context, store }) => (
|
||||
</NotistackProvider>
|
||||
</Provider>
|
||||
</MuiProvider>
|
||||
);
|
||||
)
|
||||
|
||||
App.propTypes = {
|
||||
location: PropTypes.string,
|
||||
context: PropTypes.shape({}),
|
||||
store: PropTypes.shape({})
|
||||
};
|
||||
}
|
||||
|
||||
App.defaultProps = {
|
||||
location: '',
|
||||
context: {},
|
||||
store: {}
|
||||
};
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default App
|
||||
|
@ -8,19 +8,19 @@ export const breakpoints = {
|
||||
tablet: 640,
|
||||
laptop: 1024,
|
||||
desktop: 1280
|
||||
};
|
||||
}
|
||||
|
||||
export const toolbar = {
|
||||
regular: 56,
|
||||
xs: 48,
|
||||
sm: 64
|
||||
};
|
||||
}
|
||||
|
||||
export const footer = {
|
||||
regular: 30
|
||||
};
|
||||
}
|
||||
|
||||
export const sidebar = {
|
||||
minified: 60,
|
||||
fixed: 240
|
||||
};
|
||||
}
|
||||
|
@ -10,6 +10,6 @@ const UbuntuFont = {
|
||||
url(/client/assets/fonts/Ubuntu/ubuntu.ttf) format('truetype'),
|
||||
url(/client/assets/fonts/Ubuntu/ubuntu.svg#Ubuntu) format('svg');
|
||||
`
|
||||
};
|
||||
}
|
||||
|
||||
export default UbuntuFont;
|
||||
export default UbuntuFont
|
||||
|
@ -2,16 +2,16 @@ import {
|
||||
createMuiTheme,
|
||||
responsiveFontSizes,
|
||||
createGenerateClassName
|
||||
} from '@material-ui/core';
|
||||
} from '@material-ui/core'
|
||||
|
||||
import UbuntuFont from 'client/assets/theme/fonts';
|
||||
import { toolbar, breakpoints } from 'client/assets/theme/defaults';
|
||||
import UbuntuFont from 'client/assets/theme/fonts'
|
||||
import { toolbar, breakpoints } from 'client/assets/theme/defaults'
|
||||
|
||||
const { xs, sm } = breakpoints;
|
||||
const { xs, sm } = breakpoints
|
||||
|
||||
export const generateClassName = createGenerateClassName({
|
||||
productionPrefix: 'one-'
|
||||
});
|
||||
})
|
||||
|
||||
export default responsiveFontSizes(
|
||||
createMuiTheme({
|
||||
@ -117,4 +117,4 @@ export default responsiveFontSizes(
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
)
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
makeStyles,
|
||||
@ -10,12 +11,12 @@ import {
|
||||
CardHeader,
|
||||
CardContent,
|
||||
CardActions
|
||||
} from '@material-ui/core';
|
||||
import FileIcon from '@material-ui/icons/Description';
|
||||
import VideogameAssetIcon from '@material-ui/icons/VideogameAsset';
|
||||
import AccountTreeIcon from '@material-ui/icons/AccountTree';
|
||||
} from '@material-ui/core'
|
||||
import FileIcon from '@material-ui/icons/Description'
|
||||
import VideogameAssetIcon from '@material-ui/icons/VideogameAsset'
|
||||
import AccountTreeIcon from '@material-ui/icons/AccountTree'
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
@ -42,18 +43,18 @@ const useStyles = makeStyles(theme => ({
|
||||
},
|
||||
badge: {},
|
||||
icon: {}
|
||||
}));
|
||||
}))
|
||||
|
||||
const ApplicationTemplateCard = memo(
|
||||
({ value, handleEdit, handleDeploy, handleShow, handleRemove }) => {
|
||||
const classes = useStyles();
|
||||
const { ID, NAME, TEMPLATE } = value;
|
||||
const { description, networks = [], roles = [] } = TEMPLATE.BODY;
|
||||
const classes = useStyles()
|
||||
const { NAME, TEMPLATE } = value
|
||||
const { description, networks = [], roles = [] } = TEMPLATE.BODY
|
||||
|
||||
const numberOfNetworks = Object.keys(networks)?.length ?? 0;
|
||||
const numberOfTiers = Object.keys(roles)?.length ?? 0;
|
||||
const numberOfTiers = roles?.length ?? 0
|
||||
const numberOfNetworks = Object.keys(networks)?.length ?? 0
|
||||
|
||||
const badgePosition = { vertical: 'top', horizontal: 'right' };
|
||||
const badgePosition = { vertical: 'top', horizontal: 'right' }
|
||||
|
||||
return (
|
||||
<Fade in unmountOnExit={false}>
|
||||
@ -124,8 +125,36 @@ const ApplicationTemplateCard = memo(
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Fade>
|
||||
);
|
||||
)
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
export default ApplicationTemplateCard;
|
||||
ApplicationTemplateCard.propTypes = {
|
||||
value: PropTypes.shape({
|
||||
ID: PropTypes.string,
|
||||
NAME: PropTypes.string.isRequired,
|
||||
TEMPLATE: PropTypes.shape({
|
||||
BODY: PropTypes.shape({
|
||||
description: PropTypes.string,
|
||||
networks: PropTypes.object,
|
||||
roles: PropTypes.arrayOf(PropTypes.object)
|
||||
}).isRequired
|
||||
}).isRequired
|
||||
}),
|
||||
handleEdit: PropTypes.func,
|
||||
handleDeploy: PropTypes.func,
|
||||
handleShow: PropTypes.func,
|
||||
handleRemove: PropTypes.func
|
||||
}
|
||||
|
||||
ApplicationTemplateCard.defaultProps = {
|
||||
value: {},
|
||||
handleEdit: undefined,
|
||||
handleDeploy: undefined,
|
||||
handleShow: undefined,
|
||||
handleRemove: undefined
|
||||
}
|
||||
|
||||
ApplicationTemplateCard.displayName = 'Application TemplateCard'
|
||||
|
||||
export default ApplicationTemplateCard
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import clsx from 'clsx'
|
||||
import {
|
||||
makeStyles,
|
||||
Card,
|
||||
@ -10,13 +11,13 @@ import {
|
||||
CardContent,
|
||||
Badge,
|
||||
Box
|
||||
} from '@material-ui/core';
|
||||
import StorageIcon from '@material-ui/icons/Storage';
|
||||
import VideogameAssetIcon from '@material-ui/icons/VideogameAsset';
|
||||
import AccountTreeIcon from '@material-ui/icons/AccountTree';
|
||||
import FolderOpenIcon from '@material-ui/icons/FolderOpen';
|
||||
} from '@material-ui/core'
|
||||
import StorageIcon from '@material-ui/icons/Storage'
|
||||
import VideogameAssetIcon from '@material-ui/icons/VideogameAsset'
|
||||
import AccountTreeIcon from '@material-ui/icons/AccountTree'
|
||||
import FolderOpenIcon from '@material-ui/icons/FolderOpen'
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
@ -54,18 +55,18 @@ const useStyles = makeStyles(theme => ({
|
||||
},
|
||||
badge: {},
|
||||
icon: {}
|
||||
}));
|
||||
}))
|
||||
|
||||
const ClusterCard = React.memo(
|
||||
({ value, isSelected, handleSelect, handleUnselect }) => {
|
||||
const classes = useStyles();
|
||||
const { ID, NAME, HOSTS, VNETS, DATASTORES } = value;
|
||||
const classes = useStyles()
|
||||
const { NAME, HOSTS, VNETS, DATASTORES } = value
|
||||
|
||||
const hosts = [HOSTS?.ID ?? []].flat();
|
||||
const vnets = [VNETS?.ID ?? []].flat();
|
||||
const datastores = [DATASTORES?.ID ?? []].flat();
|
||||
const hosts = [HOSTS?.ID ?? []].flat()
|
||||
const vnets = [VNETS?.ID ?? []].flat()
|
||||
const datastores = [DATASTORES?.ID ?? []].flat()
|
||||
|
||||
const badgePosition = { vertical: 'top', horizontal: 'right' };
|
||||
const badgePosition = { vertical: 'top', horizontal: 'right' }
|
||||
|
||||
return (
|
||||
<Fade in unmountOnExit={false}>
|
||||
@ -124,8 +125,39 @@ const ClusterCard = React.memo(
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Fade>
|
||||
);
|
||||
)
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
export default ClusterCard;
|
||||
ClusterCard.propTypes = {
|
||||
value: PropTypes.shape({
|
||||
ID: PropTypes.string,
|
||||
NAME: PropTypes.string.isRequired,
|
||||
HOSTS: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.object
|
||||
]),
|
||||
VNETS: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.object
|
||||
]),
|
||||
DATASTORES: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.object
|
||||
])
|
||||
}),
|
||||
isSelected: PropTypes.bool,
|
||||
handleSelect: PropTypes.func,
|
||||
handleUnselect: PropTypes.func
|
||||
}
|
||||
|
||||
ClusterCard.defaultProps = {
|
||||
value: {},
|
||||
isSelected: false,
|
||||
handleSelect: undefined,
|
||||
handleUnselect: undefined
|
||||
}
|
||||
|
||||
ClusterCard.displayName = 'ClusterCard'
|
||||
|
||||
export default ClusterCard
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Card, CardHeader, Fade, makeStyles } from '@material-ui/core';
|
||||
import { Card, CardHeader, Fade, makeStyles } from '@material-ui/core'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
@ -13,10 +13,10 @@ const useStyles = makeStyles(theme => ({
|
||||
padding: theme.spacing(1),
|
||||
textAlign: 'center'
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
||||
const EmptyCard = React.memo(({ name }) => {
|
||||
const classes = useStyles();
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Fade in unmountOnExit>
|
||||
@ -27,15 +27,17 @@ const EmptyCard = React.memo(({ name }) => {
|
||||
/>
|
||||
</Card>
|
||||
</Fade>
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
EmptyCard.propTypes = {
|
||||
name: PropTypes.string
|
||||
};
|
||||
}
|
||||
|
||||
EmptyCard.defaultProps = {
|
||||
name: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export default EmptyCard;
|
||||
EmptyCard.displayName = 'EmptyCard'
|
||||
|
||||
export default EmptyCard
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
makeStyles,
|
||||
@ -7,9 +8,9 @@ import {
|
||||
CardHeader,
|
||||
CardActions,
|
||||
Fade
|
||||
} from '@material-ui/core';
|
||||
} from '@material-ui/core'
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
@ -33,12 +34,12 @@ const useStyles = makeStyles(theme => ({
|
||||
remove: {
|
||||
backgroundColor: theme.palette.error.dark
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
||||
const NetworkCard = React.memo(
|
||||
({ value, handleEdit, handleClone, handleRemove }) => {
|
||||
const classes = useStyles();
|
||||
const { mandatory, name, description, type, id, extra } = value;
|
||||
const classes = useStyles()
|
||||
const { mandatory, name, description } = value
|
||||
|
||||
return (
|
||||
<Fade in unmountOnExit={false}>
|
||||
@ -80,8 +81,31 @@ const NetworkCard = React.memo(
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Fade>
|
||||
);
|
||||
)
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
export default NetworkCard;
|
||||
NetworkCard.propTypes = {
|
||||
value: PropTypes.shape({
|
||||
mandatory: PropTypes.bool,
|
||||
name: PropTypes.string.isRequired,
|
||||
description: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
extra: PropTypes.string
|
||||
}),
|
||||
handleEdit: PropTypes.func,
|
||||
handleClone: PropTypes.func,
|
||||
handleRemove: PropTypes.func
|
||||
}
|
||||
|
||||
NetworkCard.defaultProps = {
|
||||
value: {},
|
||||
handleEdit: undefined,
|
||||
handleClone: undefined,
|
||||
handleRemove: undefined
|
||||
}
|
||||
|
||||
NetworkCard.displayName = 'NetworkCard'
|
||||
|
||||
export default NetworkCard
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import clsx from 'clsx';
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Card, CardActionArea, Fade, makeStyles } from '@material-ui/core';
|
||||
import { Skeleton } from '@material-ui/lab';
|
||||
import clsx from 'clsx'
|
||||
import { Card, CardActionArea, Fade, makeStyles } from '@material-ui/core'
|
||||
import { Skeleton } from '@material-ui/lab'
|
||||
|
||||
import useNearScreen from 'client/hooks/useNearScreen';
|
||||
import useNearScreen from 'client/hooks/useNearScreen'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
@ -20,29 +20,22 @@ const useStyles = makeStyles(theme => ({
|
||||
minHeight: 140,
|
||||
padding: theme.spacing(1)
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
||||
const SelectCard = React.memo(
|
||||
({ isSelected, handleSelect, handleUnselect, ID, NAME }) => {
|
||||
const classes = useStyles();
|
||||
const SelectCard = memo(
|
||||
({ title, isSelected, handleClick }) => {
|
||||
const classes = useStyles()
|
||||
const { isNearScreen, fromRef } = useNearScreen({
|
||||
distance: '100px'
|
||||
});
|
||||
})
|
||||
|
||||
return (
|
||||
<div ref={fromRef}>
|
||||
{isNearScreen ? (
|
||||
<Fade in={isNearScreen}>
|
||||
<Card
|
||||
className={clsx(classes.root, { [classes.selected]: isSelected })}
|
||||
>
|
||||
<CardActionArea
|
||||
className={classes.actionArea}
|
||||
onClick={() =>
|
||||
isSelected ? handleUnselect(ID) : handleSelect(ID)
|
||||
}
|
||||
>
|
||||
<span>{`📦 ${NAME}`}</span>
|
||||
<Card className={clsx(classes.root, { [classes.selected]: isSelected })}>
|
||||
<CardActionArea className={classes.actionArea} onClick={handleClick}>
|
||||
<span>{title}</span>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Fade>
|
||||
@ -50,24 +43,22 @@ const SelectCard = React.memo(
|
||||
<Skeleton variant="rect" width="100%" height={140} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
SelectCard.propTypes = {
|
||||
title: PropTypes.string,
|
||||
isSelected: PropTypes.bool,
|
||||
handleSelect: PropTypes.func,
|
||||
handleUnselect: PropTypes.func,
|
||||
ID: PropTypes.string,
|
||||
NAME: PropTypes.string
|
||||
};
|
||||
handleClick: PropTypes.func
|
||||
}
|
||||
|
||||
SelectCard.defaultProps = {
|
||||
title: undefined,
|
||||
isSelected: false,
|
||||
handleSelect: () => undefined,
|
||||
handleUnselect: () => undefined,
|
||||
ID: undefined,
|
||||
NAME: undefined
|
||||
};
|
||||
handleClick: () => undefined
|
||||
}
|
||||
|
||||
export default SelectCard;
|
||||
SelectCard.displayName = 'SelectCard'
|
||||
|
||||
export default SelectCard
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
makeStyles,
|
||||
@ -7,10 +8,10 @@ import {
|
||||
CardHeader,
|
||||
CardActions,
|
||||
Badge
|
||||
} from '@material-ui/core';
|
||||
import DesktopWindowsIcon from '@material-ui/icons/DesktopWindows';
|
||||
} from '@material-ui/core'
|
||||
import DesktopWindowsIcon from '@material-ui/icons/DesktopWindows'
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
root: {
|
||||
@ -32,12 +33,12 @@ const useStyles = makeStyles(() => ({
|
||||
lineClamp: 2,
|
||||
boxOrient: 'vertical'
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
||||
const TierCard = React.memo(
|
||||
const TierCard = memo(
|
||||
({ values, handleEdit, handleClone, handleRemove, cardProps }) => {
|
||||
const classes = useStyles();
|
||||
const { name = 'Tier name', cardinality } = values;
|
||||
const classes = useStyles()
|
||||
const { name, cardinality } = values
|
||||
|
||||
return (
|
||||
<Card className={classes.root} {...cardProps}>
|
||||
@ -82,8 +83,32 @@ const TierCard = React.memo(
|
||||
)}
|
||||
</CardActions>
|
||||
</Card>
|
||||
);
|
||||
)
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
export default TierCard;
|
||||
TierCard.propTypes = {
|
||||
values: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
cardinality: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number
|
||||
])
|
||||
}),
|
||||
handleEdit: PropTypes.func,
|
||||
handleClone: PropTypes.func,
|
||||
handleRemove: PropTypes.func,
|
||||
cardProps: PropTypes.object
|
||||
}
|
||||
|
||||
TierCard.defaultProps = {
|
||||
values: {},
|
||||
handleEdit: undefined,
|
||||
handleClone: undefined,
|
||||
handleRemove: undefined,
|
||||
cardProps: undefined
|
||||
}
|
||||
|
||||
TierCard.displayName = 'TierCard'
|
||||
|
||||
export default TierCard
|
||||
|
@ -1,9 +1,9 @@
|
||||
import ClusterCard from 'client/components/Cards/ClusterCard';
|
||||
import NetworkCard from 'client/components/Cards/NetworkCard';
|
||||
import TierCard from 'client/components/Cards/TierCard';
|
||||
import EmptyCard from 'client/components/Cards/EmptyCard';
|
||||
import SelectCard from 'client/components/Cards/SelectCard';
|
||||
import ApplicationTemplateCard from 'client/components/Cards/ApplicationTemplateCard';
|
||||
import ClusterCard from 'client/components/Cards/ClusterCard'
|
||||
import NetworkCard from 'client/components/Cards/NetworkCard'
|
||||
import TierCard from 'client/components/Cards/TierCard'
|
||||
import EmptyCard from 'client/components/Cards/EmptyCard'
|
||||
import SelectCard from 'client/components/Cards/SelectCard'
|
||||
import ApplicationTemplateCard from 'client/components/Cards/ApplicationTemplateCard'
|
||||
|
||||
export {
|
||||
ClusterCard,
|
||||
@ -12,4 +12,4 @@ export {
|
||||
EmptyCard,
|
||||
SelectCard,
|
||||
ApplicationTemplateCard
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { memo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
useMediaQuery,
|
||||
@ -8,22 +8,22 @@ import {
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions
|
||||
} from '@material-ui/core';
|
||||
import { useForm, FormProvider } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers';
|
||||
} from '@material-ui/core'
|
||||
import { useForm, FormProvider } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers'
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const DialogForm = memo(
|
||||
({ open, title, values, resolver, onSubmit, onCancel, children }) => {
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'));
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'))
|
||||
|
||||
const { handleSubmit, ...methods } = useForm({
|
||||
mode: 'onChange',
|
||||
reValidateMode: 'onSubmit',
|
||||
defaultValues: values,
|
||||
resolver: yupResolver(resolver)
|
||||
});
|
||||
})
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
@ -63,9 +63,9 @@ const DialogForm = memo(
|
||||
</DialogActions>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
)
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
DialogForm.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
@ -81,7 +81,7 @@ DialogForm.propTypes = {
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
DialogForm.defaultProps = {
|
||||
open: true,
|
||||
@ -91,6 +91,6 @@ DialogForm.defaultProps = {
|
||||
onSubmit: undefined,
|
||||
onCancel: undefined,
|
||||
children: null
|
||||
};
|
||||
}
|
||||
|
||||
export default DialogForm;
|
||||
export default DialogForm
|
||||
|
@ -1,3 +1,3 @@
|
||||
import DialogForm from 'client/components/Dialogs/DialogForm';
|
||||
import DialogForm from 'client/components/Dialogs/DialogForm'
|
||||
|
||||
export { DialogForm };
|
||||
export { DialogForm }
|
||||
|
@ -1,28 +1,14 @@
|
||||
/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
||||
/* not use this file except in compliance with the License. You may obtain */
|
||||
/* a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
|
||||
import React from 'react';
|
||||
import { Box, Link, Typography } from '@material-ui/core';
|
||||
import { Box, Link, Typography } from '@material-ui/core'
|
||||
|
||||
import footerStyles from 'client/components/Footer/styles';
|
||||
import { by } from 'client/constants';
|
||||
import footerStyles from 'client/components/Footer/styles'
|
||||
import { by } from 'client/constants'
|
||||
|
||||
const { text, url } = by;
|
||||
const { text, url } = by
|
||||
|
||||
const Footer = React.memo(() => {
|
||||
const classes = footerStyles();
|
||||
const Footer = memo(() => {
|
||||
const classes = footerStyles()
|
||||
|
||||
return (
|
||||
<Box className={classes.footer} component="footer">
|
||||
@ -37,7 +23,9 @@ const Footer = React.memo(() => {
|
||||
</Link>
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
export default Footer;
|
||||
Footer.displayName = 'Footer'
|
||||
|
||||
export default Footer
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
footer: {
|
||||
@ -21,4 +21,4 @@ export default makeStyles(theme => ({
|
||||
color: theme.palette.primary.light,
|
||||
marginLeft: theme.spacing(1)
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { TextField } from '@material-ui/core';
|
||||
import { Autocomplete } from '@material-ui/lab';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { TextField } from '@material-ui/core'
|
||||
import { Autocomplete } from '@material-ui/lab'
|
||||
import { Controller } from 'react-hook-form'
|
||||
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper';
|
||||
import { Tr } from 'client/components/HOC/Translate';
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper'
|
||||
import { Tr } from 'client/components/HOC/Translate'
|
||||
|
||||
const AutocompleteController = ({
|
||||
control,
|
||||
@ -18,7 +18,7 @@ const AutocompleteController = ({
|
||||
}) => (
|
||||
<Controller
|
||||
render={({ value: val, onBlur, onChange }) => {
|
||||
const selected = values.find(({ value }) => value === val) ?? null;
|
||||
const selected = values.find(({ value }) => value === val) ?? null
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
@ -42,12 +42,12 @@ const AutocompleteController = ({
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}}
|
||||
name={name}
|
||||
control={control}
|
||||
/>
|
||||
);
|
||||
)
|
||||
|
||||
AutocompleteController.propTypes = {
|
||||
control: PropTypes.object,
|
||||
@ -59,7 +59,7 @@ AutocompleteController.propTypes = {
|
||||
PropTypes.bool,
|
||||
PropTypes.objectOf(PropTypes.any)
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
AutocompleteController.defaultProps = {
|
||||
control: {},
|
||||
@ -68,6 +68,6 @@ AutocompleteController.defaultProps = {
|
||||
label: '',
|
||||
values: [],
|
||||
error: false
|
||||
};
|
||||
}
|
||||
|
||||
export default AutocompleteController;
|
||||
export default AutocompleteController
|
||||
|
@ -1,16 +1,16 @@
|
||||
import React, { memo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
Tooltip
|
||||
} from '@material-ui/core';
|
||||
import { Controller } from 'react-hook-form';
|
||||
} from '@material-ui/core'
|
||||
import { Controller } from 'react-hook-form'
|
||||
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper';
|
||||
import { Tr } from 'client/components/HOC/Translate';
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper'
|
||||
import { Tr } from 'client/components/HOC/Translate'
|
||||
|
||||
const CheckboxController = memo(
|
||||
({ control, cy, name, label, tooltip, error }) => (
|
||||
@ -40,7 +40,7 @@ const CheckboxController = memo(
|
||||
/>
|
||||
),
|
||||
(prevProps, nextProps) => prevProps.error === nextProps.error
|
||||
);
|
||||
)
|
||||
|
||||
CheckboxController.propTypes = {
|
||||
control: PropTypes.object,
|
||||
@ -52,7 +52,7 @@ CheckboxController.propTypes = {
|
||||
PropTypes.bool,
|
||||
PropTypes.objectOf(PropTypes.any)
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
CheckboxController.defaultProps = {
|
||||
control: {},
|
||||
@ -62,6 +62,8 @@ CheckboxController.defaultProps = {
|
||||
tooltip: undefined,
|
||||
values: [],
|
||||
error: false
|
||||
};
|
||||
}
|
||||
|
||||
export default CheckboxController;
|
||||
CheckboxController.displayName = 'CheckboxController'
|
||||
|
||||
export default CheckboxController
|
||||
|
@ -1,39 +1,28 @@
|
||||
import React from 'react';
|
||||
import { string } from 'prop-types';
|
||||
import React from 'react'
|
||||
import { string } from 'prop-types'
|
||||
|
||||
import {
|
||||
Box,
|
||||
darken,
|
||||
lighten,
|
||||
makeStyles,
|
||||
Typography
|
||||
} from '@material-ui/core';
|
||||
import { Info as InfoIcon } from '@material-ui/icons';
|
||||
import { Tr } from 'client/components/HOC/Translate';
|
||||
import { Box, makeStyles, Typography } from '@material-ui/core'
|
||||
import { Info as InfoIcon } from '@material-ui/icons'
|
||||
import { Tr } from 'client/components/HOC/Translate'
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
const getColor = theme.palette.type === 'light' ? darken : lighten;
|
||||
const getBackgroundColor = theme.palette.type === 'light' ? lighten : darken;
|
||||
|
||||
return {
|
||||
root: {
|
||||
color: theme.palette.error.dark,
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
},
|
||||
icon: {
|
||||
fontSize: 16
|
||||
},
|
||||
text: {
|
||||
...theme.typography.body1,
|
||||
paddingLeft: theme.spacing(1),
|
||||
overflowWrap: 'anywhere'
|
||||
}
|
||||
};
|
||||
});
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
color: theme.palette.error.dark,
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
},
|
||||
icon: {
|
||||
fontSize: 16
|
||||
},
|
||||
text: {
|
||||
...theme.typography.body1,
|
||||
paddingLeft: theme.spacing(1),
|
||||
overflowWrap: 'anywhere'
|
||||
}
|
||||
}))
|
||||
|
||||
const ErrorHelper = ({ label, ...rest }) => {
|
||||
const classes = useStyles();
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Box component="span" className={classes.root} {...rest}>
|
||||
@ -46,15 +35,15 @@ const ErrorHelper = ({ label, ...rest }) => {
|
||||
{Tr(label)}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
ErrorHelper.propTypes = {
|
||||
label: string
|
||||
};
|
||||
}
|
||||
|
||||
ErrorHelper.defaultProps = {
|
||||
label: 'Error'
|
||||
};
|
||||
}
|
||||
|
||||
export default ErrorHelper;
|
||||
export default ErrorHelper
|
||||
|
@ -13,19 +13,19 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React from 'react';
|
||||
import React from 'react'
|
||||
|
||||
import { MenuItem, TextField } from '@material-ui/core';
|
||||
import { FilterVintage } from '@material-ui/icons';
|
||||
import { MenuItem, TextField } from '@material-ui/core'
|
||||
import { FilterVintage } from '@material-ui/icons'
|
||||
|
||||
import useAuth from 'client/hooks/useAuth';
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { FILTER_POOL } from 'client/constants';
|
||||
import useAuth from 'client/hooks/useAuth'
|
||||
import useOpennebula from 'client/hooks/useOpennebula'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { FILTER_POOL } from 'client/constants'
|
||||
|
||||
const GroupSelect = props => {
|
||||
const { filterPool, authUser } = useAuth();
|
||||
const { groups } = useOpennebula();
|
||||
const { filterPool, authUser } = useAuth()
|
||||
const { groups } = useOpennebula()
|
||||
|
||||
const defaultValue = React.useMemo(
|
||||
() =>
|
||||
@ -33,7 +33,7 @@ const GroupSelect = props => {
|
||||
? FILTER_POOL.ALL_RESOURCES
|
||||
: authUser?.GID,
|
||||
[filterPool]
|
||||
);
|
||||
)
|
||||
|
||||
const orderGroups = React.useMemo(
|
||||
() =>
|
||||
@ -53,7 +53,7 @@ const GroupSelect = props => {
|
||||
</MenuItem>
|
||||
)),
|
||||
[groups]
|
||||
);
|
||||
)
|
||||
|
||||
return (
|
||||
<TextField
|
||||
@ -69,11 +69,11 @@ const GroupSelect = props => {
|
||||
<MenuItem value={FILTER_POOL.ALL_RESOURCES}>{Tr('Show all')}</MenuItem>
|
||||
{orderGroups}
|
||||
</TextField>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
GroupSelect.propTypes = {};
|
||||
GroupSelect.propTypes = {}
|
||||
|
||||
GroupSelect.defaultProps = {};
|
||||
GroupSelect.defaultProps = {}
|
||||
|
||||
export default GroupSelect;
|
||||
export default GroupSelect
|
||||
|
@ -1,16 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@material-ui/core';
|
||||
import AceEditor from 'react-ace';
|
||||
import PropTypes from 'prop-types';
|
||||
import 'ace-builds/src-noconflict/mode-json';
|
||||
import 'ace-builds/src-noconflict/theme-github';
|
||||
import React from 'react'
|
||||
import { Box } from '@material-ui/core'
|
||||
import AceEditor from 'react-ace'
|
||||
import PropTypes from 'prop-types'
|
||||
import 'ace-builds/src-noconflict/mode-json'
|
||||
import 'ace-builds/src-noconflict/theme-github'
|
||||
|
||||
const { string } = PropTypes;
|
||||
const { string } = PropTypes
|
||||
|
||||
const InputCode = ({ code, language, ...props }) => {
|
||||
const handleChange = newValue => {
|
||||
console.log('change', newValue);
|
||||
};
|
||||
console.log('change', newValue)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box height="100%" minHeight={200}>
|
||||
@ -36,17 +36,17 @@ const InputCode = ({ code, language, ...props }) => {
|
||||
{...props}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
InputCode.propTypes = {
|
||||
code: string,
|
||||
language: string
|
||||
};
|
||||
}
|
||||
|
||||
InputCode.defaultProps = {
|
||||
code: '',
|
||||
language: 'json'
|
||||
};
|
||||
}
|
||||
|
||||
export default InputCode;
|
||||
export default InputCode
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React, { memo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { TextField, MenuItem } from '@material-ui/core';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { TextField, MenuItem } from '@material-ui/core'
|
||||
import { Controller } from 'react-hook-form'
|
||||
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper';
|
||||
import { Tr } from 'client/components/HOC/Translate';
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper'
|
||||
import { Tr } from 'client/components/HOC/Translate'
|
||||
|
||||
const SelectController = memo(
|
||||
({ control, cy, name, label, values, error }) => (
|
||||
@ -34,7 +34,7 @@ const SelectController = memo(
|
||||
/>
|
||||
),
|
||||
(prevProps, nextProps) => prevProps.error === nextProps.error
|
||||
);
|
||||
)
|
||||
|
||||
SelectController.propTypes = {
|
||||
control: PropTypes.object,
|
||||
@ -46,7 +46,7 @@ SelectController.propTypes = {
|
||||
PropTypes.bool,
|
||||
PropTypes.objectOf(PropTypes.any)
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
SelectController.defaultProps = {
|
||||
control: {},
|
||||
@ -55,6 +55,8 @@ SelectController.defaultProps = {
|
||||
label: '',
|
||||
values: [],
|
||||
error: false
|
||||
};
|
||||
}
|
||||
|
||||
export default SelectController;
|
||||
SelectController.displayName = 'SelectController'
|
||||
|
||||
export default SelectController
|
||||
|
@ -1,20 +1,20 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, CircularProgress, Button } from '@material-ui/core';
|
||||
import { makeStyles, CircularProgress, Button } from '@material-ui/core'
|
||||
|
||||
import { Submit } from 'client/constants/translates';
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { Submit } from 'client/constants/translates'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
button: {
|
||||
transition: 'disabled 0.5s ease',
|
||||
margin: theme.spacing(3, 0, 2)
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
||||
const SubmitButton = ({ isSubmitting, label, ...rest }) => {
|
||||
const classes = useStyles();
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Button
|
||||
@ -28,17 +28,17 @@ const SubmitButton = ({ isSubmitting, label, ...rest }) => {
|
||||
{isSubmitting && <CircularProgress size={24} />}
|
||||
{!isSubmitting && (label ?? Tr(Submit))}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
SubmitButton.propTypes = {
|
||||
isSubmitting: PropTypes.bool,
|
||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.node])
|
||||
};
|
||||
}
|
||||
|
||||
SubmitButton.defaultProps = {
|
||||
isSubmitting: false,
|
||||
label: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export default SubmitButton;
|
||||
export default SubmitButton
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React, { memo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { TextField } from '@material-ui/core';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { TextField } from '@material-ui/core'
|
||||
import { Controller } from 'react-hook-form'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper';
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper'
|
||||
|
||||
const TextController = memo(
|
||||
({ control, cy, type, name, label, error }) => (
|
||||
@ -26,7 +26,7 @@ const TextController = memo(
|
||||
/>
|
||||
),
|
||||
(prevProps, nextProps) => prevProps.error === nextProps.error
|
||||
);
|
||||
)
|
||||
|
||||
TextController.propTypes = {
|
||||
control: PropTypes.object,
|
||||
@ -38,7 +38,7 @@ TextController.propTypes = {
|
||||
PropTypes.bool,
|
||||
PropTypes.objectOf(PropTypes.any)
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
TextController.defaultProps = {
|
||||
control: {},
|
||||
@ -47,6 +47,8 @@ TextController.defaultProps = {
|
||||
name: '',
|
||||
label: '',
|
||||
error: false
|
||||
};
|
||||
}
|
||||
|
||||
export default TextController;
|
||||
TextController.displayName = 'TextController'
|
||||
|
||||
export default TextController
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { memo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { styled, Button, MobileStepper } from '@material-ui/core';
|
||||
import { KeyboardArrowLeft, KeyboardArrowRight } from '@material-ui/icons';
|
||||
import { styled, Button, MobileStepper } from '@material-ui/core'
|
||||
import { KeyboardArrowLeft, KeyboardArrowRight } from '@material-ui/icons'
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const StickyMobileStepper = styled(MobileStepper)({
|
||||
position: 'sticky',
|
||||
@ -12,7 +12,7 @@ const StickyMobileStepper = styled(MobileStepper)({
|
||||
backdropFilter: 'blur(5px)',
|
||||
background: '#fafafa9c',
|
||||
zIndex: 1
|
||||
});
|
||||
})
|
||||
|
||||
const CustomMobileStepper = memo(
|
||||
({
|
||||
@ -42,7 +42,7 @@ const CustomMobileStepper = memo(
|
||||
/>
|
||||
),
|
||||
(prev, next) => prev.activeStep === next.activeStep
|
||||
);
|
||||
)
|
||||
|
||||
CustomMobileStepper.propTypes = {
|
||||
totalSteps: PropTypes.number,
|
||||
@ -51,7 +51,7 @@ CustomMobileStepper.propTypes = {
|
||||
disabledBack: PropTypes.bool,
|
||||
handleNext: PropTypes.func,
|
||||
handleBack: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
CustomMobileStepper.defaultProps = {
|
||||
totalSteps: 0,
|
||||
@ -60,6 +60,8 @@ CustomMobileStepper.defaultProps = {
|
||||
disabledBack: false,
|
||||
handleNext: () => undefined,
|
||||
handleBack: () => undefined
|
||||
};
|
||||
}
|
||||
|
||||
export default CustomMobileStepper;
|
||||
CustomMobileStepper.displayName = 'MobileStepper'
|
||||
|
||||
export default CustomMobileStepper
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { memo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
styled,
|
||||
@ -8,9 +8,9 @@ import {
|
||||
Step,
|
||||
StepLabel,
|
||||
Box
|
||||
} from '@material-ui/core';
|
||||
} from '@material-ui/core'
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const StickyStepper = styled(Stepper)({
|
||||
position: 'sticky',
|
||||
@ -18,7 +18,7 @@ const StickyStepper = styled(Stepper)({
|
||||
backdropFilter: 'blur(5px)',
|
||||
background: '#fafafa9c',
|
||||
zIndex: 1
|
||||
});
|
||||
})
|
||||
|
||||
const CustomStepper = memo(
|
||||
({ steps, activeStep, lastStep, disabledBack, handleNext, handleBack }) => (
|
||||
@ -41,7 +41,7 @@ const CustomStepper = memo(
|
||||
</>
|
||||
),
|
||||
(prev, next) => prev.activeStep === next.activeStep
|
||||
);
|
||||
)
|
||||
|
||||
CustomStepper.propTypes = {
|
||||
steps: PropTypes.arrayOf(
|
||||
@ -55,7 +55,7 @@ CustomStepper.propTypes = {
|
||||
disabledBack: PropTypes.bool.isRequired,
|
||||
handleNext: PropTypes.func,
|
||||
handleBack: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
CustomStepper.defaultProps = {
|
||||
steps: [],
|
||||
@ -64,6 +64,8 @@ CustomStepper.defaultProps = {
|
||||
disabledBack: false,
|
||||
handleNext: () => undefined,
|
||||
handleBack: () => undefined
|
||||
};
|
||||
}
|
||||
|
||||
export default CustomStepper;
|
||||
CustomStepper.displayName = 'Stepper'
|
||||
|
||||
export default CustomStepper
|
||||
|
@ -1,60 +1,60 @@
|
||||
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useState, useMemo, useCallback, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useMediaQuery } from '@material-ui/core';
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
import { useMediaQuery } from '@material-ui/core'
|
||||
|
||||
import CustomMobileStepper from 'client/components/FormStepper/MobileStepper';
|
||||
import CustomStepper from 'client/components/FormStepper/Stepper';
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper';
|
||||
import CustomMobileStepper from 'client/components/FormStepper/MobileStepper'
|
||||
import CustomStepper from 'client/components/FormStepper/Stepper'
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper'
|
||||
|
||||
const FIRST_STEP = 0;
|
||||
const FIRST_STEP = 0
|
||||
|
||||
const FormStepper = ({ steps, schema, onSubmit }) => {
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'));
|
||||
const { watch, trigger, reset, errors } = useFormContext();
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'))
|
||||
const { watch, trigger, reset, errors } = useFormContext()
|
||||
|
||||
const [formData, setFormData] = useState(() => watch());
|
||||
const [activeStep, setActiveStep] = useState(FIRST_STEP);
|
||||
const [formData, setFormData] = useState(() => watch())
|
||||
const [activeStep, setActiveStep] = useState(FIRST_STEP)
|
||||
|
||||
const totalSteps = useMemo(() => steps?.length, [steps]);
|
||||
const lastStep = useMemo(() => totalSteps - 1, [totalSteps]);
|
||||
const disabledBack = useMemo(() => activeStep === FIRST_STEP, [activeStep]);
|
||||
const totalSteps = useMemo(() => steps?.length, [steps])
|
||||
const lastStep = useMemo(() => totalSteps - 1, [totalSteps])
|
||||
const disabledBack = useMemo(() => activeStep === FIRST_STEP, [activeStep])
|
||||
|
||||
useEffect(() => {
|
||||
reset({ ...formData }, { errors: false });
|
||||
}, [formData]);
|
||||
reset({ ...formData }, { errors: false })
|
||||
}, [formData])
|
||||
|
||||
const handleNext = () => {
|
||||
const { id, resolver } = steps[activeStep];
|
||||
const currentData = watch()[id];
|
||||
const { id, resolver } = steps[activeStep]
|
||||
const currentData = watch()[id]
|
||||
|
||||
resolver
|
||||
.validate(currentData)
|
||||
.then(() => {
|
||||
const validateData = { ...formData, [id]: currentData };
|
||||
const validateData = { ...formData, [id]: currentData }
|
||||
|
||||
if (activeStep === lastStep) {
|
||||
onSubmit(schema.cast(validateData));
|
||||
onSubmit(schema.cast(validateData))
|
||||
} else {
|
||||
setFormData(validateData);
|
||||
setActiveStep(prevActiveStep => prevActiveStep + 1);
|
||||
setFormData(validateData)
|
||||
setActiveStep(prevActiveStep => prevActiveStep + 1)
|
||||
}
|
||||
})
|
||||
.catch(() => trigger(id));
|
||||
};
|
||||
.catch(() => trigger(id))
|
||||
}
|
||||
|
||||
const handleBack = useCallback(() => {
|
||||
if (activeStep <= FIRST_STEP) return;
|
||||
if (activeStep <= FIRST_STEP) return
|
||||
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1);
|
||||
}, [activeStep]);
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1)
|
||||
}, [activeStep])
|
||||
|
||||
const { id, content: Content } = useMemo(() => steps[activeStep], [
|
||||
formData,
|
||||
activeStep,
|
||||
setFormData
|
||||
]);
|
||||
])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -84,8 +84,8 @@ const FormStepper = ({ steps, schema, onSubmit }) => {
|
||||
)}
|
||||
{Content && <Content data={formData[id]} setFormData={setFormData} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
FormStepper.propTypes = {
|
||||
steps: PropTypes.arrayOf(
|
||||
@ -99,12 +99,12 @@ FormStepper.propTypes = {
|
||||
),
|
||||
schema: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired,
|
||||
onSubmit: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
FormStepper.defaultProps = {
|
||||
steps: [],
|
||||
schema: {},
|
||||
onSubmit: console.log
|
||||
};
|
||||
}
|
||||
|
||||
export default FormStepper;
|
||||
export default FormStepper
|
||||
|
@ -1,36 +1,36 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Grid } from '@material-ui/core';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { Grid } from '@material-ui/core'
|
||||
import { useFormContext, useWatch } from 'react-hook-form'
|
||||
|
||||
import { TYPE_INPUT } from 'client/constants';
|
||||
import TextController from 'client/components/FormControl/TextController';
|
||||
import SelectController from 'client/components/FormControl/SelectController';
|
||||
import CheckboxController from 'client/components/FormControl/CheckboxController';
|
||||
import AutocompleteController from 'client/components/FormControl/AutocompleteController';
|
||||
import { TYPE_INPUT } from 'client/constants'
|
||||
import TextController from 'client/components/FormControl/TextController'
|
||||
import SelectController from 'client/components/FormControl/SelectController'
|
||||
import CheckboxController from 'client/components/FormControl/CheckboxController'
|
||||
import AutocompleteController from 'client/components/FormControl/AutocompleteController'
|
||||
|
||||
const InputController = {
|
||||
[TYPE_INPUT.TEXT]: TextController,
|
||||
[TYPE_INPUT.SELECT]: SelectController,
|
||||
[TYPE_INPUT.CHECKBOX]: CheckboxController,
|
||||
[TYPE_INPUT.AUTOCOMPLETE]: AutocompleteController
|
||||
};
|
||||
}
|
||||
|
||||
const FormWithSchema = ({ id, cy, fields }) => {
|
||||
const { control, errors } = useFormContext();
|
||||
const { control, errors } = useFormContext()
|
||||
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
{fields?.map(
|
||||
({ name, type, htmlType, label, values, dependOf, tooltip, grid }) => {
|
||||
const dataCy = `${cy}-${name}`;
|
||||
const inputName = id ? `${id}.${name}` : name;
|
||||
const formError = id ? errors[id] : errors;
|
||||
const inputError = formError ? formError[name] : false;
|
||||
const dataCy = `${cy}-${name}`
|
||||
const inputName = id ? `${id}.${name}` : name
|
||||
const formError = id ? errors[id] : errors
|
||||
const inputError = formError ? formError[name] : false
|
||||
const dependValue = dependOf
|
||||
? useWatch({ control, name: id ? `${id}.${dependOf}` : dependOf })
|
||||
: null;
|
||||
: null
|
||||
|
||||
return (
|
||||
InputController[type] && (
|
||||
@ -47,23 +47,17 @@ const FormWithSchema = ({ id, cy, fields }) => {
|
||||
})}
|
||||
</Grid>
|
||||
)
|
||||
);
|
||||
)
|
||||
}
|
||||
)}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
FormWithSchema.propTypes = {
|
||||
id: PropTypes.string,
|
||||
cy: PropTypes.string,
|
||||
fields: PropTypes.arrayOf(PropTypes.object)
|
||||
};
|
||||
}
|
||||
|
||||
FormWithSchema.defaultProps = {
|
||||
id: '',
|
||||
cy: 'form',
|
||||
fields: []
|
||||
};
|
||||
|
||||
export default FormWithSchema;
|
||||
export default FormWithSchema
|
||||
|
@ -1,3 +1,3 @@
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema';
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
export { FormWithSchema };
|
||||
export { FormWithSchema }
|
||||
|
@ -13,21 +13,21 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Box, Container } from '@material-ui/core';
|
||||
import { CSSTransition } from 'react-transition-group';
|
||||
import { Box, Container } from '@material-ui/core'
|
||||
import { CSSTransition } from 'react-transition-group'
|
||||
|
||||
import useGeneral from 'client/hooks/useGeneral';
|
||||
import Footer from 'client/components/Footer';
|
||||
import Header from 'client/components/Header';
|
||||
import internalStyles from 'client/components/HOC/InternalLayout/styles';
|
||||
import useGeneral from 'client/hooks/useGeneral'
|
||||
import Footer from 'client/components/Footer'
|
||||
import Header from 'client/components/Header'
|
||||
import internalStyles from 'client/components/HOC/InternalLayout/styles'
|
||||
|
||||
const InternalLayout = ({ authRoute, label, children }) => {
|
||||
const classes = internalStyles();
|
||||
const { isFixMenu } = useGeneral();
|
||||
const classes = internalStyles()
|
||||
const { isFixMenu } = useGeneral()
|
||||
|
||||
return authRoute ? (
|
||||
<Box className={clsx(classes.root, { [classes.isDrawerFixed]: isFixMenu })}>
|
||||
@ -57,8 +57,8 @@ const InternalLayout = ({ authRoute, label, children }) => {
|
||||
</Box>
|
||||
) : (
|
||||
children
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
InternalLayout.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
@ -68,12 +68,12 @@ InternalLayout.propTypes = {
|
||||
]),
|
||||
authRoute: PropTypes.bool.isRequired,
|
||||
label: PropTypes.string
|
||||
};
|
||||
}
|
||||
|
||||
InternalLayout.defaultProps = {
|
||||
children: [],
|
||||
authRoute: false,
|
||||
label: null
|
||||
};
|
||||
}
|
||||
|
||||
export default InternalLayout;
|
||||
export default InternalLayout
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { sidebar, toolbar, footer } from 'client/assets/theme/defaults';
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import { sidebar, toolbar, footer } from 'client/assets/theme/defaults'
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
root: {
|
||||
@ -70,4 +70,4 @@ export default makeStyles(theme => ({
|
||||
},
|
||||
enterDone: {},
|
||||
exitDone: {}
|
||||
}));
|
||||
}))
|
||||
|
@ -13,51 +13,51 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useLocation, Redirect } from 'react-router-dom';
|
||||
import React, { useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useLocation, Redirect } from 'react-router-dom'
|
||||
|
||||
import useAuth from 'client/hooks/useAuth';
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
import useAuth from 'client/hooks/useAuth'
|
||||
import useOpennebula from 'client/hooks/useOpennebula'
|
||||
|
||||
import LoadingScreen from 'client/components/LoadingScreen';
|
||||
import Sidebar from 'client/components/Sidebar';
|
||||
import Notifier from 'client/components/Notifier';
|
||||
import { PATH, findRouteByPathname } from 'client/router/endpoints';
|
||||
import LoadingScreen from 'client/components/LoadingScreen'
|
||||
import Sidebar from 'client/components/Sidebar'
|
||||
import Notifier from 'client/components/Notifier'
|
||||
import { PATH, findRouteByPathname } from 'client/router/endpoints'
|
||||
|
||||
const MainLayout = ({ children }) => {
|
||||
const { pathname } = useLocation();
|
||||
const { groups } = useOpennebula();
|
||||
const { pathname } = useLocation()
|
||||
const { groups } = useOpennebula()
|
||||
const {
|
||||
isLogged,
|
||||
isLoginInProcess,
|
||||
getAuthInfo,
|
||||
authUser,
|
||||
firstRender
|
||||
} = useAuth();
|
||||
} = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
if (isLogged && !isLoginInProcess) {
|
||||
getAuthInfo();
|
||||
getAuthInfo()
|
||||
}
|
||||
}, [isLogged, isLoginInProcess]);
|
||||
}, [isLogged, isLoginInProcess])
|
||||
|
||||
const { authenticated } = findRouteByPathname(pathname);
|
||||
const authRoute = Boolean(authenticated);
|
||||
const { authenticated } = findRouteByPathname(pathname)
|
||||
const authRoute = Boolean(authenticated)
|
||||
|
||||
// PENDING TO AUTHENTICATING OR FIRST RENDERING
|
||||
if (firstRender || (isLogged && authRoute && !authUser && !groups?.length)) {
|
||||
return <LoadingScreen />;
|
||||
return <LoadingScreen />
|
||||
}
|
||||
|
||||
// PROTECTED ROUTE
|
||||
if (authRoute && !isLogged && !isLoginInProcess) {
|
||||
return <Redirect to={PATH.LOGIN} />;
|
||||
return <Redirect to={PATH.LOGIN} />
|
||||
}
|
||||
|
||||
// PUBLIC ROUTE
|
||||
if (!authRoute && isLogged && !isLoginInProcess) {
|
||||
return <Redirect to={PATH.DASHBOARD} />;
|
||||
return <Redirect to={PATH.DASHBOARD} />
|
||||
}
|
||||
|
||||
return (
|
||||
@ -66,8 +66,8 @@ const MainLayout = ({ children }) => {
|
||||
{children}
|
||||
<Notifier />
|
||||
</>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
MainLayout.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
@ -75,10 +75,10 @@ MainLayout.propTypes = {
|
||||
PropTypes.node,
|
||||
PropTypes.string
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
MainLayout.defaultProps = {
|
||||
children: ''
|
||||
};
|
||||
}
|
||||
|
||||
export default MainLayout;
|
||||
export default MainLayout
|
||||
|
@ -13,18 +13,18 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React, { useContext, useState, useEffect, createContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Select } from '@material-ui/core';
|
||||
import { sprintf } from 'sprintf-js';
|
||||
import root from 'window-or-global';
|
||||
import { defaultLang } from 'server/utils/constants/defaults';
|
||||
import React, { useContext, useState, useEffect, createContext } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Select } from '@material-ui/core'
|
||||
import { sprintf } from 'sprintf-js'
|
||||
import root from 'window-or-global'
|
||||
import { defaultLang } from 'server/utils/constants/defaults'
|
||||
|
||||
const defaultFunction = () => undefined;
|
||||
const TranslateContext = createContext();
|
||||
const document = root.document;
|
||||
const defaultFunction = () => undefined
|
||||
const TranslateContext = createContext()
|
||||
const document = root.document
|
||||
let languageScript =
|
||||
document && document.createElement && document.createElement('script');
|
||||
document && document.createElement && document.createElement('script')
|
||||
|
||||
const GenerateScript = (
|
||||
language = defaultLang,
|
||||
@ -32,84 +32,84 @@ const GenerateScript = (
|
||||
setHash = defaultFunction
|
||||
) => {
|
||||
try {
|
||||
const script = document.createElement('script');
|
||||
script.src = `/client/assets/languages/${language}.js`;
|
||||
script.async = true;
|
||||
const script = document.createElement('script')
|
||||
script.src = `/client/assets/languages/${language}.js`
|
||||
script.async = true
|
||||
script.onload = () => {
|
||||
setLang(language);
|
||||
setHash(root.locale);
|
||||
};
|
||||
document.body.appendChild(script);
|
||||
languageScript = script;
|
||||
setLang(language)
|
||||
setHash(root.locale)
|
||||
}
|
||||
document.body.appendChild(script)
|
||||
languageScript = script
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (error) {}
|
||||
};
|
||||
}
|
||||
|
||||
const RemoveScript = () => {
|
||||
document.body.removeChild(languageScript);
|
||||
};
|
||||
document.body.removeChild(languageScript)
|
||||
}
|
||||
|
||||
const TranslateProvider = ({ children }) => {
|
||||
const [lang, setLang] = useState(defaultLang);
|
||||
const [hash, setHash] = useState({});
|
||||
const [lang, setLang] = useState(defaultLang)
|
||||
const [hash, setHash] = useState({})
|
||||
useEffect(() => {
|
||||
GenerateScript(lang, setLang, setHash);
|
||||
GenerateScript(lang, setLang, setHash)
|
||||
return () => {
|
||||
RemoveScript();
|
||||
};
|
||||
}, []);
|
||||
RemoveScript()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const changeLang = (language = defaultLang) => {
|
||||
RemoveScript();
|
||||
GenerateScript(language, setLang, setHash);
|
||||
};
|
||||
RemoveScript()
|
||||
GenerateScript(language, setLang, setHash)
|
||||
}
|
||||
|
||||
const value = {
|
||||
lang,
|
||||
hash,
|
||||
changeLang
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<TranslateContext.Provider value={value}>
|
||||
{children}
|
||||
</TranslateContext.Provider>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
const translate = (str = '', values) => {
|
||||
const context = useContext(TranslateContext);
|
||||
let key = str;
|
||||
const context = useContext(TranslateContext)
|
||||
let key = str
|
||||
|
||||
if (context?.hash[key]) {
|
||||
key = context.hash[key];
|
||||
key = context.hash[key]
|
||||
}
|
||||
|
||||
if (!!values && Array.isArray(values)) {
|
||||
key = sprintf(key, ...values);
|
||||
key = sprintf(key, ...values)
|
||||
}
|
||||
|
||||
return key;
|
||||
};
|
||||
return key
|
||||
}
|
||||
|
||||
const Tr = (str = '') => {
|
||||
let key = str;
|
||||
let values;
|
||||
let key = str
|
||||
let values
|
||||
|
||||
if (Array.isArray(str)) {
|
||||
key = str[0] || '';
|
||||
values = str[1];
|
||||
key = str[0] || ''
|
||||
values = str[1]
|
||||
}
|
||||
|
||||
const valuesTr = !!values && !Array.isArray(values) ? [values] : values;
|
||||
const valuesTr = !!values && !Array.isArray(values) ? [values] : values
|
||||
|
||||
return translate(key, valuesTr);
|
||||
};
|
||||
return translate(key, valuesTr)
|
||||
}
|
||||
|
||||
const SelectTranslate = () => {
|
||||
const context = useContext(TranslateContext);
|
||||
const context = useContext(TranslateContext)
|
||||
const languages =
|
||||
root && root.langs && Array.isArray(root.langs) ? root.langs : [];
|
||||
root && root.langs && Array.isArray(root.langs) ? root.langs : []
|
||||
const handleChange = (e, changeLang) => {
|
||||
if (
|
||||
e &&
|
||||
@ -118,9 +118,9 @@ const SelectTranslate = () => {
|
||||
changeLang &&
|
||||
typeof changeLang === 'function'
|
||||
) {
|
||||
changeLang(e.target.value);
|
||||
changeLang(e.target.value)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Select
|
||||
@ -135,33 +135,33 @@ const SelectTranslate = () => {
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
const Translate = ({ word = '', values }) => {
|
||||
const valuesTr = !!values && !Array.isArray(values) ? [values] : values;
|
||||
return translate(word, valuesTr);
|
||||
};
|
||||
const valuesTr = !!values && !Array.isArray(values) ? [values] : values
|
||||
return translate(word, valuesTr)
|
||||
}
|
||||
|
||||
TranslateProvider.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
TranslateProvider.defaultProps = {
|
||||
children: []
|
||||
};
|
||||
}
|
||||
|
||||
Translate.propTypes = {
|
||||
word: PropTypes.string,
|
||||
values: PropTypes.oneOfType([PropTypes.string, PropTypes.array])
|
||||
};
|
||||
}
|
||||
|
||||
Translate.defaultProps = {
|
||||
word: '',
|
||||
values: ''
|
||||
};
|
||||
}
|
||||
|
||||
export { TranslateContext, TranslateProvider, SelectTranslate, Translate, Tr };
|
||||
export { TranslateContext, TranslateProvider, SelectTranslate, Translate, Tr }
|
||||
|
@ -13,15 +13,15 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import InternalLayout from 'client/components/HOC/InternalLayout';
|
||||
import MainLayout from 'client/components/HOC/MainLayout';
|
||||
import InternalLayout from 'client/components/HOC/InternalLayout'
|
||||
import MainLayout from 'client/components/HOC/MainLayout'
|
||||
import {
|
||||
TranslateContext,
|
||||
TranslateProvider,
|
||||
Translate,
|
||||
Tr,
|
||||
SelectTranslate
|
||||
} from 'client/components/HOC/Translate';
|
||||
} from 'client/components/HOC/Translate'
|
||||
|
||||
export {
|
||||
InternalLayout,
|
||||
@ -31,4 +31,4 @@ export {
|
||||
Translate,
|
||||
Tr,
|
||||
SelectTranslate
|
||||
};
|
||||
}
|
||||
|
@ -1,32 +1,32 @@
|
||||
import React from 'react';
|
||||
import React from 'react'
|
||||
|
||||
import { Button } from '@material-ui/core';
|
||||
import FilterIcon from '@material-ui/icons/FilterDrama';
|
||||
import SelectedIcon from '@material-ui/icons/FilterVintage';
|
||||
import { Button } from '@material-ui/core'
|
||||
import FilterIcon from '@material-ui/icons/FilterDrama'
|
||||
import SelectedIcon from '@material-ui/icons/FilterVintage'
|
||||
|
||||
import useAuth from 'client/hooks/useAuth';
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
import Search from 'client/components/Search';
|
||||
import useAuth from 'client/hooks/useAuth'
|
||||
import useOpennebula from 'client/hooks/useOpennebula'
|
||||
import Search from 'client/components/Search'
|
||||
|
||||
import { FILTER_POOL } from 'client/constants';
|
||||
import HeaderPopover from 'client/components/Header/Popover';
|
||||
import headerStyles from 'client/components/Header/styles';
|
||||
import { FILTER_POOL } from 'client/constants'
|
||||
import HeaderPopover from 'client/components/Header/Popover'
|
||||
import headerStyles from 'client/components/Header/styles'
|
||||
|
||||
const { ALL_RESOURCES, PRIMARY_GROUP_RESOURCES } = FILTER_POOL;
|
||||
const { ALL_RESOURCES, PRIMARY_GROUP_RESOURCES } = FILTER_POOL
|
||||
|
||||
const Group = () => {
|
||||
const classes = headerStyles();
|
||||
const { authUser, filterPool, setPrimaryGroup } = useAuth();
|
||||
const { groups } = useOpennebula();
|
||||
const classes = headerStyles()
|
||||
const { authUser, filterPool, setPrimaryGroup } = useAuth()
|
||||
const { groups } = useOpennebula()
|
||||
|
||||
const handleChangeGroup = group => {
|
||||
group && setPrimaryGroup({ group });
|
||||
};
|
||||
group && setPrimaryGroup({ group })
|
||||
}
|
||||
|
||||
const renderResult = ({ ID, NAME }, handleClose) => {
|
||||
const isSelected =
|
||||
(filterPool === ALL_RESOURCES && ALL_RESOURCES === ID) ||
|
||||
(filterPool === PRIMARY_GROUP_RESOURCES && authUser?.GID === ID);
|
||||
(filterPool === PRIMARY_GROUP_RESOURCES && authUser?.GID === ID)
|
||||
|
||||
return (
|
||||
<Button
|
||||
@ -34,28 +34,28 @@ const Group = () => {
|
||||
fullWidth
|
||||
className={classes.groupButton}
|
||||
onClick={() => {
|
||||
handleChangeGroup(ID);
|
||||
handleClose();
|
||||
handleChangeGroup(ID)
|
||||
handleClose()
|
||||
}}
|
||||
>
|
||||
{NAME}
|
||||
{isSelected && <SelectedIcon className={classes.groupSelectedIcon} />}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
const sortGroupAsMainFirst = (a, b) => {
|
||||
if (a.ID === authUser?.GUID) {
|
||||
return -1;
|
||||
return -1
|
||||
} else if (b.ID === authUser?.GUID) {
|
||||
return 1;
|
||||
return 1
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
return 0
|
||||
}
|
||||
|
||||
const sortMainGroupFirst = groups
|
||||
?.concat({ ID: ALL_RESOURCES, NAME: 'Show All' })
|
||||
?.sort(sortGroupAsMainFirst);
|
||||
?.sort(sortGroupAsMainFirst)
|
||||
|
||||
return (
|
||||
<HeaderPopover
|
||||
@ -77,7 +77,7 @@ const Group = () => {
|
||||
/>
|
||||
)}
|
||||
</HeaderPopover>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default Group;
|
||||
export default Group
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
Box,
|
||||
@ -8,18 +8,18 @@ import {
|
||||
Popover,
|
||||
Typography,
|
||||
Button
|
||||
} from '@material-ui/core';
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
} from '@material-ui/core'
|
||||
import CloseIcon from '@material-ui/icons/Close'
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
import headerStyles from 'client/components/Header/styles';
|
||||
import clsx from 'clsx';
|
||||
import headerStyles from 'client/components/Header/styles'
|
||||
import clsx from 'clsx'
|
||||
|
||||
const typeButton = {
|
||||
button: Button,
|
||||
iconButton: IconButton
|
||||
};
|
||||
}
|
||||
|
||||
const HeaderPopover = ({
|
||||
id,
|
||||
@ -30,21 +30,21 @@ const HeaderPopover = ({
|
||||
disablePadding,
|
||||
children
|
||||
}) => {
|
||||
const classes = headerStyles();
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'));
|
||||
const classes = headerStyles()
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'))
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
|
||||
const handleOpen = event => setAnchorEl(event.currentTarget);
|
||||
const handleClose = () => setAnchorEl(null);
|
||||
const handleOpen = event => setAnchorEl(event.currentTarget)
|
||||
const handleClose = () => setAnchorEl(null)
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const anchorId = open ? id : undefined;
|
||||
const open = Boolean(anchorEl)
|
||||
const anchorId = open ? id : undefined
|
||||
|
||||
const ButtonComponent = React.useMemo(
|
||||
() => (buttonLabel ? typeButton.button : typeButton.iconButton),
|
||||
[buttonLabel]
|
||||
);
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -98,8 +98,8 @@ const HeaderPopover = ({
|
||||
{children({ handleClose })}
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
HeaderPopover.propTypes = {
|
||||
id: PropTypes.string,
|
||||
@ -109,7 +109,7 @@ HeaderPopover.propTypes = {
|
||||
headerTitle: PropTypes.string,
|
||||
disablePadding: PropTypes.bool,
|
||||
children: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
HeaderPopover.defaultProps = {
|
||||
id: 'id-popover',
|
||||
@ -119,6 +119,6 @@ HeaderPopover.defaultProps = {
|
||||
headerTitle: null,
|
||||
disablePadding: false,
|
||||
children: () => undefined
|
||||
};
|
||||
}
|
||||
|
||||
export default HeaderPopover;
|
||||
export default HeaderPopover
|
||||
|
@ -1,21 +1,22 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { MenuItem, MenuList, Divider } from '@material-ui/core';
|
||||
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
|
||||
import React from 'react'
|
||||
|
||||
import useAuth from 'client/hooks/useAuth';
|
||||
import { Tr, SelectTranslate } from 'client/components/HOC';
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { MenuItem, MenuList, Divider } from '@material-ui/core'
|
||||
import AccountCircleIcon from '@material-ui/icons/AccountCircle'
|
||||
|
||||
import { SignOut } from 'client/constants/translates';
|
||||
import { PATH } from 'client/router/endpoints';
|
||||
import HeaderPopover from 'client/components/Header/Popover';
|
||||
import useAuth from 'client/hooks/useAuth'
|
||||
import { Tr, SelectTranslate } from 'client/components/HOC'
|
||||
|
||||
import { SignOut } from 'client/constants/translates'
|
||||
import { PATH } from 'client/router/endpoints'
|
||||
import HeaderPopover from 'client/components/Header/Popover'
|
||||
|
||||
const User = React.memo(() => {
|
||||
const history = useHistory();
|
||||
const { logout, authUser } = useAuth();
|
||||
const history = useHistory()
|
||||
const { logout, authUser } = useAuth()
|
||||
|
||||
const handleLogout = () => logout();
|
||||
const handleGoToSettings = () => history.push(PATH.SETTINGS);
|
||||
const handleLogout = () => logout()
|
||||
const handleGoToSettings = () => history.push(PATH.SETTINGS)
|
||||
|
||||
return (
|
||||
<HeaderPopover
|
||||
@ -38,7 +39,9 @@ const User = React.memo(() => {
|
||||
</MenuList>
|
||||
)}
|
||||
</HeaderPopover>
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
export default User;
|
||||
User.displayName = 'UserHeaderComponent'
|
||||
|
||||
export default User
|
||||
|
@ -1,25 +1,10 @@
|
||||
/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
||||
/* not use this file except in compliance with the License. You may obtain */
|
||||
/* a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
import React from 'react'
|
||||
|
||||
import React from 'react';
|
||||
import { MenuItem, MenuList } from '@material-ui/core'
|
||||
import LanguageIcon from '@material-ui/icons/Language'
|
||||
|
||||
import { MenuItem, MenuList } from '@material-ui/core';
|
||||
import LanguageIcon from '@material-ui/icons/Language';
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import HeaderPopover from 'client/components/Header/Popover';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import HeaderPopover from 'client/components/Header/Popover'
|
||||
|
||||
const Zone = React.memo(() => (
|
||||
<HeaderPopover
|
||||
@ -34,6 +19,8 @@ const Zone = React.memo(() => (
|
||||
</MenuList>
|
||||
)}
|
||||
</HeaderPopover>
|
||||
));
|
||||
))
|
||||
|
||||
export default Zone;
|
||||
Zone.displayName = 'ZoneHeaderComponent'
|
||||
|
||||
export default Zone
|
||||
|
@ -13,8 +13,8 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
AppBar,
|
||||
@ -22,23 +22,23 @@ import {
|
||||
Typography,
|
||||
IconButton,
|
||||
useMediaQuery
|
||||
} from '@material-ui/core';
|
||||
import MenuIcon from '@material-ui/icons/Menu';
|
||||
} from '@material-ui/core'
|
||||
import MenuIcon from '@material-ui/icons/Menu'
|
||||
|
||||
import useAuth from 'client/hooks/useAuth';
|
||||
import useGeneral from 'client/hooks/useGeneral';
|
||||
import User from 'client/components/Header/User';
|
||||
import Group from 'client/components/Header/Group';
|
||||
import Zone from 'client/components/Header/Zone';
|
||||
import headerStyles from 'client/components/Header/styles';
|
||||
import useAuth from 'client/hooks/useAuth'
|
||||
import useGeneral from 'client/hooks/useGeneral'
|
||||
import User from 'client/components/Header/User'
|
||||
import Group from 'client/components/Header/Group'
|
||||
import Zone from 'client/components/Header/Zone'
|
||||
import headerStyles from 'client/components/Header/styles'
|
||||
|
||||
const Header = ({ title }) => {
|
||||
const { isOneAdmin } = useAuth();
|
||||
const { isFixMenu, fixMenu } = useGeneral();
|
||||
const classes = headerStyles();
|
||||
const isUpLg = useMediaQuery(theme => theme.breakpoints.up('lg'));
|
||||
const { isOneAdmin } = useAuth()
|
||||
const { isFixMenu, fixMenu } = useGeneral()
|
||||
const classes = headerStyles()
|
||||
const isUpLg = useMediaQuery(theme => theme.breakpoints.up('lg'))
|
||||
|
||||
const handleFixMenu = () => fixMenu(true);
|
||||
const handleFixMenu = () => fixMenu(true)
|
||||
|
||||
return React.useMemo(
|
||||
() => (
|
||||
@ -63,15 +63,15 @@ const Header = ({ title }) => {
|
||||
</AppBar>
|
||||
),
|
||||
[isFixMenu, fixMenu, isUpLg, isOneAdmin]
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
title: PropTypes.string
|
||||
};
|
||||
}
|
||||
|
||||
Header.defaultProps = {
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
export default Header;
|
||||
export default Header
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
title: {
|
||||
@ -40,4 +40,4 @@ export default makeStyles(theme => ({
|
||||
fontSize: '1rem',
|
||||
margin: theme.spacing(0, 2)
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
makeStyles,
|
||||
@ -7,8 +7,8 @@ import {
|
||||
CardContent,
|
||||
Card,
|
||||
Grid
|
||||
} from '@material-ui/core';
|
||||
import AddIcon from '@material-ui/icons/Add';
|
||||
} from '@material-ui/core'
|
||||
import AddIcon from '@material-ui/icons/Add'
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
cardPlus: {
|
||||
@ -17,10 +17,10 @@ const useStyles = makeStyles(() => ({
|
||||
display: 'flex',
|
||||
textAlign: 'center'
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
||||
function ListCards({ handleCreate, list, CardComponent, cardsProps }) {
|
||||
const classes = useStyles();
|
||||
function ListCards ({ handleCreate, list, CardComponent, cardsProps }) {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
@ -46,7 +46,7 @@ function ListCards({ handleCreate, list, CardComponent, cardsProps }) {
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
ListCards.propTypes = {
|
||||
@ -58,13 +58,13 @@ ListCards.propTypes = {
|
||||
PropTypes.element
|
||||
]),
|
||||
cardsProps: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
ListCards.defaultProps = {
|
||||
list: [],
|
||||
handleCreate: undefined,
|
||||
CardComponent: null,
|
||||
cardsProps: () => undefined
|
||||
};
|
||||
}
|
||||
|
||||
export default ListCards;
|
||||
export default ListCards
|
||||
|
@ -1,40 +1,40 @@
|
||||
import React, { useRef, useEffect, useCallback, createRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useRef, useEffect, useCallback, createRef } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { debounce, LinearProgress } from '@material-ui/core';
|
||||
import { debounce, LinearProgress } from '@material-ui/core'
|
||||
|
||||
import useNearScreen from 'client/hooks/useNearScreen';
|
||||
import useList from 'client/hooks/useList';
|
||||
import useNearScreen from 'client/hooks/useNearScreen'
|
||||
import useList from 'client/hooks/useList'
|
||||
|
||||
const ListInfiniteScroll = ({ list, renderResult }) => {
|
||||
const gridRef = createRef();
|
||||
const gridRef = createRef()
|
||||
const { loading, shortList, finish, reset, setLength } = useList({
|
||||
list,
|
||||
initLength: 50
|
||||
});
|
||||
})
|
||||
|
||||
const loaderRef = useRef();
|
||||
const loaderRef = useRef()
|
||||
const { isNearScreen } = useNearScreen({
|
||||
distance: '100px',
|
||||
externalRef: loading ? null : loaderRef,
|
||||
once: false
|
||||
});
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
reset(list);
|
||||
gridRef.current.scrollIntoView({ block: 'start' });
|
||||
}, [list]);
|
||||
reset(list)
|
||||
gridRef.current.scrollIntoView({ block: 'start' })
|
||||
}, [list])
|
||||
|
||||
const debounceHandleNextPage = useCallback(
|
||||
debounce(() => {
|
||||
setLength(prevLength => prevLength + 20);
|
||||
setLength(prevLength => prevLength + 20)
|
||||
}, 200),
|
||||
[setLength]
|
||||
);
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (isNearScreen && !finish) debounceHandleNextPage();
|
||||
}, [isNearScreen, finish, debounceHandleNextPage]);
|
||||
if (isNearScreen && !finish) debounceHandleNextPage()
|
||||
}, [isNearScreen, finish, debounceHandleNextPage])
|
||||
|
||||
return (
|
||||
<div style={{ overflowY: 'auto', padding: 10 }}>
|
||||
@ -55,17 +55,17 @@ const ListInfiniteScroll = ({ list, renderResult }) => {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
ListInfiniteScroll.propTypes = {
|
||||
list: PropTypes.arrayOf(PropTypes.any),
|
||||
renderResult: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
ListInfiniteScroll.defaultProps = {
|
||||
list: [],
|
||||
renderResult: () => null
|
||||
};
|
||||
}
|
||||
|
||||
export default ListInfiniteScroll;
|
||||
export default ListInfiniteScroll
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import React from 'react'
|
||||
|
||||
import { Box } from '@material-ui/core';
|
||||
import Logo from 'client/icons/logo';
|
||||
import { Box } from '@material-ui/core'
|
||||
import Logo from 'client/icons/logo'
|
||||
|
||||
const LoadingScreen = () => (
|
||||
<Box
|
||||
@ -18,6 +18,6 @@ const LoadingScreen = () => (
|
||||
>
|
||||
<Logo width={360} height={360} spinner withText />
|
||||
</Box>
|
||||
);
|
||||
)
|
||||
|
||||
export default LoadingScreen;
|
||||
export default LoadingScreen
|
||||
|
@ -1,61 +1,74 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { IconButton } from '@material-ui/core';
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
import { removeSnackbar } from 'client/actions/general';
|
||||
import React, { useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
let displayed = [];
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { IconButton } from '@material-ui/core'
|
||||
import CloseIcon from '@material-ui/icons/Close'
|
||||
|
||||
import { removeSnackbar } from 'client/actions/general'
|
||||
|
||||
const CloseButton = ({ handleClick }) => (
|
||||
<IconButton onClick={handleClick} component="span">
|
||||
<CloseIcon fontSize="small" />
|
||||
</IconButton>
|
||||
)
|
||||
|
||||
let displayed = []
|
||||
|
||||
const Notifier = () => {
|
||||
const dispatch = useDispatch();
|
||||
const notifications = useSelector(store => store.General.notifications || []);
|
||||
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
|
||||
const dispatch = useDispatch()
|
||||
const notifications = useSelector(store => store.General.notifications || [])
|
||||
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
|
||||
|
||||
const storeDisplayed = id => {
|
||||
displayed = [...displayed, id];
|
||||
};
|
||||
displayed = [...displayed, id]
|
||||
}
|
||||
|
||||
const removeDisplayed = id => {
|
||||
displayed = [...displayed.filter(key => id !== key)];
|
||||
};
|
||||
displayed = [...displayed.filter(key => id !== key)]
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
notifications.forEach(
|
||||
({ key, message, options = {}, dismissed = false }) => {
|
||||
if (dismissed) {
|
||||
closeSnackbar(key);
|
||||
return;
|
||||
closeSnackbar(key)
|
||||
return
|
||||
}
|
||||
|
||||
if (displayed.includes(key)) return;
|
||||
if (displayed.includes(key)) return
|
||||
|
||||
enqueueSnackbar(message, {
|
||||
key,
|
||||
...options,
|
||||
action: () => (
|
||||
<IconButton onClick={() => closeSnackbar(key)} component="span">
|
||||
<CloseIcon fontSize="small" />
|
||||
</IconButton>
|
||||
),
|
||||
action: CloseButton({ handleClick: () => closeSnackbar(key) }),
|
||||
onClose: (event, reason, myKey) => {
|
||||
if (options.onClose) {
|
||||
options.onClose(event, reason, myKey);
|
||||
options.onClose(event, reason, myKey)
|
||||
}
|
||||
},
|
||||
onExited: (_, myKey) => {
|
||||
dispatch(removeSnackbar(myKey));
|
||||
removeDisplayed(myKey);
|
||||
dispatch(removeSnackbar(myKey))
|
||||
removeDisplayed(myKey)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// keep track of snackbars that we've displayed
|
||||
storeDisplayed(key);
|
||||
storeDisplayed(key)
|
||||
}
|
||||
);
|
||||
}, [notifications, closeSnackbar, enqueueSnackbar, dispatch]);
|
||||
)
|
||||
}, [notifications, closeSnackbar, enqueueSnackbar, dispatch])
|
||||
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
export default Notifier;
|
||||
CloseButton.propTypes = {
|
||||
handleClick: PropTypes.func
|
||||
}
|
||||
|
||||
CloseButton.defaultProps = {
|
||||
handleClick: undefined
|
||||
}
|
||||
|
||||
export default Notifier
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useState, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import Fuse from 'fuse.js';
|
||||
import { TextField, Box, debounce } from '@material-ui/core';
|
||||
import Fuse from 'fuse.js'
|
||||
import { TextField, Box, debounce } from '@material-ui/core'
|
||||
|
||||
import ListInfiniteScroll from 'client/components/List/ListInfiniteScroll';
|
||||
import ListInfiniteScroll from 'client/components/List/ListInfiniteScroll'
|
||||
|
||||
const Search = ({
|
||||
list,
|
||||
@ -13,28 +13,28 @@ const Search = ({
|
||||
startAdornment,
|
||||
searchBoxProps
|
||||
}) => {
|
||||
const [query, setQuery] = useState('');
|
||||
const [result, setResult] = useState(undefined);
|
||||
const [query, setQuery] = useState('')
|
||||
const [result, setResult] = useState(undefined)
|
||||
const listFuse = useMemo(
|
||||
() => new Fuse(list, listOptions, Fuse.createIndex(listOptions.keys, list)),
|
||||
[list, listOptions]
|
||||
);
|
||||
)
|
||||
|
||||
const debounceResult = React.useCallback(
|
||||
debounce(value => {
|
||||
const search = listFuse.search(value)?.map(({ item }) => item);
|
||||
const search = listFuse.search(value)?.map(({ item }) => item)
|
||||
|
||||
setResult(value ? search : undefined);
|
||||
setResult(value ? search : undefined)
|
||||
}, 1000),
|
||||
[list]
|
||||
);
|
||||
)
|
||||
|
||||
const handleChange = event => {
|
||||
const { value: nextValue } = event?.target;
|
||||
const { value: nextValue } = event?.target
|
||||
|
||||
setQuery(nextValue);
|
||||
debounceResult(nextValue);
|
||||
};
|
||||
setQuery(nextValue)
|
||||
debounceResult(nextValue)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -49,13 +49,13 @@ const Search = ({
|
||||
/>
|
||||
</Box>
|
||||
{result?.length === 0 ? (
|
||||
<h4>{`Your search did not match`}</h4>
|
||||
<h4>{'Your search did not match'}</h4>
|
||||
) : (
|
||||
<ListInfiniteScroll list={result ?? list} renderResult={renderResult} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
Search.propTypes = {
|
||||
list: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
@ -68,7 +68,7 @@ Search.propTypes = {
|
||||
renderResult: PropTypes.func,
|
||||
startAdornment: PropTypes.objectOf(PropTypes.any),
|
||||
searchBoxProps: PropTypes.objectOf(PropTypes.any)
|
||||
};
|
||||
}
|
||||
|
||||
Search.defaultProps = {
|
||||
list: [],
|
||||
@ -76,6 +76,6 @@ Search.defaultProps = {
|
||||
renderResult: item => item,
|
||||
startAdornment: undefined,
|
||||
searchBoxProps: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export default Search;
|
||||
export default Search
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
backdrop: {
|
||||
@ -16,4 +16,4 @@ export default makeStyles(theme => ({
|
||||
header: { display: 'flex', alignItems: 'center' },
|
||||
title: { flexGrow: 1 },
|
||||
button: { justifyContent: 'start' }
|
||||
}));
|
||||
}))
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import clsx from 'clsx';
|
||||
import React, { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import {
|
||||
List,
|
||||
@ -9,21 +9,21 @@ import {
|
||||
ListItemText,
|
||||
ListItemIcon,
|
||||
useMediaQuery
|
||||
} from '@material-ui/core';
|
||||
import ExpandLessIcon from '@material-ui/icons/ExpandLess';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
} from '@material-ui/core'
|
||||
import ExpandLessIcon from '@material-ui/icons/ExpandLess'
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
|
||||
|
||||
import useGeneral from 'client/hooks/useGeneral';
|
||||
import SidebarLink from 'client/components/Sidebar/SidebarLink';
|
||||
import sidebarStyles from 'client/components/Sidebar/styles';
|
||||
import useGeneral from 'client/hooks/useGeneral'
|
||||
import SidebarLink from 'client/components/Sidebar/SidebarLink'
|
||||
import sidebarStyles from 'client/components/Sidebar/styles'
|
||||
|
||||
const SidebarCollapseItem = ({ label, routes, icon: Icon }) => {
|
||||
const classes = sidebarStyles();
|
||||
const { isFixMenu } = useGeneral();
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const isUpLg = useMediaQuery(theme => theme.breakpoints.up('lg'));
|
||||
const classes = sidebarStyles()
|
||||
const { isFixMenu } = useGeneral()
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const isUpLg = useMediaQuery(theme => theme.breakpoints.up('lg'))
|
||||
|
||||
const handleExpand = () => setExpanded(!expanded);
|
||||
const handleExpand = () => setExpanded(!expanded)
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -58,8 +58,8 @@ const SidebarCollapseItem = ({ label, routes, icon: Icon }) => {
|
||||
</Collapse>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
SidebarCollapseItem.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
@ -70,12 +70,12 @@ SidebarCollapseItem.propTypes = {
|
||||
path: PropTypes.string
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
SidebarCollapseItem.defaultProps = {
|
||||
label: '',
|
||||
icon: null,
|
||||
routes: []
|
||||
};
|
||||
}
|
||||
|
||||
export default SidebarCollapseItem;
|
||||
export default SidebarCollapseItem
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useHistory, useLocation } from 'react-router-dom'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import {
|
||||
withStyles,
|
||||
@ -11,10 +11,10 @@ import {
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
useMediaQuery
|
||||
} from '@material-ui/core';
|
||||
} from '@material-ui/core'
|
||||
|
||||
import useGeneral from 'client/hooks/useGeneral';
|
||||
import sidebarStyles from 'client/components/Sidebar/styles';
|
||||
import useGeneral from 'client/hooks/useGeneral'
|
||||
import sidebarStyles from 'client/components/Sidebar/styles'
|
||||
|
||||
const StyledBadge = withStyles(() => ({
|
||||
badge: {
|
||||
@ -22,21 +22,21 @@ const StyledBadge = withStyles(() => ({
|
||||
top: 13,
|
||||
fontSize: '0.7rem'
|
||||
}
|
||||
}))(Badge);
|
||||
}))(Badge)
|
||||
|
||||
const SidebarLink = ({ label, path, icon: Icon, devMode, isSubItem }) => {
|
||||
const classes = sidebarStyles();
|
||||
const history = useHistory();
|
||||
const { pathname } = useLocation();
|
||||
const { fixMenu } = useGeneral();
|
||||
const isUpLg = useMediaQuery(theme => theme.breakpoints.up('lg'));
|
||||
const classes = sidebarStyles()
|
||||
const history = useHistory()
|
||||
const { pathname } = useLocation()
|
||||
const { fixMenu } = useGeneral()
|
||||
const isUpLg = useMediaQuery(theme => theme.breakpoints.up('lg'))
|
||||
|
||||
const handleClick = () => {
|
||||
history.push(path);
|
||||
!isUpLg && fixMenu(false);
|
||||
};
|
||||
history.push(path)
|
||||
!isUpLg && fixMenu(false)
|
||||
}
|
||||
|
||||
const isCurrentPathname = pathname === path;
|
||||
const isCurrentPathname = pathname === path
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
@ -63,8 +63,8 @@ const SidebarLink = ({ label, path, icon: Icon, devMode, isSubItem }) => {
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
SidebarLink.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
@ -79,7 +79,7 @@ SidebarLink.propTypes = {
|
||||
]),
|
||||
devMode: PropTypes.bool,
|
||||
isSubItem: PropTypes.bool
|
||||
};
|
||||
}
|
||||
|
||||
SidebarLink.defaultProps = {
|
||||
label: '',
|
||||
@ -87,6 +87,6 @@ SidebarLink.defaultProps = {
|
||||
icon: undefined,
|
||||
devMode: false,
|
||||
isSubItem: false
|
||||
};
|
||||
}
|
||||
|
||||
export default SidebarLink;
|
||||
export default SidebarLink
|
||||
|
@ -13,8 +13,8 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import React, { useMemo } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import {
|
||||
List,
|
||||
Drawer,
|
||||
@ -22,23 +22,23 @@ import {
|
||||
Box,
|
||||
IconButton,
|
||||
useMediaQuery
|
||||
} from '@material-ui/core';
|
||||
import { Menu as MenuIcon, Close as CloseIcon } from '@material-ui/icons';
|
||||
} from '@material-ui/core'
|
||||
import { Menu as MenuIcon, Close as CloseIcon } from '@material-ui/icons'
|
||||
|
||||
import useGeneral from 'client/hooks/useGeneral';
|
||||
import endpoints from 'client/router/endpoints';
|
||||
import useGeneral from 'client/hooks/useGeneral'
|
||||
import endpoints from 'client/router/endpoints'
|
||||
|
||||
import sidebarStyles from 'client/components/Sidebar/styles';
|
||||
import SidebarLink from 'client/components/Sidebar/SidebarLink';
|
||||
import SidebarCollapseItem from 'client/components/Sidebar/SidebarCollapseItem';
|
||||
import Logo from 'client/icons/logo';
|
||||
import sidebarStyles from 'client/components/Sidebar/styles'
|
||||
import SidebarLink from 'client/components/Sidebar/SidebarLink'
|
||||
import SidebarCollapseItem from 'client/components/Sidebar/SidebarCollapseItem'
|
||||
import Logo from 'client/icons/logo'
|
||||
|
||||
const Sidebar = () => {
|
||||
const classes = sidebarStyles();
|
||||
const { isFixMenu, fixMenu } = useGeneral();
|
||||
const isUpLg = useMediaQuery(theme => theme.breakpoints.up('lg'));
|
||||
const classes = sidebarStyles()
|
||||
const { isFixMenu, fixMenu } = useGeneral()
|
||||
const isUpLg = useMediaQuery(theme => theme.breakpoints.up('lg'))
|
||||
|
||||
const handleSwapMenu = () => fixMenu(!isFixMenu);
|
||||
const handleSwapMenu = () => fixMenu(!isFixMenu)
|
||||
|
||||
const SidebarEndpoints = useMemo(
|
||||
() =>
|
||||
@ -54,7 +54,7 @@ const Sidebar = () => {
|
||||
)
|
||||
),
|
||||
[endpoints]
|
||||
);
|
||||
)
|
||||
|
||||
return useMemo(
|
||||
() => (
|
||||
@ -93,7 +93,7 @@ const Sidebar = () => {
|
||||
</Drawer>
|
||||
),
|
||||
[isFixMenu, fixMenu, isUpLg]
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default Sidebar;
|
||||
export default Sidebar
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { sidebar, toolbar } from 'client/assets/theme/defaults';
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import { sidebar, toolbar } from 'client/assets/theme/defaults'
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
// -------------------------------
|
||||
@ -127,4 +127,4 @@ export default makeStyles(theme => ({
|
||||
backgroundColor: theme.palette.primary.light
|
||||
}
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { SpeedDial, SpeedDialIcon, SpeedDialAction } from '@material-ui/lab';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { SpeedDial, SpeedDialIcon, SpeedDialAction } from '@material-ui/lab'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
@ -16,19 +16,19 @@ const useStyles = makeStyles(theme => ({
|
||||
left: theme.spacing(2)
|
||||
}
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
||||
const SpeedDials = ({ hidden = false, actions = [] }) => {
|
||||
const classes = useStyles();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const classes = useStyles()
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
const handleOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<SpeedDial
|
||||
@ -50,8 +50,8 @@ const SpeedDials = ({ hidden = false, actions = [] }) => {
|
||||
/>
|
||||
))}
|
||||
</SpeedDial>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
SpeedDials.propTypes = {
|
||||
hidden: PropTypes.bool,
|
||||
@ -62,11 +62,11 @@ SpeedDials.propTypes = {
|
||||
handleClick: PropTypes.func
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
SpeedDials.defaultProps = {
|
||||
hidden: false,
|
||||
actions: []
|
||||
};
|
||||
}
|
||||
|
||||
export default SpeedDials;
|
||||
export default SpeedDials
|
||||
|
@ -36,4 +36,4 @@ module.exports = {
|
||||
CHECKBOX: 'checkbox',
|
||||
AUTOCOMPLETE: 'autocomplete'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -10,4 +10,4 @@ module.exports = {
|
||||
SignOut: 'Sign Out',
|
||||
Submit: 'Submit',
|
||||
Response: 'Response'
|
||||
};
|
||||
}
|
||||
|
@ -1,27 +1,26 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import useGeneral from 'client/hooks/useGeneral';
|
||||
import useApplication from 'client/hooks/useApplication';
|
||||
import useApplication from 'client/hooks/useApplication'
|
||||
|
||||
import ListCards from 'client/components/List/ListCards';
|
||||
import { ClusterCard } from 'client/components/Cards';
|
||||
import ListCards from 'client/components/List/ListCards'
|
||||
import { ClusterCard } from 'client/components/Cards'
|
||||
|
||||
const ApplicationsDeployed = () => {
|
||||
const { applications, getApplications } = useApplication();
|
||||
const { applications, getApplications } = useApplication()
|
||||
|
||||
useEffect(() => {
|
||||
getApplications();
|
||||
}, []);
|
||||
getApplications()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<ListCards
|
||||
list={applications}
|
||||
CardComponent={ClusterCard}
|
||||
cardsProps={({ value }) => {
|
||||
console.log(value);
|
||||
console.log(value)
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ApplicationsDeployed;
|
||||
export default ApplicationsDeployed
|
||||
|
@ -1,26 +1,26 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Box, LinearProgress } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { Box, LinearProgress } from '@material-ui/core'
|
||||
import { Alert } from '@material-ui/lab'
|
||||
|
||||
import useApplication from 'client/hooks/useApplication';
|
||||
import useFetch from 'client/hooks/useFetch';
|
||||
import useApplication from 'client/hooks/useApplication'
|
||||
import useFetch from 'client/hooks/useFetch'
|
||||
|
||||
import ListCards from 'client/components/List/ListCards';
|
||||
import { ApplicationTemplateCard } from 'client/components/Cards';
|
||||
import { PATH } from 'client/router/endpoints';
|
||||
import ListCards from 'client/components/List/ListCards'
|
||||
import { ApplicationTemplateCard } from 'client/components/Cards'
|
||||
import { PATH } from 'client/router/endpoints'
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const ApplicationsTemplatesList = () => {
|
||||
const history = useHistory();
|
||||
const { applicationsTemplates, getApplicationsTemplates } = useApplication();
|
||||
const { fetchRequest, loading, error } = useFetch(getApplicationsTemplates);
|
||||
const history = useHistory()
|
||||
const { applicationsTemplates, getApplicationsTemplates } = useApplication()
|
||||
const { fetchRequest, loading, error } = useFetch(getApplicationsTemplates)
|
||||
|
||||
useEffect(() => {
|
||||
fetchRequest();
|
||||
}, []);
|
||||
fetchRequest()
|
||||
}, [])
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
@ -29,28 +29,28 @@ const ApplicationsTemplatesList = () => {
|
||||
{Tr('Cannot connect to OneFlow server')}
|
||||
</Alert>
|
||||
</Box>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <LinearProgress />;
|
||||
return <LinearProgress />
|
||||
}
|
||||
|
||||
return (
|
||||
<Box p={3}>
|
||||
<ListCards
|
||||
list={applicationsTemplates}
|
||||
handleCreate={() => history.push(PATH.APPLICATION_TEMPLATE.CREATE)}
|
||||
handleCreate={() => history.push(PATH.APPLICATIONS_TEMPLATES.CREATE)}
|
||||
CardComponent={ApplicationTemplateCard}
|
||||
cardsProps={({ value: { ID } }) => ({
|
||||
handleEdit: () =>
|
||||
history.push(PATH.APPLICATION_TEMPLATE.EDIT.replace(':id', ID)),
|
||||
history.push(PATH.APPLICATIONS_TEMPLATES.EDIT.replace(':id', ID)),
|
||||
handleDeploy: undefined,
|
||||
handleRemove: undefined
|
||||
})}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default ApplicationsTemplatesList;
|
||||
export default ApplicationsTemplatesList
|
||||
|
@ -1,73 +0,0 @@
|
||||
/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
||||
/* not use this file except in compliance with the License. You may obtain */
|
||||
/* a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Tab, Tabs, Paper, Container } from '@material-ui/core';
|
||||
|
||||
import ApplicationsTemplatesList from './Templates';
|
||||
import ApplicationsDeployed from './Deployed';
|
||||
|
||||
const TABS = {
|
||||
TEMPLATES: 'templates',
|
||||
APPLICATIONS: 'applications'
|
||||
};
|
||||
|
||||
function Applications() {
|
||||
const [value, setValue] = useState(TABS.TEMPLATES);
|
||||
|
||||
const handleChange = (_, newValue) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<Container
|
||||
disableGutters
|
||||
style={{ height: '100%', display: 'flex', flexDirection: 'column' }}
|
||||
>
|
||||
<Paper>
|
||||
<Tabs
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
>
|
||||
<Tab
|
||||
value={TABS.TEMPLATES}
|
||||
label="Applications templates"
|
||||
id={`tab-${TABS.TEMPLATES}`}
|
||||
/>
|
||||
<Tab
|
||||
value={TABS.APPLICATIONS}
|
||||
label="Applications deployed"
|
||||
id={`tab-${TABS.APPLICATIONS}`}
|
||||
/>
|
||||
</Tabs>
|
||||
</Paper>
|
||||
<div hidden={value !== TABS.TEMPLATES}>
|
||||
{value === TABS.TEMPLATES && <ApplicationsTemplatesList />}
|
||||
</div>
|
||||
<div hidden={value !== TABS.APPLICATIONS}>
|
||||
{value === TABS.APPLICATIONS && <ApplicationsDeployed />}
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
Applications.propTypes = {};
|
||||
|
||||
Applications.defaultProps = {};
|
||||
|
||||
export default Applications;
|
@ -1,27 +0,0 @@
|
||||
/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
||||
/* not use this file except in compliance with the License. You may obtain */
|
||||
/* a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
function ApplicationManage() {
|
||||
return <div>Manage</div>;
|
||||
}
|
||||
|
||||
ApplicationManage.propTypes = {};
|
||||
|
||||
ApplicationManage.defaultProps = {};
|
||||
|
||||
export default ApplicationManage;
|
@ -1,4 +1,73 @@
|
||||
import ApplicationsList from 'client/containers/Applications/List';
|
||||
import ApplicationsManage from 'client/containers/Applications/Manage';
|
||||
/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
||||
/* not use this file except in compliance with the License. You may obtain */
|
||||
/* a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
export { ApplicationsList, ApplicationsManage };
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import { Tab, Tabs, Paper, Container } from '@material-ui/core'
|
||||
|
||||
import ApplicationsTemplatesList from 'client/containers/Applications/List/Templates'
|
||||
import ApplicationsDeployed from 'client/containers/Applications/List/Deployed'
|
||||
|
||||
const TABS = {
|
||||
TEMPLATES: 'templates',
|
||||
APPLICATIONS: 'applications'
|
||||
}
|
||||
|
||||
function Applications () {
|
||||
const [value, setValue] = useState(TABS.TEMPLATES)
|
||||
|
||||
const handleChange = (_, newValue) => {
|
||||
setValue(newValue)
|
||||
}
|
||||
|
||||
return (
|
||||
<Container
|
||||
disableGutters
|
||||
style={{ height: '100%', display: 'flex', flexDirection: 'column' }}
|
||||
>
|
||||
<Paper>
|
||||
<Tabs
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
>
|
||||
<Tab
|
||||
value={TABS.TEMPLATES}
|
||||
label="Applications templates"
|
||||
id={`tab-${TABS.TEMPLATES}`}
|
||||
/>
|
||||
<Tab
|
||||
value={TABS.APPLICATIONS}
|
||||
label="Applications deployed"
|
||||
id={`tab-${TABS.APPLICATIONS}`}
|
||||
/>
|
||||
</Tabs>
|
||||
</Paper>
|
||||
<div hidden={value !== TABS.TEMPLATES}>
|
||||
{value === TABS.TEMPLATES && <ApplicationsTemplatesList />}
|
||||
</div>
|
||||
<div hidden={value !== TABS.APPLICATIONS}>
|
||||
{value === TABS.APPLICATIONS && <ApplicationsDeployed />}
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
Applications.propTypes = {}
|
||||
|
||||
Applications.defaultProps = {}
|
||||
|
||||
export default Applications
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema';
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
import { FORM_FIELDS, STEP_FORM_SCHEMA } from './schema';
|
||||
import { FORM_FIELDS, STEP_FORM_SCHEMA } from './schema'
|
||||
|
||||
export const STEP_ID = 'application';
|
||||
export const STEP_ID = 'application'
|
||||
|
||||
const BasicConfiguration = () => ({
|
||||
id: STEP_ID,
|
||||
@ -14,6 +14,6 @@ const BasicConfiguration = () => ({
|
||||
() => <FormWithSchema cy="form-flow" fields={FORM_FIELDS} id={STEP_ID} />,
|
||||
[]
|
||||
)
|
||||
});
|
||||
})
|
||||
|
||||
export default BasicConfiguration;
|
||||
export default BasicConfiguration
|
||||
|
@ -1,17 +1,17 @@
|
||||
import * as yup from 'yup';
|
||||
import { TYPE_INPUT } from 'client/constants';
|
||||
import { getValidationFromFields } from 'client/utils/helpers';
|
||||
import * as yup from 'yup'
|
||||
import { TYPE_INPUT } from 'client/constants'
|
||||
import { getValidationFromFields } from 'client/utils/helpers'
|
||||
|
||||
const STRATEGIES_DEPLOY = [
|
||||
{ text: 'None', value: 'none' },
|
||||
{ text: 'Straight', value: 'straight' }
|
||||
];
|
||||
]
|
||||
|
||||
const SHUTDOWN_ACTIONS = [
|
||||
{ text: 'None', value: 'none' },
|
||||
{ text: 'Terminate', value: 'terminate' },
|
||||
{ text: 'Terminate hard', value: 'terminate-hard' }
|
||||
];
|
||||
]
|
||||
|
||||
export const FORM_FIELDS = [
|
||||
{
|
||||
@ -66,8 +66,8 @@ export const FORM_FIELDS = [
|
||||
type: TYPE_INPUT.CHECKBOX,
|
||||
validation: yup.boolean().default(false)
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
export const STEP_FORM_SCHEMA = yup.object(
|
||||
getValidationFromFields(FORM_FIELDS)
|
||||
);
|
||||
)
|
||||
|
@ -1,46 +1,46 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import React, { useEffect, useCallback } from 'react'
|
||||
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
import useOpennebula from 'client/hooks/useOpennebula'
|
||||
|
||||
import useListForm from 'client/hooks/useListForm';
|
||||
import ListCards from 'client/components/List/ListCards';
|
||||
import { ClusterCard } from 'client/components/Cards';
|
||||
import useListForm from 'client/hooks/useListForm'
|
||||
import ListCards from 'client/components/List/ListCards'
|
||||
import { ClusterCard } from 'client/components/Cards'
|
||||
|
||||
import { STEP_FORM_SCHEMA } from './schema';
|
||||
import { STEP_FORM_SCHEMA } from './schema'
|
||||
|
||||
export const STEP_ID = 'clusters';
|
||||
export const STEP_ID = 'clusters'
|
||||
|
||||
const Clusters = () => ({
|
||||
id: STEP_ID,
|
||||
label: 'Where will it run?',
|
||||
resolver: STEP_FORM_SCHEMA,
|
||||
content: useCallback(({ data, setFormData }) => {
|
||||
const { clusters, getClusters } = useOpennebula();
|
||||
const { clusters, getClusters } = useOpennebula()
|
||||
const { handleSelect, handleUnselect } = useListForm({
|
||||
key: STEP_ID,
|
||||
setList: setFormData
|
||||
});
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
getClusters();
|
||||
}, []);
|
||||
getClusters()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<ListCards
|
||||
list={clusters}
|
||||
CardComponent={ClusterCard}
|
||||
cardsProps={({ value }) => {
|
||||
const { ID } = value;
|
||||
const { ID } = value
|
||||
|
||||
return {
|
||||
isSelected: data?.some(selected => selected === ID),
|
||||
handleSelect: () => handleSelect(ID),
|
||||
handleUnselect: () => handleUnselect(ID)
|
||||
};
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}, [])
|
||||
});
|
||||
})
|
||||
|
||||
export default Clusters;
|
||||
export default Clusters
|
||||
|
@ -1,8 +1,8 @@
|
||||
import * as yup from 'yup';
|
||||
import * as yup from 'yup'
|
||||
|
||||
export const STEP_FORM_SCHEMA = yup
|
||||
.array(yup.string().trim())
|
||||
.min(1, 'Select cluster')
|
||||
.max(1, 'Max. one cluster selected')
|
||||
.required('Cluster field is required')
|
||||
.default([]);
|
||||
.default([])
|
||||
|
@ -1,27 +1,27 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useCallback } from 'react'
|
||||
|
||||
import { useWatch } from 'react-hook-form';
|
||||
import { useWatch } from 'react-hook-form'
|
||||
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
import useListForm from 'client/hooks/useListForm';
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema';
|
||||
import ListCards from 'client/components/List/ListCards';
|
||||
import { DialogForm } from 'client/components/Dialogs';
|
||||
import { NetworkCard } from 'client/components/Cards';
|
||||
import useOpennebula from 'client/hooks/useOpennebula'
|
||||
import useListForm from 'client/hooks/useListForm'
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
import ListCards from 'client/components/List/ListCards'
|
||||
import { DialogForm } from 'client/components/Dialogs'
|
||||
import { NetworkCard } from 'client/components/Cards'
|
||||
|
||||
import { STEP_ID as TIERS_ID } from 'client/containers/ApplicationsTemplates/Create/Steps/Tiers';
|
||||
import { FORM_FIELDS, NETWORK_FORM_SCHEMA, STEP_FORM_SCHEMA } from './schema';
|
||||
import { STEP_ID as TIERS_ID } from 'client/containers/ApplicationsTemplates/Create/Steps/Tiers'
|
||||
import { FORM_FIELDS, NETWORK_FORM_SCHEMA, STEP_FORM_SCHEMA } from './schema'
|
||||
|
||||
export const STEP_ID = 'networking';
|
||||
export const STEP_ID = 'networking'
|
||||
|
||||
const Networks = () => ({
|
||||
id: STEP_ID,
|
||||
label: 'Configure Networking',
|
||||
resolver: STEP_FORM_SCHEMA,
|
||||
content: useCallback(({ data, setFormData }) => {
|
||||
const form = useWatch({});
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
const { getVNetworks, getVNetworksTemplates } = useOpennebula();
|
||||
const form = useWatch({})
|
||||
const [showDialog, setShowDialog] = useState(false)
|
||||
const { getVNetworks, getVNetworksTemplates } = useOpennebula()
|
||||
const {
|
||||
editingData,
|
||||
handleSave,
|
||||
@ -33,12 +33,12 @@ const Networks = () => ({
|
||||
list: data,
|
||||
setList: setFormData,
|
||||
defaultValue: NETWORK_FORM_SCHEMA.default()
|
||||
});
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
getVNetworks();
|
||||
getVNetworksTemplates();
|
||||
}, []);
|
||||
getVNetworks()
|
||||
getVNetworksTemplates()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -46,22 +46,22 @@ const Networks = () => ({
|
||||
list={data}
|
||||
CardComponent={NetworkCard}
|
||||
handleCreate={() => {
|
||||
handleEdit();
|
||||
setShowDialog(true);
|
||||
handleEdit()
|
||||
setShowDialog(true)
|
||||
}}
|
||||
cardsProps={({ value: { id } }) => {
|
||||
const isUsed = form[TIERS_ID].some(({ networks }) =>
|
||||
networks?.includes(id)
|
||||
);
|
||||
)
|
||||
|
||||
return {
|
||||
handleEdit: () => {
|
||||
handleEdit(id);
|
||||
setShowDialog(true);
|
||||
handleEdit(id)
|
||||
setShowDialog(true)
|
||||
},
|
||||
handleClone: () => handleClone(id),
|
||||
handleRemove: !isUsed ? () => handleRemove(id) : undefined
|
||||
};
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{showDialog && (
|
||||
@ -71,8 +71,8 @@ const Networks = () => ({
|
||||
open={showDialog}
|
||||
values={editingData}
|
||||
onSubmit={values => {
|
||||
handleSave(values);
|
||||
setShowDialog(false);
|
||||
handleSave(values)
|
||||
setShowDialog(false)
|
||||
}}
|
||||
onCancel={() => setShowDialog(false)}
|
||||
>
|
||||
@ -80,8 +80,8 @@ const Networks = () => ({
|
||||
</DialogForm>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
)
|
||||
}, [])
|
||||
});
|
||||
})
|
||||
|
||||
export default Networks;
|
||||
export default Networks
|
||||
|
@ -1,19 +1,19 @@
|
||||
import * as yup from 'yup';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { TYPE_INPUT } from 'client/constants';
|
||||
import { getValidationFromFields } from 'client/utils/helpers';
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
import * as yup from 'yup'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { TYPE_INPUT } from 'client/constants'
|
||||
import { getValidationFromFields } from 'client/utils/helpers'
|
||||
import useOpennebula from 'client/hooks/useOpennebula'
|
||||
|
||||
const SELECT = {
|
||||
template: 'template',
|
||||
network: 'network'
|
||||
};
|
||||
}
|
||||
|
||||
const TYPES_NETWORKS = [
|
||||
{ text: 'Create', value: 'template_id', select: SELECT.template },
|
||||
{ text: 'Reserve', value: 'reserve_from', select: SELECT.network },
|
||||
{ text: 'Existing', value: 'id', select: SELECT.network }
|
||||
];
|
||||
]
|
||||
|
||||
const ID = {
|
||||
name: 'id',
|
||||
@ -26,7 +26,7 @@ const ID = {
|
||||
.required()
|
||||
.default(uuidv4),
|
||||
grid: { style: { display: 'none' } }
|
||||
};
|
||||
}
|
||||
|
||||
const MANDATORY = {
|
||||
name: 'mandatory',
|
||||
@ -37,7 +37,7 @@ const MANDATORY = {
|
||||
.required('Mandatory field is required')
|
||||
.default(false),
|
||||
grid: { md: 12 }
|
||||
};
|
||||
}
|
||||
|
||||
const NAME = {
|
||||
name: 'name',
|
||||
@ -49,7 +49,7 @@ const NAME = {
|
||||
.matches(/^[\w+\s*]+$/g, { message: 'Invalid characters' })
|
||||
.required('Name field is required')
|
||||
.default('')
|
||||
};
|
||||
}
|
||||
|
||||
const DESCRIPTION = {
|
||||
name: 'description',
|
||||
@ -60,7 +60,7 @@ const DESCRIPTION = {
|
||||
.string()
|
||||
.trim()
|
||||
.default('')
|
||||
};
|
||||
}
|
||||
|
||||
const TYPE = {
|
||||
name: 'type',
|
||||
@ -72,23 +72,23 @@ const TYPE = {
|
||||
.oneOf(TYPES_NETWORKS.map(({ value }) => value))
|
||||
.required('Type field is required')
|
||||
.default(TYPES_NETWORKS[0].value)
|
||||
};
|
||||
}
|
||||
|
||||
const ID_VNET = {
|
||||
name: 'idVnet',
|
||||
label: `Select a network`,
|
||||
label: 'Select a network',
|
||||
type: TYPE_INPUT.AUTOCOMPLETE,
|
||||
dependOf: TYPE.name,
|
||||
values: dependValue => {
|
||||
const { vNetworks, vNetworksTemplates } = useOpennebula();
|
||||
const type = TYPES_NETWORKS.find(({ value }) => value === dependValue);
|
||||
const { vNetworks, vNetworksTemplates } = useOpennebula()
|
||||
const type = TYPES_NETWORKS.find(({ value }) => value === dependValue)
|
||||
|
||||
const values =
|
||||
type?.select === SELECT.network ? vNetworks : vNetworksTemplates;
|
||||
type?.select === SELECT.network ? vNetworks : vNetworksTemplates
|
||||
|
||||
return values
|
||||
.map(({ ID: value, NAME: text }) => ({ text, value }))
|
||||
.sort((a, b) => a.value - b.value);
|
||||
.sort((a, b) => a.value - b.value)
|
||||
},
|
||||
validation: yup
|
||||
.string()
|
||||
@ -101,7 +101,7 @@ const ID_VNET = {
|
||||
: schema.required('Network template field is required')
|
||||
)
|
||||
.default(undefined)
|
||||
};
|
||||
}
|
||||
|
||||
const EXTRA = {
|
||||
name: 'extra',
|
||||
@ -112,7 +112,7 @@ const EXTRA = {
|
||||
.string()
|
||||
.trim()
|
||||
.default('')
|
||||
};
|
||||
}
|
||||
|
||||
export const FORM_FIELDS = [
|
||||
ID,
|
||||
@ -122,13 +122,13 @@ export const FORM_FIELDS = [
|
||||
TYPE,
|
||||
ID_VNET,
|
||||
EXTRA
|
||||
];
|
||||
]
|
||||
|
||||
export const NETWORK_FORM_SCHEMA = yup.object(
|
||||
getValidationFromFields(FORM_FIELDS)
|
||||
);
|
||||
)
|
||||
|
||||
export const STEP_FORM_SCHEMA = yup
|
||||
.array()
|
||||
.of(NETWORK_FORM_SCHEMA)
|
||||
.default([]);
|
||||
.default([])
|
||||
|
@ -1,72 +1,72 @@
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { memo, useCallback } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
Handle,
|
||||
useStoreState,
|
||||
getOutgoers,
|
||||
addEdge
|
||||
} from 'react-flow-renderer';
|
||||
} from 'react-flow-renderer'
|
||||
|
||||
import { TierCard } from 'client/components/Cards';
|
||||
import { TierCard } from 'client/components/Cards'
|
||||
|
||||
const CustomNode = memo(({ data, selected, ...nodeProps }) => {
|
||||
const { tier, handleEdit } = data;
|
||||
const elements = useStoreState(state => state.elements);
|
||||
const nodes = useStoreState(state => state.nodes);
|
||||
const { tier, handleEdit } = data
|
||||
const elements = useStoreState(state => state.elements)
|
||||
const nodes = useStoreState(state => state.nodes)
|
||||
|
||||
const detectCycleUtil = useCallback(
|
||||
(node, elementsTemp, visited, recStack) => {
|
||||
const { id: nodeId } = node.data;
|
||||
const { id: nodeId } = node.data
|
||||
|
||||
if (!visited[nodeId]) {
|
||||
visited[nodeId] = true;
|
||||
recStack[nodeId] = true;
|
||||
visited[nodeId] = true
|
||||
recStack[nodeId] = true
|
||||
|
||||
const children = getOutgoers(node, elementsTemp);
|
||||
const children = getOutgoers(node, elementsTemp)
|
||||
for (let index = 0; index < children.length; index += 1) {
|
||||
const child = children[index];
|
||||
const { id: childId } = child.data;
|
||||
const child = children[index]
|
||||
const { id: childId } = child.data
|
||||
if (
|
||||
!visited[childId] &&
|
||||
detectCycleUtil(child, elementsTemp, visited, recStack)
|
||||
) {
|
||||
return true;
|
||||
return true
|
||||
} else if (recStack[childId]) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recStack[nodeId] = false;
|
||||
return false;
|
||||
recStack[nodeId] = false
|
||||
return false
|
||||
},
|
||||
[]
|
||||
);
|
||||
)
|
||||
|
||||
const detectCycle = useCallback(
|
||||
params => {
|
||||
const elementsTemp = addEdge(params, elements);
|
||||
const visited = {};
|
||||
const recStack = {};
|
||||
const elementsTemp = addEdge(params, elements)
|
||||
const visited = {}
|
||||
const recStack = {}
|
||||
|
||||
for (let index = 0; index < nodes.length; index += 1) {
|
||||
const node = nodes[index];
|
||||
const node = nodes[index]
|
||||
if (detectCycleUtil(node, elementsTemp, visited, recStack)) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true
|
||||
},
|
||||
[nodes, elements, detectCycleUtil]
|
||||
);
|
||||
)
|
||||
|
||||
const isValidConnection = useCallback(
|
||||
({ source, target }) =>
|
||||
source !== target ? detectCycle({ source, target }) : false,
|
||||
[detectCycle]
|
||||
);
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -89,17 +89,19 @@ const CustomNode = memo(({ data, selected, ...nodeProps }) => {
|
||||
isValidConnection={isValidConnection}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
CustomNode.propTypes = {
|
||||
data: PropTypes.objectOf(PropTypes.any),
|
||||
selected: PropTypes.bool
|
||||
};
|
||||
}
|
||||
|
||||
CustomNode.defaultProps = {
|
||||
data: {},
|
||||
selected: false
|
||||
};
|
||||
}
|
||||
|
||||
export default CustomNode;
|
||||
CustomNode.displayName = 'CustomFlowNode'
|
||||
|
||||
export default CustomNode
|
||||
|
@ -1,18 +1,18 @@
|
||||
import React, { memo, useEffect, useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { memo, useEffect, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Box } from '@material-ui/core';
|
||||
import { Add as AddIcon, SelectAll as SelectAllIcon } from '@material-ui/icons';
|
||||
import ReactFlow, { Background } from 'react-flow-renderer';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import { Add as AddIcon, SelectAll as SelectAllIcon } from '@material-ui/icons'
|
||||
import ReactFlow, { Background } from 'react-flow-renderer'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
|
||||
import SpeedDials from 'client/components/SpeedDials';
|
||||
import { STEP_ID as TIER_ID } from 'client/containers/ApplicationsTemplates/Create/Steps/Tiers';
|
||||
import SpeedDials from 'client/components/SpeedDials'
|
||||
import { STEP_ID as TIER_ID } from 'client/containers/ApplicationsTemplates/Create/Steps/Tiers'
|
||||
|
||||
import CustomNode from './CustomNode';
|
||||
import useFlowGraph from './useFlowGraph';
|
||||
import CustomNode from './CustomNode'
|
||||
import useFlowGraph from './useFlowGraph'
|
||||
|
||||
const NOT_KEY_CODE = -1;
|
||||
const NOT_KEY_CODE = -1
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
root: {
|
||||
@ -26,11 +26,11 @@ const useStyles = makeStyles(() => ({
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
||||
const Flow = memo(({ dataFields, handleCreate, handleEdit, handleSetData }) => {
|
||||
const { watch } = useFormContext();
|
||||
const classes = useStyles();
|
||||
const { watch } = useFormContext()
|
||||
const classes = useStyles()
|
||||
const {
|
||||
flow,
|
||||
handleRefreshFlow,
|
||||
@ -38,13 +38,13 @@ const Flow = memo(({ dataFields, handleCreate, handleEdit, handleSetData }) => {
|
||||
handleConnect,
|
||||
handleUpdatePosition,
|
||||
handleSelectAll
|
||||
} = useFlowGraph({ nodeFields: dataFields, setList: handleSetData });
|
||||
} = useFlowGraph({ nodeFields: dataFields, setList: handleSetData })
|
||||
|
||||
useEffect(() => {
|
||||
handleRefreshFlow(watch(TIER_ID), ({ id }) => ({
|
||||
handleEdit: () => handleEdit(id)
|
||||
}));
|
||||
}, [watch]);
|
||||
}))
|
||||
}, [watch])
|
||||
|
||||
const actions = useMemo(
|
||||
() => [
|
||||
@ -60,7 +60,7 @@ const Flow = memo(({ dataFields, handleCreate, handleEdit, handleSetData }) => {
|
||||
}
|
||||
],
|
||||
[handleCreate, handleSelectAll]
|
||||
);
|
||||
)
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
@ -75,21 +75,21 @@ const Flow = memo(({ dataFields, handleCreate, handleEdit, handleSetData }) => {
|
||||
<SpeedDials actions={actions} />
|
||||
<Background color="#aaa" gap={16} />
|
||||
</ReactFlow>
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
Flow.propTypes = {
|
||||
dataFields: PropTypes.arrayOf(PropTypes.string),
|
||||
handleCreate: PropTypes.func,
|
||||
handleEdit: PropTypes.func,
|
||||
handleSetData: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
Flow.defaultProps = {
|
||||
dataFields: [],
|
||||
handleCreate: undefined,
|
||||
handleEdit: undefined,
|
||||
handleSetData: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export default Flow;
|
||||
export default Flow
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
import {
|
||||
isNode,
|
||||
@ -6,19 +6,19 @@ import {
|
||||
addEdge,
|
||||
removeElements,
|
||||
useStoreActions
|
||||
} from 'react-flow-renderer';
|
||||
} from 'react-flow-renderer'
|
||||
|
||||
const useFlowGraph = ({ nodeFields, setList }) => {
|
||||
const [flow, setFlow] = useState([]);
|
||||
const [flow, setFlow] = useState([])
|
||||
const setSelectedElements = useStoreActions(
|
||||
actions => actions.setSelectedElements
|
||||
);
|
||||
)
|
||||
|
||||
const getParents = (currentFlow, childId) =>
|
||||
currentFlow
|
||||
.filter(isEdge)
|
||||
.filter(({ target }) => childId === target)
|
||||
.map(({ source }) => source);
|
||||
.map(({ source }) => source)
|
||||
|
||||
const getList = currentFlow =>
|
||||
currentFlow?.filter(isNode)?.map(({ data: nodeData, position }) =>
|
||||
@ -33,7 +33,7 @@ const useFlowGraph = ({ nodeFields, setList }) => {
|
||||
}),
|
||||
{}
|
||||
)
|
||||
);
|
||||
)
|
||||
|
||||
const handleRefreshFlow = useCallback((data, extraItemProps) => {
|
||||
setFlow(
|
||||
@ -55,35 +55,35 @@ const useFlowGraph = ({ nodeFields, setList }) => {
|
||||
],
|
||||
[]
|
||||
)
|
||||
);
|
||||
}, []);
|
||||
)
|
||||
}, [])
|
||||
|
||||
const updateList = newFlow => {
|
||||
const list = getList(newFlow);
|
||||
setList(list);
|
||||
};
|
||||
const list = getList(newFlow)
|
||||
setList(list)
|
||||
}
|
||||
|
||||
const handleRemoveElements = elements => {
|
||||
const newFlow = removeElements(elements, flow);
|
||||
updateList(newFlow);
|
||||
};
|
||||
const newFlow = removeElements(elements, flow)
|
||||
updateList(newFlow)
|
||||
}
|
||||
|
||||
const handleConnect = params => {
|
||||
const newFlow = addEdge({ ...params, animated: true }, flow);
|
||||
updateList(newFlow);
|
||||
};
|
||||
const newFlow = addEdge({ ...params, animated: true }, flow)
|
||||
updateList(newFlow)
|
||||
}
|
||||
|
||||
const handleUpdatePosition = (_, node) => {
|
||||
const newFlow = flow.map(element =>
|
||||
element.id === node.id ? node : element
|
||||
);
|
||||
updateList(newFlow);
|
||||
};
|
||||
)
|
||||
updateList(newFlow)
|
||||
}
|
||||
|
||||
const handleSelectAll = useCallback(() => {
|
||||
const nodes = flow.filter(isNode);
|
||||
setSelectedElements(nodes.map(({ id, type }) => ({ id, type })));
|
||||
}, [flow]);
|
||||
const nodes = flow.filter(isNode)
|
||||
setSelectedElements(nodes.map(({ id, type }) => ({ id, type })))
|
||||
}, [flow])
|
||||
|
||||
return {
|
||||
flow,
|
||||
@ -92,7 +92,7 @@ const useFlowGraph = ({ nodeFields, setList }) => {
|
||||
handleConnect,
|
||||
handleUpdatePosition,
|
||||
handleSelectAll
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default useFlowGraph;
|
||||
export default useFlowGraph
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema';
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
import { FORM_FIELDS, STEP_FORM_SCHEMA } from './schema';
|
||||
import { FORM_FIELDS, STEP_FORM_SCHEMA } from './schema'
|
||||
|
||||
export const STEP_ID = 'tier';
|
||||
export const STEP_ID = 'tier'
|
||||
|
||||
const BasicConfiguration = () => ({
|
||||
id: STEP_ID,
|
||||
@ -14,6 +14,6 @@ const BasicConfiguration = () => ({
|
||||
() => <FormWithSchema cy="form-tier" fields={FORM_FIELDS} id={STEP_ID} />,
|
||||
[]
|
||||
)
|
||||
});
|
||||
})
|
||||
|
||||
export default BasicConfiguration;
|
||||
export default BasicConfiguration
|
||||
|
@ -1,12 +1,12 @@
|
||||
import * as yup from 'yup';
|
||||
import { TYPE_INPUT } from 'client/constants';
|
||||
import { getValidationFromFields } from 'client/utils/helpers';
|
||||
import * as yup from 'yup'
|
||||
import { TYPE_INPUT } from 'client/constants'
|
||||
import { getValidationFromFields } from 'client/utils/helpers'
|
||||
|
||||
const SHUTDOWN_ACTIONS = [
|
||||
{ text: 'None', value: 'none' },
|
||||
{ text: 'Shutdown', value: 'shutdown' },
|
||||
{ text: 'Shutdown hard', value: 'shutdown-hard' }
|
||||
];
|
||||
]
|
||||
|
||||
export const FORM_FIELDS = [
|
||||
{
|
||||
@ -41,8 +41,8 @@ export const FORM_FIELDS = [
|
||||
.oneOf(SHUTDOWN_ACTIONS.map(({ value }) => value))
|
||||
.default(SHUTDOWN_ACTIONS[0].value)
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
export const STEP_FORM_SCHEMA = yup.object(
|
||||
getValidationFromFields(FORM_FIELDS)
|
||||
);
|
||||
)
|
||||
|
@ -1,41 +1,43 @@
|
||||
import React, { useCallback, useContext } from 'react';
|
||||
import React, { useCallback, useContext } from 'react'
|
||||
|
||||
import useListForm from 'client/hooks/useListForm';
|
||||
import ListCards from 'client/components/List/ListCards';
|
||||
import { SelectCard } from 'client/components/Cards';
|
||||
import useListForm from 'client/hooks/useListForm'
|
||||
import ListCards from 'client/components/List/ListCards'
|
||||
import { SelectCard } from 'client/components/Cards'
|
||||
|
||||
import { STEP_ID as NETWORKING } from 'client/containers/ApplicationsTemplates/Create/Steps/Networking';
|
||||
import { Context } from 'client/containers/ApplicationsTemplates/Create/Steps/Tiers';
|
||||
import { STEP_FORM_SCHEMA } from './schema';
|
||||
import { STEP_ID as NETWORKING } from 'client/containers/ApplicationsTemplates/Create/Steps/Networking'
|
||||
import { Context } from 'client/containers/ApplicationsTemplates/Create/Steps/Tiers'
|
||||
import { STEP_FORM_SCHEMA } from './schema'
|
||||
|
||||
export const STEP_ID = 'networks';
|
||||
export const STEP_ID = 'networks'
|
||||
|
||||
const Networks = () => ({
|
||||
id: STEP_ID,
|
||||
label: 'Networks',
|
||||
resolver: STEP_FORM_SCHEMA,
|
||||
content: useCallback(({ data, setFormData }) => {
|
||||
const { nestedForm: list } = useContext(Context);
|
||||
const { nestedForm: list } = useContext(Context)
|
||||
const { handleSelect, handleUnselect } = useListForm({
|
||||
key: STEP_ID,
|
||||
multiple: true,
|
||||
setList: setFormData
|
||||
});
|
||||
})
|
||||
|
||||
return (
|
||||
<ListCards
|
||||
list={list[NETWORKING]}
|
||||
CardComponent={SelectCard}
|
||||
cardsProps={({ value: { id, name } }) => ({
|
||||
ID: id,
|
||||
NAME: name,
|
||||
isSelected: data?.some(selected => selected === id),
|
||||
handleSelect: () => handleSelect(id),
|
||||
handleUnselect: () => handleUnselect(id)
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}, [])
|
||||
});
|
||||
cardsProps={({ value: { id, name } }) => {
|
||||
const isSelected = data?.some(selected => selected === id)
|
||||
|
||||
export default Networks;
|
||||
return {
|
||||
title: name,
|
||||
isSelected,
|
||||
handleClick: () => isSelected ? handleSelect(id) : handleUnselect(id)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}, [])
|
||||
})
|
||||
|
||||
export default Networks
|
||||
|
@ -1,3 +1,3 @@
|
||||
import * as yup from 'yup';
|
||||
import * as yup from 'yup'
|
||||
|
||||
export const STEP_FORM_SCHEMA = yup.array(yup.string().trim()).default([]);
|
||||
export const STEP_FORM_SCHEMA = yup.array(yup.string().trim()).default([])
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema';
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
import { FORM_FIELDS, STEP_FORM_SCHEMA } from './schema';
|
||||
import { FORM_FIELDS, STEP_FORM_SCHEMA } from './schema'
|
||||
|
||||
export const STEP_ID = 'policies';
|
||||
export const STEP_ID = 'policies'
|
||||
|
||||
const Policies = () => ({
|
||||
id: STEP_ID,
|
||||
@ -16,6 +16,6 @@ const Policies = () => ({
|
||||
),
|
||||
[]
|
||||
)
|
||||
});
|
||||
})
|
||||
|
||||
export default Policies;
|
||||
export default Policies
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as yup from 'yup';
|
||||
import { TYPE_INPUT } from 'client/constants';
|
||||
import { getValidationFromFields } from 'client/utils/helpers';
|
||||
import * as yup from 'yup'
|
||||
import { TYPE_INPUT } from 'client/constants'
|
||||
import { getValidationFromFields } from 'client/utils/helpers'
|
||||
|
||||
export const FORM_FIELDS = [
|
||||
{
|
||||
@ -21,8 +21,8 @@ export const FORM_FIELDS = [
|
||||
.trim()
|
||||
.default('')
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
export const STEP_FORM_SCHEMA = yup.object(
|
||||
getValidationFromFields(FORM_FIELDS)
|
||||
);
|
||||
)
|
||||
|
@ -1,14 +1,14 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import AceEditor from 'react-ace';
|
||||
import 'ace-builds/src-noconflict/mode-dockerfile';
|
||||
import 'ace-builds/src-noconflict/theme-github';
|
||||
import AceEditor from 'react-ace'
|
||||
import 'ace-builds/src-noconflict/mode-dockerfile'
|
||||
import 'ace-builds/src-noconflict/theme-github'
|
||||
|
||||
const DockerFile = ({ backButton, handleSetData, currentValue, ...props }) => {
|
||||
const handleChange = newValue => {
|
||||
handleSetData(newValue);
|
||||
};
|
||||
handleSetData(newValue)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -37,19 +37,19 @@ const DockerFile = ({ backButton, handleSetData, currentValue, ...props }) => {
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
DockerFile.propTypes = {
|
||||
backButton: PropTypes.node,
|
||||
currentValue: PropTypes.string,
|
||||
handleSetData: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
DockerFile.defaultProps = {
|
||||
backButton: null,
|
||||
currentValue: undefined,
|
||||
handleSetData: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
export default DockerFile;
|
||||
export default DockerFile
|
||||
|
@ -1,38 +1,34 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
import Search from 'client/components/Search';
|
||||
import { SelectCard } from 'client/components/Cards';
|
||||
import useOpennebula from 'client/hooks/useOpennebula'
|
||||
import Search from 'client/components/Search'
|
||||
import { SelectCard } from 'client/components/Cards'
|
||||
|
||||
const sortByID = (a, b) => a.ID - b.ID;
|
||||
const sortByID = (a, b) => a.ID - b.ID
|
||||
|
||||
const ListMarketApp = ({ backButton, currentValue, handleSetData }) => {
|
||||
const { apps, getMarketApps } = useOpennebula();
|
||||
const { apps, getMarketApps } = useOpennebula()
|
||||
|
||||
useEffect(() => {
|
||||
getMarketApps();
|
||||
}, []);
|
||||
|
||||
const handleSelect = index => handleSetData(index);
|
||||
const handleUnselect = () => handleSetData();
|
||||
|
||||
const renderApp = app => (
|
||||
<SelectCard
|
||||
key={`app-${app.ID}`}
|
||||
isSelected={app.ID === String(currentValue)}
|
||||
handleSelect={handleSelect}
|
||||
handleUnselect={handleUnselect}
|
||||
{...app}
|
||||
/>
|
||||
);
|
||||
getMarketApps()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Search
|
||||
list={apps?.sort(sortByID)}
|
||||
listOptions={{ shouldSort: true, sortFn: sortByID, keys: ['NAME'] }}
|
||||
renderResult={renderApp}
|
||||
startAdornment={backButton}
|
||||
renderResult={({ ID, NAME }) => {
|
||||
const isSelected = ID === String(currentValue)
|
||||
|
||||
return <SelectCard
|
||||
key={`app-${ID}`}
|
||||
title={`📦 ${NAME}`}
|
||||
isSelected={isSelected}
|
||||
handleClick={() => handleSetData(!isSelected && ID)}
|
||||
/>
|
||||
}}
|
||||
searchBoxProps={{
|
||||
style: {
|
||||
display: 'flex',
|
||||
@ -41,19 +37,19 @@ const ListMarketApp = ({ backButton, currentValue, handleSetData }) => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
ListMarketApp.propTypes = {
|
||||
backButton: PropTypes.node,
|
||||
currentValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
handleSetData: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
ListMarketApp.defaultProps = {
|
||||
backButton: null,
|
||||
currentValue: undefined,
|
||||
handleSetData: () => undefined
|
||||
};
|
||||
}
|
||||
|
||||
export default ListMarketApp;
|
||||
export default ListMarketApp
|
||||
|
@ -1,38 +1,34 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import useOpennebula from 'client/hooks/useOpennebula';
|
||||
import Search from 'client/components/Search';
|
||||
import { SelectCard } from 'client/components/Cards';
|
||||
import useOpennebula from 'client/hooks/useOpennebula'
|
||||
import Search from 'client/components/Search'
|
||||
import { SelectCard } from 'client/components/Cards'
|
||||
|
||||
const sortByID = (a, b) => a.ID - b.ID;
|
||||
const sortByID = (a, b) => a.ID - b.ID
|
||||
|
||||
const ListTemplates = ({ backButton, currentValue, handleSetData }) => {
|
||||
const { templates, getTemplates } = useOpennebula();
|
||||
const { templates, getTemplates } = useOpennebula()
|
||||
|
||||
useEffect(() => {
|
||||
getTemplates();
|
||||
}, []);
|
||||
|
||||
const handleSelect = index => handleSetData(index);
|
||||
const handleUnselect = () => handleSetData();
|
||||
|
||||
const renderTemplate = tmp => (
|
||||
<SelectCard
|
||||
key={`tmp-${tmp.ID}`}
|
||||
isSelected={tmp.ID === String(currentValue)}
|
||||
handleSelect={handleSelect}
|
||||
handleUnselect={handleUnselect}
|
||||
{...tmp}
|
||||
/>
|
||||
);
|
||||
getTemplates()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Search
|
||||
list={templates?.sort(sortByID)}
|
||||
listOptions={{ shouldSort: true, sortFn: sortByID, keys: ['NAME'] }}
|
||||
renderResult={renderTemplate}
|
||||
startAdornment={backButton}
|
||||
renderResult={({ ID, NAME }) => {
|
||||
const isSelected = ID === String(currentValue)
|
||||
|
||||
return <SelectCard
|
||||
key={`tmp-${ID}`}
|
||||
title={`📁 ${NAME}`}
|
||||
isSelected={isSelected}
|
||||
handleClick={() => handleSetData(!isSelected && ID)}
|
||||
/>
|
||||
}}
|
||||
searchBoxProps={{
|
||||
style: {
|
||||
display: 'flex',
|
||||
@ -41,19 +37,19 @@ const ListTemplates = ({ backButton, currentValue, handleSetData }) => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
ListTemplates.propTypes = {
|
||||
backButton: PropTypes.node,
|
||||
currentValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
handleSetData: PropTypes.func
|
||||
};
|
||||
}
|
||||
|
||||
ListTemplates.defaultProps = {
|
||||
backButton: null,
|
||||
currentValue: undefined,
|
||||
handleSetData: () => undefined
|
||||
};
|
||||
}
|
||||
|
||||
export default ListTemplates;
|
||||
export default ListTemplates
|
||||
|
@ -1,23 +1,23 @@
|
||||
import React, { useCallback, useState, useEffect, useMemo } from 'react';
|
||||
import React, { useCallback, useState, useEffect, useMemo } from 'react'
|
||||
|
||||
import {
|
||||
ArrowBackIosOutlined as BackIcon,
|
||||
ShoppingCartOutlined as MarketplaceIcon,
|
||||
InsertDriveFileOutlined as TemplateIcon
|
||||
} from '@material-ui/icons';
|
||||
import { makeStyles, IconButton, Button, Fade } from '@material-ui/core';
|
||||
import DockerLogo from 'client/icons/docker';
|
||||
} from '@material-ui/icons'
|
||||
import { makeStyles, IconButton, Button, Fade } from '@material-ui/core'
|
||||
import DockerLogo from 'client/icons/docker'
|
||||
|
||||
import ListTemplates from './List/Templates';
|
||||
import ListMarketApps from './List/MarketApps';
|
||||
import DockerFile from './List/Docker';
|
||||
import { STEP_FORM_SCHEMA } from './schema';
|
||||
import ListTemplates from './List/Templates'
|
||||
import ListMarketApps from './List/MarketApps'
|
||||
import DockerFile from './List/Docker'
|
||||
import { STEP_FORM_SCHEMA } from './schema'
|
||||
|
||||
export const STEP_ID = 'template';
|
||||
export const STEP_ID = 'template'
|
||||
|
||||
const SCREENS = [
|
||||
{
|
||||
id: 'template',
|
||||
id: 'id',
|
||||
button: <TemplateIcon style={{ fontSize: 100 }} />,
|
||||
content: ListTemplates
|
||||
},
|
||||
@ -31,7 +31,7 @@ const SCREENS = [
|
||||
button: <DockerLogo width="100" height="100%" color="#066da5" />,
|
||||
content: DockerFile
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
root: {
|
||||
@ -45,35 +45,35 @@ const useStyles = makeStyles(() => ({
|
||||
flexWrap: 'wrap'
|
||||
},
|
||||
button: { backgroundColor: '#fff' }
|
||||
}));
|
||||
}))
|
||||
|
||||
const Template = () => ({
|
||||
id: STEP_ID,
|
||||
label: 'Template',
|
||||
resolver: STEP_FORM_SCHEMA,
|
||||
content: useCallback(({ data = {}, setFormData }) => {
|
||||
const classes = useStyles();
|
||||
const [screen, setScreen] = useState(undefined);
|
||||
const classes = useStyles()
|
||||
const [screen, setScreen] = useState(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(data).length > 0) {
|
||||
const currentScreen = Object.keys(data)[0];
|
||||
setScreen(SCREENS.find(src => src.id === currentScreen));
|
||||
const currentScreen = Object.keys(data)[0]
|
||||
setScreen(SCREENS.find(src => src.id === currentScreen))
|
||||
}
|
||||
}, []);
|
||||
}, [])
|
||||
|
||||
const handleSetTemplate = template =>
|
||||
setFormData(prevData => ({
|
||||
...prevData,
|
||||
[STEP_ID]: template ? { [screen.id]: template } : undefined
|
||||
}));
|
||||
}))
|
||||
|
||||
const handleBack = () => {
|
||||
setScreen(undefined);
|
||||
handleSetTemplate();
|
||||
};
|
||||
setScreen(undefined)
|
||||
handleSetTemplate()
|
||||
}
|
||||
|
||||
const Content = useMemo(() => screen?.content, [screen]);
|
||||
const Content = useMemo(() => screen?.content, [screen])
|
||||
|
||||
return screen !== undefined ? (
|
||||
<Content
|
||||
@ -101,8 +101,8 @@ const Template = () => ({
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}, [])
|
||||
});
|
||||
})
|
||||
|
||||
export default Template;
|
||||
export default Template
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as yup from 'yup';
|
||||
import { getValidationFromFields } from 'client/utils/helpers';
|
||||
import * as yup from 'yup'
|
||||
import { getValidationFromFields } from 'client/utils/helpers'
|
||||
|
||||
export const FORM_FIELDS = [
|
||||
{
|
||||
name: 'template',
|
||||
name: 'id',
|
||||
validation: yup.number().min(0, 'Invalid template')
|
||||
},
|
||||
{
|
||||
@ -14,9 +14,9 @@ export const FORM_FIELDS = [
|
||||
name: 'docker',
|
||||
validation: yup.string().trim()
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
export const STEP_FORM_SCHEMA = yup
|
||||
.object(getValidationFromFields(FORM_FIELDS))
|
||||
.required('Template is required')
|
||||
.default(undefined);
|
||||
.default(undefined)
|
||||
|
@ -1,18 +1,18 @@
|
||||
import * as yup from 'yup';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import * as yup from 'yup'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import BasicConfiguration from './BasicConfiguration';
|
||||
import Networks from './Networks';
|
||||
import Template from './Template';
|
||||
import Policies from './Policies';
|
||||
import BasicConfiguration from './BasicConfiguration'
|
||||
import Networks from './Networks'
|
||||
import Template from './Template'
|
||||
import Policies from './Policies'
|
||||
|
||||
const Steps = () => {
|
||||
const basic = BasicConfiguration();
|
||||
const networks = Networks();
|
||||
const template = Template();
|
||||
const policies = Policies();
|
||||
const basic = BasicConfiguration()
|
||||
const networks = Networks()
|
||||
const template = Template()
|
||||
const policies = Policies()
|
||||
|
||||
const steps = [basic, networks, template, policies];
|
||||
const steps = [basic, networks, template, policies]
|
||||
|
||||
const resolvers = yup.object({
|
||||
id: yup
|
||||
@ -28,11 +28,11 @@ const Steps = () => {
|
||||
x: yup.number().default(0),
|
||||
y: yup.number().default(0)
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
const defaultValues = () => resolvers.default();
|
||||
const defaultValues = () => resolvers.default()
|
||||
|
||||
return { steps, defaultValues, resolvers };
|
||||
};
|
||||
return { steps, defaultValues, resolvers }
|
||||
}
|
||||
|
||||
export default Steps;
|
||||
export default Steps
|
||||
|
@ -1,25 +1,25 @@
|
||||
import React, { useEffect, useState, useCallback, createContext } from 'react';
|
||||
import React, { useEffect, useState, useCallback, createContext } from 'react'
|
||||
|
||||
import * as yup from 'yup';
|
||||
import { useWatch } from 'react-hook-form';
|
||||
import * as yup from 'yup'
|
||||
import { useWatch } from 'react-hook-form'
|
||||
|
||||
import { ReactFlowProvider } from 'react-flow-renderer';
|
||||
import { Box } from '@material-ui/core';
|
||||
import { ReactFlowProvider } from 'react-flow-renderer'
|
||||
import { Box } from '@material-ui/core'
|
||||
|
||||
import useListForm from 'client/hooks/useListForm';
|
||||
import FormStepper from 'client/components/FormStepper';
|
||||
import { DialogForm } from 'client/components/Dialogs';
|
||||
import { STEP_ID as NETWORKING_ID } from 'client/containers/ApplicationsTemplates/Create/Steps/Networking';
|
||||
import { STEP_ID as NETWORKS_ID } from 'client/containers/ApplicationsTemplates/Create/Steps/Tiers/Steps/Networks';
|
||||
import useListForm from 'client/hooks/useListForm'
|
||||
import FormStepper from 'client/components/FormStepper'
|
||||
import { DialogForm } from 'client/components/Dialogs'
|
||||
import { STEP_ID as NETWORKING_ID } from 'client/containers/ApplicationsTemplates/Create/Steps/Networking'
|
||||
import { STEP_ID as NETWORKS_ID } from 'client/containers/ApplicationsTemplates/Create/Steps/Tiers/Steps/Networks'
|
||||
|
||||
import Steps from './Steps';
|
||||
import Flow from './Flow';
|
||||
import Steps from './Steps'
|
||||
import Flow from './Flow'
|
||||
|
||||
export const Context = createContext({});
|
||||
export const STEP_ID = 'tiers';
|
||||
export const Context = createContext({})
|
||||
export const STEP_ID = 'tiers'
|
||||
|
||||
const Tiers = () => {
|
||||
const { steps, defaultValues, resolvers } = Steps();
|
||||
const { steps, defaultValues, resolvers } = Steps()
|
||||
|
||||
return {
|
||||
id: STEP_ID,
|
||||
@ -30,9 +30,9 @@ const Tiers = () => {
|
||||
.required('Tiers field is required')
|
||||
.default([]),
|
||||
content: useCallback(({ data, setFormData }) => {
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
const [nestedForm, setNestedForm] = useState({});
|
||||
const form = useWatch({});
|
||||
const [showDialog, setShowDialog] = useState(false)
|
||||
const [nestedForm, setNestedForm] = useState({})
|
||||
const form = useWatch({})
|
||||
|
||||
const {
|
||||
editingData,
|
||||
@ -44,24 +44,24 @@ const Tiers = () => {
|
||||
list: data,
|
||||
setList: setFormData,
|
||||
defaultValue: defaultValues()
|
||||
});
|
||||
})
|
||||
|
||||
const handleEditTier = id => {
|
||||
handleEdit(id);
|
||||
setShowDialog(true);
|
||||
};
|
||||
handleEdit(id)
|
||||
setShowDialog(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setNestedForm(form);
|
||||
}, []);
|
||||
setNestedForm(form)
|
||||
}, [])
|
||||
|
||||
const formSteps = React.useMemo(() => {
|
||||
const networking = nestedForm[NETWORKING_ID] ?? [];
|
||||
const networking = nestedForm[NETWORKING_ID] ?? []
|
||||
|
||||
return steps.filter(
|
||||
({ id }) => id !== NETWORKS_ID || networking.length !== 0
|
||||
);
|
||||
}, [nestedForm]);
|
||||
)
|
||||
}, [nestedForm])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -89,8 +89,8 @@ const Tiers = () => {
|
||||
steps={formSteps}
|
||||
schema={resolvers}
|
||||
onSubmit={values => {
|
||||
handleSave(values);
|
||||
setShowDialog(false);
|
||||
handleSave(values)
|
||||
setShowDialog(false)
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@ -98,9 +98,9 @@ const Tiers = () => {
|
||||
</Context.Provider>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
)
|
||||
}, [])
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Tiers;
|
||||
export default Tiers
|
||||
|
@ -1,28 +1,28 @@
|
||||
import * as yup from 'yup';
|
||||
import * as yup from 'yup'
|
||||
|
||||
import BasicConfiguration from './BasicConfiguration';
|
||||
import Clusters from './Clusters';
|
||||
import Networking from './Networking';
|
||||
import Tiers from './Tiers';
|
||||
import BasicConfiguration from './BasicConfiguration'
|
||||
import Clusters from './Clusters'
|
||||
import Networking from './Networking'
|
||||
import Tiers from './Tiers'
|
||||
|
||||
const Steps = () => {
|
||||
const basic = BasicConfiguration();
|
||||
const clusters = Clusters();
|
||||
const networking = Networking();
|
||||
const tiers = Tiers();
|
||||
const basic = BasicConfiguration()
|
||||
const clusters = Clusters()
|
||||
const networking = Networking()
|
||||
const tiers = Tiers()
|
||||
|
||||
const steps = [basic, clusters, networking, tiers];
|
||||
const steps = [basic, clusters, networking, tiers]
|
||||
|
||||
const resolvers = yup.object({
|
||||
[basic.id]: basic.resolver,
|
||||
[clusters.id]: clusters.resolver,
|
||||
[networking.id]: networking.resolver,
|
||||
[tiers.id]: tiers.resolver
|
||||
});
|
||||
})
|
||||
|
||||
const defaultValues = resolvers.default();
|
||||
const defaultValues = resolvers.default()
|
||||
|
||||
return { steps, defaultValues, resolvers };
|
||||
};
|
||||
return { steps, defaultValues, resolvers }
|
||||
}
|
||||
|
||||
export default Steps;
|
||||
export default Steps
|
||||
|
@ -1,72 +1,73 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Redirect, useHistory, useParams } from 'react-router-dom';
|
||||
import React, { useEffect } from 'react'
|
||||
import { Redirect, useHistory, useParams } from 'react-router-dom'
|
||||
|
||||
import { Container } from '@material-ui/core';
|
||||
import { useForm, FormProvider } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers';
|
||||
import { Container } from '@material-ui/core'
|
||||
import { useForm, FormProvider } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers'
|
||||
|
||||
import FormStepper from 'client/components/FormStepper';
|
||||
import Steps from 'client/containers/ApplicationsTemplates/Create/Steps';
|
||||
import FormStepper from 'client/components/FormStepper'
|
||||
import Steps from 'client/containers/ApplicationsTemplates/Create/Steps'
|
||||
|
||||
import { PATH } from 'client/router/endpoints';
|
||||
import useFetch from 'client/hooks/useFetch';
|
||||
import useApplication from 'client/hooks/useApplication';
|
||||
import mapApplicationToForm from 'client/utils/parser/toApplicationForm';
|
||||
import mapFormToApplication from 'client/utils/parser/toApplicationTemplate';
|
||||
import { PATH } from 'client/router/endpoints'
|
||||
import useFetch from 'client/hooks/useFetch'
|
||||
import useApplication from 'client/hooks/useApplication'
|
||||
import mapApplicationToForm from 'client/utils/parser/toApplicationForm'
|
||||
import mapFormToApplication from 'client/utils/parser/toApplicationTemplate'
|
||||
|
||||
function ApplicationCreate() {
|
||||
const history = useHistory();
|
||||
const { id } = useParams();
|
||||
const { steps, defaultValues, resolvers } = Steps();
|
||||
function ApplicationCreate () {
|
||||
const history = useHistory()
|
||||
const { id } = useParams()
|
||||
const { steps, defaultValues, resolvers } = Steps()
|
||||
const {
|
||||
getApplicationTemplate,
|
||||
createApplicationTemplate,
|
||||
updateApplicationTemplate
|
||||
} = useApplication();
|
||||
} = useApplication()
|
||||
const { data, fetchRequest, loading, error } = useFetch(
|
||||
getApplicationTemplate
|
||||
);
|
||||
)
|
||||
|
||||
const methods = useForm({
|
||||
mode: 'onSubmit',
|
||||
defaultValues,
|
||||
resolver: yupResolver(resolvers)
|
||||
});
|
||||
})
|
||||
|
||||
const onSubmit = formData => {
|
||||
const application = mapFormToApplication(formData);
|
||||
const application = mapFormToApplication(formData)
|
||||
|
||||
if (id)
|
||||
if (id) {
|
||||
updateApplicationTemplate({ id, data: application }).then(
|
||||
res => res && history.push(PATH.APPLICATION.LIST)
|
||||
);
|
||||
else
|
||||
res => res && history.push(PATH.APPLICATIONS)
|
||||
)
|
||||
} else {
|
||||
createApplicationTemplate({ data: application }).then(
|
||||
res => res && history.push(PATH.APPLICATION.LIST)
|
||||
);
|
||||
};
|
||||
res => res && history.push(PATH.APPLICATIONS)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (id) {
|
||||
const idNumber = parseInt(id, 10);
|
||||
if (idNumber < 0) throw new Error();
|
||||
const idNumber = parseInt(id, 10)
|
||||
if (idNumber < 0) throw new Error()
|
||||
|
||||
fetchRequest({ id: idNumber });
|
||||
fetchRequest({ id: idNumber })
|
||||
}
|
||||
} catch {
|
||||
// show error
|
||||
history.push(PATH.DASHBOARD);
|
||||
history.push(PATH.DASHBOARD)
|
||||
}
|
||||
}, [id]);
|
||||
}, [id])
|
||||
|
||||
useEffect(() => {
|
||||
const formData = data ? mapApplicationToForm(data) : {};
|
||||
methods.reset(resolvers.cast(formData), { errors: false });
|
||||
}, [data]);
|
||||
const formData = data ? mapApplicationToForm(data) : {}
|
||||
methods.reset(resolvers.cast(formData), { errors: false })
|
||||
}, [data])
|
||||
|
||||
if (error) {
|
||||
return <Redirect to={PATH.DASHBOARD} />;
|
||||
return <Redirect to={PATH.DASHBOARD} />
|
||||
}
|
||||
|
||||
return (
|
||||
@ -82,11 +83,11 @@ function ApplicationCreate() {
|
||||
</FormProvider>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
ApplicationCreate.propTypes = {};
|
||||
ApplicationCreate.propTypes = {}
|
||||
|
||||
ApplicationCreate.defaultProps = {};
|
||||
ApplicationCreate.defaultProps = {}
|
||||
|
||||
export default ApplicationCreate;
|
||||
export default ApplicationCreate
|
||||
|
@ -1,3 +1,3 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
export default makeStyles(theme => ({}));
|
||||
export default makeStyles(theme => ({}))
|
||||
|
@ -1,3 +1,3 @@
|
||||
import ApplicationsTemplatesCreate from 'client/containers/ApplicationsTemplates/Create';
|
||||
import ApplicationsTemplatesCreate from 'client/containers/ApplicationsTemplates/Create'
|
||||
|
||||
export { ApplicationsTemplatesCreate };
|
||||
export { ApplicationsTemplatesCreate }
|
||||
|
@ -13,14 +13,14 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React from 'react';
|
||||
import React from 'react'
|
||||
|
||||
import { Box, Typography } from '@material-ui/core';
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
|
||||
import dashboardStyles from 'client/containers/Dashboard/styles';
|
||||
import dashboardStyles from 'client/containers/Dashboard/styles'
|
||||
|
||||
function Dashboard() {
|
||||
const classes = dashboardStyles();
|
||||
function Dashboard () {
|
||||
const classes = dashboardStyles()
|
||||
|
||||
return (
|
||||
<Box>
|
||||
@ -32,7 +32,7 @@ function Dashboard() {
|
||||
Dashboard
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default Dashboard;
|
||||
export default Dashboard
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
root: {},
|
||||
title: {
|
||||
color: theme.palette.common.black
|
||||
}
|
||||
}));
|
||||
}))
|
||||
|
@ -13,21 +13,21 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import { Translate } from 'client/components/HOC/Translate';
|
||||
import { NotFound } from 'client/constants/translates';
|
||||
import { Translate } from 'client/components/HOC/Translate'
|
||||
import { NotFound } from 'client/constants/translates'
|
||||
|
||||
function Error404() {
|
||||
function Error404 () {
|
||||
return (
|
||||
<Fragment>
|
||||
<Translate word={NotFound} />
|
||||
</Fragment>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
Error404.propTypes = {};
|
||||
Error404.propTypes = {}
|
||||
|
||||
Error404.defaultProps = {};
|
||||
Error404.defaultProps = {}
|
||||
|
||||
export default Error404;
|
||||
export default Error404
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Typography, Link } from '@material-ui/core';
|
||||
import React from 'react'
|
||||
import { Typography, Link } from '@material-ui/core'
|
||||
|
||||
const Copyright = () => {
|
||||
const year = new Date().getFullYear();
|
||||
const year = new Date().getFullYear()
|
||||
|
||||
return (
|
||||
<Typography variant="body2" color="textSecondary" align="center">
|
||||
@ -12,7 +12,7 @@ const Copyright = () => {
|
||||
</Link>
|
||||
{` ${year}. `}
|
||||
</Typography>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default Copyright;
|
||||
export default Copyright
|
||||
|
@ -1,20 +1,20 @@
|
||||
import React from 'react';
|
||||
import { func, string } from 'prop-types';
|
||||
import React from 'react'
|
||||
import { func, string } from 'prop-types'
|
||||
|
||||
import { Box, Button, TextField } from '@material-ui/core';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers';
|
||||
import * as yup from 'yup';
|
||||
import { Box, Button, TextField } from '@material-ui/core'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers'
|
||||
import * as yup from 'yup'
|
||||
|
||||
import { Token2FA, Next } from 'client/constants/translates';
|
||||
import loginStyles from 'client/containers/Login/styles';
|
||||
import { Token2FA, Next } from 'client/constants/translates'
|
||||
import loginStyles from 'client/containers/Login/styles'
|
||||
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import ButtonSubmit from 'client/components/FormControl/SubmitButton';
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper';
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import ButtonSubmit from 'client/components/FormControl/SubmitButton'
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper'
|
||||
|
||||
const Form2fa = ({ onBack, onSubmit, error }) => {
|
||||
const classes = loginStyles();
|
||||
const classes = loginStyles()
|
||||
|
||||
const { register, handleSubmit, errors } = useForm({
|
||||
reValidateMode: 'onSubmit',
|
||||
@ -23,9 +23,9 @@ const Form2fa = ({ onBack, onSubmit, error }) => {
|
||||
token2fa: yup.string().required('Authenticator is a required field')
|
||||
})
|
||||
)
|
||||
});
|
||||
})
|
||||
|
||||
const tokenError = Boolean(errors.token || error);
|
||||
const tokenError = Boolean(errors.token || error)
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -59,19 +59,19 @@ const Form2fa = ({ onBack, onSubmit, error }) => {
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
Form2fa.propTypes = {
|
||||
onBack: func.isRequired,
|
||||
onSubmit: func.isRequired,
|
||||
error: string
|
||||
};
|
||||
}
|
||||
|
||||
Form2fa.defaultProps = {
|
||||
onBack: () => undefined,
|
||||
onSubmit: () => undefined,
|
||||
error: null
|
||||
};
|
||||
}
|
||||
|
||||
export default Form2fa;
|
||||
export default Form2fa
|
||||
|
@ -1,18 +1,18 @@
|
||||
import React from 'react';
|
||||
import { func } from 'prop-types';
|
||||
import React from 'react'
|
||||
import { func } from 'prop-types'
|
||||
|
||||
import { Box, Button } from '@material-ui/core';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { Box, Button } from '@material-ui/core'
|
||||
import { useForm, Controller } from 'react-hook-form'
|
||||
|
||||
import GroupSelect from 'client/components/FormControl/GroupSelect';
|
||||
import ButtonSubmit from 'client/components/FormControl/SubmitButton';
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import loginStyles from 'client/containers/Login/styles';
|
||||
import { Next } from 'client/constants/translates';
|
||||
import GroupSelect from 'client/components/FormControl/GroupSelect'
|
||||
import ButtonSubmit from 'client/components/FormControl/SubmitButton'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import loginStyles from 'client/containers/Login/styles'
|
||||
import { Next } from 'client/constants/translates'
|
||||
|
||||
function FormGroup({ onBack, onSubmit }) {
|
||||
const classes = loginStyles();
|
||||
const { control, handleSubmit } = useForm();
|
||||
function FormGroup ({ onBack, onSubmit }) {
|
||||
const classes = loginStyles()
|
||||
const { control, handleSubmit } = useForm()
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -28,21 +28,21 @@ function FormGroup({ onBack, onSubmit }) {
|
||||
label={Tr(Next)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
FormGroup.propTypes = {
|
||||
onBack: func.isRequired,
|
||||
onSubmit: func.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
FormGroup.defaultProps = {
|
||||
onBack: () => undefined,
|
||||
onSubmit: () => undefined
|
||||
};
|
||||
}
|
||||
|
||||
FormGroup.propTypes = {};
|
||||
FormGroup.propTypes = {}
|
||||
|
||||
FormGroup.defaultProps = {};
|
||||
FormGroup.defaultProps = {}
|
||||
|
||||
export default FormGroup;
|
||||
export default FormGroup
|
||||
|
@ -1,23 +1,23 @@
|
||||
import React from 'react';
|
||||
import { func, string } from 'prop-types';
|
||||
import { Box, Checkbox, TextField, FormControlLabel } from '@material-ui/core';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers';
|
||||
import * as yup from 'yup';
|
||||
import React from 'react'
|
||||
import { func, string } from 'prop-types'
|
||||
import { Box, Checkbox, TextField, FormControlLabel } from '@material-ui/core'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers'
|
||||
import * as yup from 'yup'
|
||||
|
||||
import {
|
||||
SignIn,
|
||||
Username,
|
||||
Password,
|
||||
KeepLoggedIn
|
||||
} from 'client/constants/translates';
|
||||
import { Tr } from 'client/components/HOC';
|
||||
import ButtonSubmit from 'client/components/FormControl/SubmitButton';
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper';
|
||||
import loginStyles from 'client/containers/Login/styles';
|
||||
} from 'client/constants/translates'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import ButtonSubmit from 'client/components/FormControl/SubmitButton'
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper'
|
||||
import loginStyles from 'client/containers/Login/styles'
|
||||
|
||||
function FormUser({ onSubmit, error }) {
|
||||
const classes = loginStyles();
|
||||
function FormUser ({ onSubmit, error }) {
|
||||
const classes = loginStyles()
|
||||
|
||||
const { register, handleSubmit, errors } = useForm({
|
||||
reValidateMode: 'onSubmit',
|
||||
@ -28,10 +28,10 @@ function FormUser({ onSubmit, error }) {
|
||||
remember: yup.boolean()
|
||||
})
|
||||
)
|
||||
});
|
||||
})
|
||||
|
||||
const userError = Boolean(errors.user || error);
|
||||
const passError = Boolean(errors.pass);
|
||||
const userError = Boolean(errors.user || error)
|
||||
const passError = Boolean(errors.pass)
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -88,17 +88,17 @@ function FormUser({ onSubmit, error }) {
|
||||
label={Tr(SignIn)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
FormUser.propTypes = {
|
||||
onSubmit: func.isRequired,
|
||||
error: string
|
||||
};
|
||||
}
|
||||
|
||||
FormUser.defaultProps = {
|
||||
onSubmit: () => undefined,
|
||||
error: null
|
||||
};
|
||||
}
|
||||
|
||||
export default FormUser;
|
||||
export default FormUser
|
||||
|
@ -1,19 +1,4 @@
|
||||
/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
||||
/* not use this file except in compliance with the License. You may obtain */
|
||||
/* a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState } from 'react'
|
||||
import {
|
||||
Paper,
|
||||
Box,
|
||||
@ -21,28 +6,28 @@ import {
|
||||
Slide,
|
||||
LinearProgress,
|
||||
useMediaQuery
|
||||
} from '@material-ui/core';
|
||||
} from '@material-ui/core'
|
||||
|
||||
import useAuth from 'client/hooks/useAuth';
|
||||
import useAuth from 'client/hooks/useAuth'
|
||||
|
||||
import FormUser from 'client/containers/Login/Forms/FormUser';
|
||||
import Form2fa from 'client/containers/Login/Forms/Form2fa';
|
||||
import FormGroup from 'client/containers/Login/Forms/FormGroup';
|
||||
import loginStyles from 'client/containers/Login/styles';
|
||||
import Logo from 'client/icons/logo';
|
||||
import { ONEADMIN_ID } from 'client/constants';
|
||||
import FormUser from 'client/containers/Login/Forms/FormUser'
|
||||
import Form2fa from 'client/containers/Login/Forms/Form2fa'
|
||||
import FormGroup from 'client/containers/Login/Forms/FormGroup'
|
||||
import loginStyles from 'client/containers/Login/styles'
|
||||
import Logo from 'client/icons/logo'
|
||||
import { ONEADMIN_ID } from 'client/constants'
|
||||
|
||||
const STEP = {
|
||||
USER_FORM: 0,
|
||||
FA2_FORM: 1,
|
||||
GROUP_FORM: 2
|
||||
};
|
||||
}
|
||||
|
||||
function Login() {
|
||||
const classes = loginStyles();
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'));
|
||||
const [user, setUser] = useState(undefined);
|
||||
const [step, setStep] = useState(STEP.USER_FORM);
|
||||
function Login () {
|
||||
const classes = loginStyles()
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'))
|
||||
const [user, setUser] = useState(undefined)
|
||||
const [step, setStep] = useState(STEP.USER_FORM)
|
||||
const {
|
||||
isLoading,
|
||||
error,
|
||||
@ -50,30 +35,28 @@ function Login() {
|
||||
logout,
|
||||
getAuthInfo,
|
||||
setPrimaryGroup
|
||||
} = useAuth();
|
||||
} = useAuth()
|
||||
|
||||
const handleSubmitUser = dataForm => {
|
||||
login({ ...user, ...dataForm }).then(data => {
|
||||
console.log('-->', data);
|
||||
if (data?.token) {
|
||||
getAuthInfo().then(() => {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
data?.id !== ONEADMIN_ID && setStep(STEP.GROUP_FORM);
|
||||
});
|
||||
data?.id !== ONEADMIN_ID && setStep(STEP.GROUP_FORM)
|
||||
})
|
||||
} else {
|
||||
setStep(data ? STEP.FA2_FORM : step);
|
||||
setUser(data ? dataForm : user);
|
||||
setStep(data ? STEP.FA2_FORM : step)
|
||||
setUser(data ? dataForm : user)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
const handleSubmitGroup = dataForm => setPrimaryGroup(dataForm);
|
||||
const handleSubmitGroup = dataForm => setPrimaryGroup(dataForm)
|
||||
|
||||
const handleBack = () => {
|
||||
logout();
|
||||
setUser(undefined);
|
||||
setStep(STEP.USER_FORM);
|
||||
};
|
||||
logout()
|
||||
setUser(undefined)
|
||||
setStep(STEP.USER_FORM)
|
||||
}
|
||||
|
||||
return (
|
||||
<Container
|
||||
@ -131,11 +114,11 @@ function Login() {
|
||||
</Box>
|
||||
</Paper>
|
||||
</Container>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
Login.propTypes = {};
|
||||
Login.propTypes = {}
|
||||
|
||||
Login.defaultProps = {};
|
||||
Login.defaultProps = {}
|
||||
|
||||
export default Login;
|
||||
export default Login
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
||||
export default makeStyles(theme =>
|
||||
// const getColor = theme.palette.type === 'light' ? darken : lighten;
|
||||
// const getBackgroundColor = theme.palette.type === 'light' ? lighten : darken;
|
||||
// color: getColor(theme.palette.error.main, 0.6),
|
||||
// backgroundColor: getBackgroundColor(theme.palette.error.main, 0.9)
|
||||
// const getColor = theme.palette.type === 'light' ? darken : lighten;
|
||||
// const getBackgroundColor = theme.palette.type === 'light' ? lighten : darken;
|
||||
// color: getColor(theme.palette.error.main, 0.6),
|
||||
// backgroundColor: getBackgroundColor(theme.palette.error.main, 0.9)
|
||||
|
||||
({
|
||||
root: {
|
||||
@ -40,4 +40,4 @@ export default makeStyles(theme =>
|
||||
animation: '1s ease-out 0s 1'
|
||||
}
|
||||
})
|
||||
);
|
||||
)
|
||||
|
@ -13,15 +13,15 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React from 'react';
|
||||
import { Translate } from 'client/components/HOC';
|
||||
import constants from 'client/constants';
|
||||
import React from 'react'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import constants from 'client/constants'
|
||||
|
||||
const { settings } = constants;
|
||||
const { settings } = constants
|
||||
const Settings = () => (
|
||||
<div>
|
||||
<Translate word={settings} />
|
||||
</div>
|
||||
);
|
||||
)
|
||||
|
||||
export default Settings;
|
||||
export default Settings
|
||||
|
@ -1,36 +1,36 @@
|
||||
import React from 'react';
|
||||
import { string, func, shape, object } from 'prop-types';
|
||||
import React from 'react'
|
||||
import { string, func, shape, object } from 'prop-types'
|
||||
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { useForm, Controller } from 'react-hook-form'
|
||||
import {
|
||||
TextField,
|
||||
Grid,
|
||||
Typography,
|
||||
FormControlLabel,
|
||||
Checkbox
|
||||
} from '@material-ui/core';
|
||||
} from '@material-ui/core'
|
||||
|
||||
import ButtonSubmit from 'client/components/FormControl/SubmitButton';
|
||||
import { requestData, requestParams } from 'client/utils';
|
||||
import ButtonSubmit from 'client/components/FormControl/SubmitButton'
|
||||
import { requestData, requestParams } from 'client/utils'
|
||||
|
||||
const ResponseForm = ({
|
||||
handleChangeResponse,
|
||||
command: { name, httpMethod, params }
|
||||
}) => {
|
||||
const { control, handleSubmit, errors, formState } = useForm();
|
||||
const { control, handleSubmit, errors, formState } = useForm()
|
||||
|
||||
const onSubmit = dataForm => {
|
||||
const { url, options } = requestParams(dataForm, {
|
||||
name,
|
||||
httpMethod,
|
||||
params
|
||||
});
|
||||
})
|
||||
|
||||
requestData(url, options).then(({ id, ...res }) => {
|
||||
id === 401 && console.log('ERROR');
|
||||
id === 200 && handleChangeResponse(JSON.stringify(res, null, '\t'));
|
||||
});
|
||||
};
|
||||
id === 401 && console.log('ERROR')
|
||||
id === 200 && handleChangeResponse(JSON.stringify(res, null, '\t'))
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -81,8 +81,8 @@ const ResponseForm = ({
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
ResponseForm.propTypes = {
|
||||
command: shape({
|
||||
@ -91,7 +91,7 @@ ResponseForm.propTypes = {
|
||||
params: object.isRequired
|
||||
}).isRequired,
|
||||
handleChangeResponse: func.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
ResponseForm.defaultProps = {
|
||||
command: {
|
||||
@ -100,5 +100,5 @@ ResponseForm.defaultProps = {
|
||||
params: {}
|
||||
},
|
||||
handleChangeResponse: () => undefined
|
||||
};
|
||||
export default ResponseForm;
|
||||
}
|
||||
export default ResponseForm
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user