mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-16 22:50:10 +03:00
F OpenNebula/one#5422: Add vm detail tab forms (#1422)
This commit is contained in:
parent
2d196caeca
commit
ada6fb630b
@ -1,4 +1,5 @@
|
||||
{
|
||||
"sourceType": "unambiguous",
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
@ -12,7 +13,9 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"@babel/preset-react"
|
||||
["@babel/preset-react", {
|
||||
"runtime": "automatic"
|
||||
}]
|
||||
],
|
||||
"plugins": [
|
||||
["module-resolver", {
|
||||
|
@ -21,6 +21,8 @@
|
||||
"import/no-extraneous-dependencies": ["error", {
|
||||
"devDependencies": ["*.js"]
|
||||
}],
|
||||
"react/jsx-uses-react": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"default-case": 0,
|
||||
"jsdoc/require-jsdoc": ["error", {
|
||||
"publicOnly": true,
|
||||
|
@ -130,6 +130,7 @@ info-tabs:
|
||||
enabled: true
|
||||
not_on:
|
||||
- vcenter
|
||||
- firecracker
|
||||
|
||||
network:
|
||||
enabled: true
|
||||
|
@ -0,0 +1,14 @@
|
||||
# this display button and clock icon in table of vm
|
||||
:leases:
|
||||
suspend:
|
||||
time: "+1209600"
|
||||
color: "#000000"
|
||||
warning:
|
||||
time: "-86400"
|
||||
color: "#085aef"
|
||||
terminate:
|
||||
time: "+1209600"
|
||||
color: "#e1ef08"
|
||||
warning:
|
||||
time: "-86400"
|
||||
color: "#ef2808"
|
2734
src/fireedge/package-lock.json
generated
2734
src/fireedge/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,7 @@
|
||||
"vms"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "0.5.0-rc.3",
|
||||
"cross-env": "7.0.2",
|
||||
"eslint": "7.11.0",
|
||||
"eslint-config-prettier": "6.11.0",
|
||||
@ -46,7 +47,7 @@
|
||||
"eslint-plugin-standard": "4.0.1",
|
||||
"opennebula-generatepotfile": "1.0.0",
|
||||
"opennebula-potojson": "1.0.0",
|
||||
"react-hot-loader": "4.13.0",
|
||||
"react-refresh": "0.10.0",
|
||||
"webpack-dev-middleware": "5.0.0",
|
||||
"webpack-hot-middleware": "2.25.0"
|
||||
},
|
||||
@ -60,13 +61,13 @@
|
||||
"@babel/plugin-proposal-optional-chaining": "7.12.13",
|
||||
"@babel/preset-env": "7.12.13",
|
||||
"@babel/preset-react": "7.12.13",
|
||||
"@hookform/resolvers": "0.1.1",
|
||||
"@hookform/resolvers": "1.3.7",
|
||||
"@loadable/babel-plugin": "5.13.2",
|
||||
"@loadable/component": "5.14.1",
|
||||
"@loadable/server": "5.14.2",
|
||||
"@loadable/webpack-plugin": "5.15.0",
|
||||
"@material-ui/core": "4.11.0",
|
||||
"@material-ui/lab": "4.0.0-alpha.58",
|
||||
"@loadable/component": "5.15.0",
|
||||
"@loadable/server": "5.15.1",
|
||||
"@loadable/webpack-plugin": "5.15.1",
|
||||
"@material-ui/core": "4.12.3",
|
||||
"@material-ui/lab": "4.0.0-alpha.60",
|
||||
"@reduxjs/toolkit": "1.5.1",
|
||||
"ace-builds": "1.4.12",
|
||||
"atob": "2.1.2",
|
||||
@ -74,7 +75,6 @@
|
||||
"babel-eslint": "10.1.0",
|
||||
"babel-loader": "8.2.1",
|
||||
"babel-plugin-module-resolver": "4.0.0",
|
||||
"babel-preset-react-hmre": "1.1.1",
|
||||
"btoa": "1.2.1",
|
||||
"clsx": "1.1.1",
|
||||
"compression": "1.7.4",
|
||||
@ -101,20 +101,20 @@
|
||||
"luxon": "1.25.0",
|
||||
"marked": "2.0.0",
|
||||
"morgan": "1.10.0",
|
||||
"notistack": "1.0.9",
|
||||
"notistack": "1.0.10",
|
||||
"opennebula-guacamole": "1.0.0",
|
||||
"path": "0.12.7",
|
||||
"process": "0.11.10",
|
||||
"prop-types": "15.7.2",
|
||||
"qrcode": "1.4.4",
|
||||
"react": "16.14.0",
|
||||
"react-ace": "9.1.4",
|
||||
"react-dom": "16.13.1",
|
||||
"react-flow-renderer": "5.11.1",
|
||||
"react-hook-form": "6.8.6",
|
||||
"react": "17.0.2",
|
||||
"react-ace": "9.2.1",
|
||||
"react-dom": "17.0.2",
|
||||
"react-flow-renderer": "9.6.0",
|
||||
"react-hook-form": "6.12.0",
|
||||
"react-json-pretty": "2.2.0",
|
||||
"react-minimal-pie-chart": "8.2.0",
|
||||
"react-redux": "7.2.1",
|
||||
"react-redux": "7.2.4",
|
||||
"react-router": "5.2.0",
|
||||
"react-router-dom": "5.2.0",
|
||||
"react-table": "7.7.0",
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { useEffect, useMemo, JSXElementConstructor } from 'react'
|
||||
|
||||
import Router from 'client/router'
|
||||
import { ENDPOINTS, PATH } from 'client/apps/provision/routes'
|
||||
@ -34,7 +34,7 @@ const APP_NAME = _APPS.provision.name
|
||||
/**
|
||||
* Provision App component.
|
||||
*
|
||||
* @returns {React.JSXElementConstructor} App rendered.
|
||||
* @returns {JSXElementConstructor} App rendered.
|
||||
*/
|
||||
const ProvisionApp = () => {
|
||||
const { isLogged, jwt, firstRender } = useAuth()
|
||||
@ -44,7 +44,7 @@ const ProvisionApp = () => {
|
||||
const { getProvisionsTemplates } = useProvisionApi()
|
||||
const { changeTitle } = useGeneralApi()
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
if (jwt) {
|
||||
@ -58,7 +58,7 @@ const ProvisionApp = () => {
|
||||
})()
|
||||
}, [jwt])
|
||||
|
||||
const endpoints = React.useMemo(() => [
|
||||
const endpoints = useMemo(() => [
|
||||
...ENDPOINTS,
|
||||
...(isDevelopment() ? DEV_ENDPOINTS : [])
|
||||
], [])
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { StaticRouter, BrowserRouter } from 'react-router-dom'
|
||||
@ -36,9 +36,9 @@ const APP_NAME = _APPS.provision.name
|
||||
* @param {Store} props.store - Redux store
|
||||
* @param {string|object} props.location - The URL the server received
|
||||
* @param {object} props.context - Context object contains the results of the render
|
||||
* @returns {React.JSXElementConstructor} Provision App
|
||||
* @returns {JSXElementConstructor} Provision App
|
||||
*/
|
||||
const Provision = ({ store, location, context }) => (
|
||||
const Provision = ({ store = {}, location = '', context = {} }) => (
|
||||
<ReduxProvider store={store}>
|
||||
<SocketProvider>
|
||||
<TranslateProvider>
|
||||
@ -64,14 +64,8 @@ const Provision = ({ store, location, context }) => (
|
||||
|
||||
Provision.propTypes = {
|
||||
location: PropTypes.string,
|
||||
context: PropTypes.shape({}),
|
||||
store: PropTypes.shape({})
|
||||
}
|
||||
|
||||
Provision.defaultProps = {
|
||||
location: '',
|
||||
context: {},
|
||||
store: {}
|
||||
context: PropTypes.object,
|
||||
store: PropTypes.object
|
||||
}
|
||||
|
||||
Provision.displayName = 'ProvisionApp'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { useEffect, useMemo, JSXElementConstructor } from 'react'
|
||||
|
||||
import Router from 'client/router'
|
||||
import { ENDPOINTS, PATH, getEndpointsByView } from 'client/apps/sunstone/routes'
|
||||
@ -34,20 +34,21 @@ const APP_NAME = _APPS.sunstone.name
|
||||
/**
|
||||
* Sunstone App component.
|
||||
*
|
||||
* @returns {React.JSXElementConstructor} App rendered.
|
||||
* @returns {JSXElementConstructor} App rendered.
|
||||
*/
|
||||
const SunstoneApp = () => {
|
||||
const { isLogged, jwt, firstRender, view, views } = useAuth()
|
||||
const { getAuthUser, logout, getSunstoneViews } = useAuthApi()
|
||||
const { getAuthUser, logout, getSunstoneViews, getSunstoneConfig } = useAuthApi()
|
||||
const { changeTitle } = useGeneralApi()
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
if (jwt) {
|
||||
changeTitle(APP_NAME)
|
||||
getAuthUser()
|
||||
await getSunstoneViews()
|
||||
await getSunstoneConfig()
|
||||
}
|
||||
} catch {
|
||||
logout()
|
||||
@ -55,7 +56,7 @@ const SunstoneApp = () => {
|
||||
})()
|
||||
}, [jwt])
|
||||
|
||||
const endpoints = React.useMemo(() => [
|
||||
const endpoints = useMemo(() => [
|
||||
...ENDPOINTS,
|
||||
...(view ? getEndpointsByView(views?.[view], ONE_ENDPOINTS) : []),
|
||||
...(isDevelopment() ? DEV_ENDPOINTS : [])
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { StaticRouter, BrowserRouter } from 'react-router-dom'
|
||||
@ -35,9 +35,9 @@ const APP_NAME = _APPS.sunstone.name
|
||||
* @param {Store} props.store - Redux store
|
||||
* @param {string|object} props.location - The URL the server received
|
||||
* @param {object} props.context - Context object contains the results of the render
|
||||
* @returns {React.JSXElementConstructor} Sunstone App
|
||||
* @returns {JSXElementConstructor} Sunstone App
|
||||
*/
|
||||
const Provision = ({ store, location, context }) => (
|
||||
const Sunstone = ({ store = {}, location = '', context = {} }) => (
|
||||
<ReduxProvider store={store}>
|
||||
<TranslateProvider>
|
||||
<MuiProvider theme={theme}>
|
||||
@ -59,18 +59,12 @@ const Provision = ({ store, location, context }) => (
|
||||
</ReduxProvider>
|
||||
)
|
||||
|
||||
Provision.propTypes = {
|
||||
Sunstone.propTypes = {
|
||||
location: PropTypes.string,
|
||||
context: PropTypes.shape({}),
|
||||
store: PropTypes.shape({})
|
||||
}
|
||||
|
||||
Provision.defaultProps = {
|
||||
location: '',
|
||||
context: {},
|
||||
store: {}
|
||||
}
|
||||
Sunstone.displayName = 'SunstoneApp'
|
||||
|
||||
Provision.displayName = 'SunstoneApp'
|
||||
|
||||
export default Provision
|
||||
export default Sunstone
|
||||
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Box } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo, useEffect, useRef, useState } from 'react'
|
||||
import { memo, useEffect, useRef, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Chip, Slide } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Button, CardActions } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Badge, Box, CardContent } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Typography } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Card, CardHeader, Fade, makeStyles } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Typography } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { NetworkAlt as NetworkIcon } from 'iconoir-react'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Card, CardContent, Button, CardActions } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo, useMemo } from 'react'
|
||||
import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Db as ProviderIcon, Cloud as ProvisionIcon } from 'iconoir-react'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo, useMemo } from 'react'
|
||||
import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Db as ProviderIcon, SettingsCloud as ProvisionIcon } from 'iconoir-react'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import useFetch from 'client/hooks/useFetch'
|
||||
@ -22,7 +22,6 @@ import { SubmitButton } from 'client/components/FormControl'
|
||||
const Action = memo(({
|
||||
cy,
|
||||
handleClick,
|
||||
icon,
|
||||
stopPropagation,
|
||||
...props
|
||||
}) => {
|
||||
@ -33,9 +32,7 @@ const Action = memo(({
|
||||
return (
|
||||
<SubmitButton
|
||||
data-cy={cy}
|
||||
icon={!!icon}
|
||||
isSubmitting={loading}
|
||||
label={icon}
|
||||
onClick={evt => {
|
||||
stopPropagation && evt?.stopPropagation?.()
|
||||
fetchRequest()
|
||||
@ -49,7 +46,7 @@ const Action = memo(({
|
||||
Action.propTypes = {
|
||||
cy: PropTypes.string,
|
||||
handleClick: PropTypes.func.isRequired,
|
||||
icon: PropTypes.node.isRequired,
|
||||
icon: PropTypes.node,
|
||||
stopPropagation: PropTypes.bool
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import clsx from 'clsx'
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Button, CardActions, Badge } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ViewGrid as VmIcon } from 'iconoir-react'
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import clsx from 'clsx'
|
||||
@ -76,7 +76,7 @@ const useStyles = makeStyles(theme => {
|
||||
}
|
||||
})
|
||||
|
||||
const WavesCard = React.memo(({ text, value, bgColor, icon: Icon }) => {
|
||||
const WavesCard = memo(({ text, value, bgColor, icon: Icon }) => {
|
||||
const classes = useStyles({ bgColor })
|
||||
|
||||
return (
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { memo, useState, useEffect, JSXElementConstructor } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Box, CircularProgress, Typography } from '@material-ui/core'
|
||||
@ -24,12 +24,12 @@ import NumberEasing from 'client/components/NumberEasing'
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {string} props.color - Color of component: primary, secondary or inherit
|
||||
* @returns {React.JSXElementConstructor} Circular progress bar component
|
||||
* @returns {JSXElementConstructor} Circular progress bar component
|
||||
*/
|
||||
const Circle = React.memo(({ color = 'secondary' }) => {
|
||||
const [progress, setProgress] = React.useState(0)
|
||||
const Circle = memo(({ color = 'secondary' }) => {
|
||||
const [progress, setProgress] = useState(0)
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setProgress(prevProgress => {
|
||||
const nextProgress = prevProgress + 2
|
||||
@ -65,9 +65,9 @@ Circle.displayName = 'Circle'
|
||||
* @param {object} props - Props
|
||||
* @param {string} props.label - Text in the middle
|
||||
* @param {object} props.labelProps - Props of text
|
||||
* @returns {React.JSXElementConstructor} Circular chart component
|
||||
* @returns {JSXElementConstructor} Circular chart component
|
||||
*/
|
||||
const CircleChart = React.memo(({ label, labelProps }) => (
|
||||
const CircleChart = memo(({ label, labelProps }) => (
|
||||
<Box position='relative' display='inline-flex' width={1}>
|
||||
<Box display='flex' flexDirection='column' alignItems='center' width={1}>
|
||||
<Circle />
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Tooltip } from '@material-ui/core'
|
||||
@ -48,9 +48,9 @@ const useStyles = makeStyles(theme => ({
|
||||
* @param {{ name: string, color: string }[]} props.legend - Legend
|
||||
* @param {number[]} props.data - Chart data
|
||||
* @param {number} props.total - Total value of chart, equals to 100% of bar
|
||||
* @returns {React.JSXElementConstructor} Chart bar component
|
||||
* @returns {JSXElementConstructor} Chart bar component
|
||||
*/
|
||||
const SingleBar = ({ legend, data, total }) => {
|
||||
const SingleBar = ({ legend, data, total = 0 }) => {
|
||||
const fragments = data.map(data => Math.floor(data * 10 / (total || 1)))
|
||||
|
||||
const classes = useStyles({ fragments })
|
||||
@ -101,12 +101,6 @@ SingleBar.propTypes = {
|
||||
total: PropTypes.number
|
||||
}
|
||||
|
||||
SingleBar.defaultProps = {
|
||||
legend: undefined,
|
||||
data: undefined,
|
||||
total: 0
|
||||
}
|
||||
|
||||
SingleBar.displayName = 'SingleBar'
|
||||
|
||||
export default SingleBar
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Paper, Divider } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { useEffect, useState, memo } from 'react'
|
||||
import { useEffect, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo, useState } from 'react'
|
||||
import { memo, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import clsx from 'clsx'
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import Message from 'client/components/DebugLog/message'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
@ -71,8 +71,14 @@ const DialogConfirmation = memo(
|
||||
>
|
||||
<DialogTitle disableTypography className={classes.title}>
|
||||
<div className={classes.titleText}>
|
||||
<Typography variant='h6'>{Tr(title)}</Typography>
|
||||
{subheader && <Typography variant='subtitle1'>{Tr(subheader)}</Typography>}
|
||||
<Typography variant='h6'>
|
||||
{typeof title === 'string' ? Tr(title) : title}
|
||||
</Typography>
|
||||
{subheader && (
|
||||
<Typography variant='subtitle1'>
|
||||
{typeof subheader === 'string' ? Tr(subheader) : subheader}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
{handleCancel && (
|
||||
<IconButton
|
||||
@ -85,16 +91,18 @@ const DialogConfirmation = memo(
|
||||
</IconButton>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogContent dividers {...contentProps}>
|
||||
{children}
|
||||
</DialogContent>
|
||||
{children && (
|
||||
<DialogContent dividers {...contentProps}>
|
||||
{children}
|
||||
</DialogContent>
|
||||
)}
|
||||
{handleAccept && (
|
||||
<DialogActions>
|
||||
<Action
|
||||
aria-label='accept'
|
||||
color='secondary'
|
||||
data-cy='dg-accept-button'
|
||||
handleClick={handleAccept}
|
||||
icon={false}
|
||||
label={T.Accept}
|
||||
{...acceptButtonProps}
|
||||
/>
|
||||
@ -106,19 +114,19 @@ const DialogConfirmation = memo(
|
||||
)
|
||||
|
||||
export const DialogPropTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
subheader: PropTypes.string,
|
||||
contentProps: PropTypes.objectOf(PropTypes.any),
|
||||
handleAccept: PropTypes.func,
|
||||
acceptButtonProps: PropTypes.objectOf(PropTypes.any),
|
||||
handleCancel: PropTypes.func,
|
||||
cancelButtonProps: PropTypes.objectOf(PropTypes.any),
|
||||
handleEntering: PropTypes.func,
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
open: PropTypes.bool,
|
||||
title: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.node
|
||||
])
|
||||
]).isRequired,
|
||||
subheader: PropTypes.string,
|
||||
contentProps: PropTypes.object,
|
||||
handleAccept: PropTypes.func,
|
||||
acceptButtonProps: PropTypes.object,
|
||||
handleCancel: PropTypes.func,
|
||||
cancelButtonProps: PropTypes.object,
|
||||
handleEntering: PropTypes.func,
|
||||
children: PropTypes.any
|
||||
}
|
||||
|
||||
DialogConfirmation.propTypes = DialogPropTypes
|
||||
|
@ -14,13 +14,12 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import clsx from 'clsx'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import { useForm, FormProvider } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers'
|
||||
import { yupResolver } from '@hookform/resolvers/yup'
|
||||
|
||||
import DialogConfirmation, { DialogPropTypes } from 'client/components/Dialogs/DialogConfirmation'
|
||||
|
||||
@ -63,6 +62,9 @@ const DialogForm = ({ values, resolver, handleSubmit, dialogProps, children }) =
|
||||
acceptButtonProps={{
|
||||
isSubmitting: methods.formState.isSubmitting
|
||||
}}
|
||||
cancelButtonProps={{
|
||||
disabled: methods.formState.isSubmitting
|
||||
}}
|
||||
{...dialogProps}
|
||||
>
|
||||
<FormProvider {...methods}>
|
||||
@ -79,7 +81,7 @@ DialogForm.propTypes = {
|
||||
]),
|
||||
resolver: PropTypes.func.isRequired,
|
||||
handleSubmit: PropTypes.func,
|
||||
dialogProps: DialogPropTypes,
|
||||
dialogProps: PropTypes.shape(DialogPropTypes),
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useEffect } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import clsx from 'clsx'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import clsx from 'clsx'
|
||||
@ -29,7 +29,7 @@ const useStyles = makeStyles(theme => ({
|
||||
}
|
||||
}))
|
||||
|
||||
const FloatingActionButton = React.memo(
|
||||
const FloatingActionButton = memo(
|
||||
({ icon, className, ...props }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
|
||||
import { Box, Link, Typography } from '@material-ui/core'
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { TextField, Chip } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import { string } from 'prop-types'
|
||||
|
||||
import { Box, makeStyles, Typography } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo, useState, useRef, useEffect } from 'react'
|
||||
import { memo, useState, useRef, useEffect, ChangeEvent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import clsx from 'clsx'
|
||||
|
||||
@ -78,7 +78,7 @@ const FileController = memo(
|
||||
/**
|
||||
* Handle change to validate the files.
|
||||
*
|
||||
* @param {React.ChangeEvent} event - Change event object
|
||||
* @param {ChangeEvent} event - Change event object
|
||||
*/
|
||||
const handleChange = async event => {
|
||||
try {
|
||||
|
@ -14,53 +14,80 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import { Box } from '@material-ui/core'
|
||||
import AceEditor from 'react-ace'
|
||||
import { useEffect, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import 'ace-builds/src-noconflict/mode-json'
|
||||
import 'ace-builds/src-noconflict/theme-github'
|
||||
import loadable from '@loadable/component'
|
||||
|
||||
const InputCode = ({ code, language, ...props }) => {
|
||||
const handleChange = newValue => {
|
||||
console.log('change', newValue)
|
||||
}
|
||||
const Ace = loadable.lib(() => import('react-ace'), { ssr: false })
|
||||
|
||||
return (
|
||||
<Box height="100%" minHeight={200}>
|
||||
<AceEditor
|
||||
style={{ border: '1px solid lightgray' }}
|
||||
wrapEnabled
|
||||
value={code}
|
||||
fontSize={16}
|
||||
mode="json"
|
||||
theme="github"
|
||||
width="100%"
|
||||
height="100%"
|
||||
// maxLines={Infinity}
|
||||
minLines={10}
|
||||
onChange={handleChange}
|
||||
name="form-control-code"
|
||||
showPrintMargin={false}
|
||||
editorProps={{ $blockScrolling: true }}
|
||||
setOptions={{
|
||||
useWorker: false,
|
||||
tabSize: 2
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
const WrapperToLoadMode = ({ children, mode }) => {
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
await import(`ace-builds/src-noconflict/mode-${mode}`)
|
||||
await import('ace-builds/src-noconflict/theme-github')
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
load()
|
||||
|
||||
return () => {
|
||||
// remove all styles when component will be unmounted
|
||||
document
|
||||
.querySelectorAll('[id^=ace]')
|
||||
.forEach(child => child.parentNode.removeChild(child))
|
||||
}
|
||||
}, [])
|
||||
|
||||
return loading ? null : children
|
||||
}
|
||||
|
||||
const InputCode = ({ code, mode, ...props }) => (
|
||||
<Ace>
|
||||
{({ default: Editor }) => (
|
||||
<WrapperToLoadMode mode={mode}>
|
||||
<Editor
|
||||
style={{ border: '1px solid lightgray' }}
|
||||
wrapEnabled
|
||||
value={code}
|
||||
fontSize={16}
|
||||
mode={mode}
|
||||
theme="github"
|
||||
width="100%"
|
||||
height="100%"
|
||||
// maxLines={Infinity}
|
||||
minLines={10}
|
||||
name="form-control-code"
|
||||
showPrintMargin={false}
|
||||
editorProps={{ $blockScrolling: true }}
|
||||
setOptions={{
|
||||
useWorker: false,
|
||||
tabSize: 2
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
</WrapperToLoadMode>
|
||||
)}
|
||||
</Ace>
|
||||
)
|
||||
|
||||
InputCode.propTypes = {
|
||||
code: PropTypes.string,
|
||||
language: PropTypes.string
|
||||
mode: PropTypes.oneOf([
|
||||
'json',
|
||||
'apache_conf',
|
||||
'css',
|
||||
'dockerfile',
|
||||
'markdown',
|
||||
'xml'
|
||||
])
|
||||
}
|
||||
|
||||
InputCode.defaultProps = {
|
||||
code: '',
|
||||
language: 'json'
|
||||
mode: 'json'
|
||||
}
|
||||
|
||||
export default InputCode
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo, useState, useCallback } from 'react'
|
||||
import { memo, useState, useCallback } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { InputAdornment, IconButton } from '@material-ui/core'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { TextField } from '@material-ui/core'
|
||||
@ -32,7 +32,22 @@ const SelectController = memo(
|
||||
<TextField
|
||||
value={optionSelected ?? defaultValue}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
onChange={
|
||||
multiple
|
||||
? event => {
|
||||
const { options } = event.target
|
||||
const newValue = []
|
||||
|
||||
for (let i = 0, l = options.length; i < l; i += 1) {
|
||||
if (options[i].selected) {
|
||||
newValue.push(options[i].value)
|
||||
}
|
||||
}
|
||||
|
||||
onChange(newValue)
|
||||
}
|
||||
: onChange
|
||||
}
|
||||
color='secondary'
|
||||
select
|
||||
fullWidth
|
||||
@ -55,6 +70,7 @@ const SelectController = memo(
|
||||
)}
|
||||
name={name}
|
||||
control={control}
|
||||
multiple={multiple}
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Typography, TextField, Slider, FormHelperText, Grid } from '@material-ui/core'
|
||||
|
@ -13,13 +13,20 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { forwardRef, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, CircularProgress, Button, IconButton } from '@material-ui/core'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import {
|
||||
makeStyles,
|
||||
CircularProgress,
|
||||
Button,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Typography
|
||||
} from '@material-ui/core'
|
||||
|
||||
import { Tr, ConditionalWrap } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
@ -34,37 +41,60 @@ const useStyles = makeStyles(theme => ({
|
||||
}
|
||||
}))
|
||||
|
||||
const ButtonComponent = ({ icon, children, ...props }) => icon ? (
|
||||
<IconButton {...props}>{children}</IconButton>
|
||||
) : (
|
||||
<Button type='submit' variant='contained' {...props}>
|
||||
{children}
|
||||
</Button>
|
||||
)
|
||||
|
||||
ButtonComponent.propTypes = {
|
||||
icon: PropTypes.bool,
|
||||
children: PropTypes.any
|
||||
}
|
||||
|
||||
const SubmitButton = React.memo(
|
||||
({ isSubmitting, disabled, label, icon, color, size, className, ...props }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<ButtonComponent
|
||||
className={clsx(classes.root, className, {
|
||||
[classes.disabled]: disabled
|
||||
})}
|
||||
color={color}
|
||||
disabled={disabled || isSubmitting}
|
||||
icon={icon}
|
||||
const ButtonComponent = forwardRef(
|
||||
({ icon, endicon, children, size = 'small', ...props }, ref) =>
|
||||
icon ? (
|
||||
<IconButton ref={ref} {...props}>{children}</IconButton>
|
||||
) : (
|
||||
<Button ref={ref}
|
||||
type='submit'
|
||||
endIcon={endicon}
|
||||
variant='contained'
|
||||
size={size}
|
||||
{...props}
|
||||
>
|
||||
{isSubmitting && <CircularProgress color='secondary' size={24} />}
|
||||
{!isSubmitting && (label ?? Tr(T.Submit))}
|
||||
</ButtonComponent>
|
||||
{children}
|
||||
</Button>
|
||||
)
|
||||
)
|
||||
|
||||
const TooltipComponent = ({ tooltip, tooltipProps, children }) => (
|
||||
<ConditionalWrap
|
||||
condition={tooltip && tooltip !== ''}
|
||||
wrap={wrapperChildren => (
|
||||
<Tooltip
|
||||
arrow
|
||||
placement='bottom'
|
||||
title={<Typography variant='subtitle2'>{tooltip}</Typography>}
|
||||
{...tooltipProps}
|
||||
>{wrapperChildren}</Tooltip>
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</ConditionalWrap>
|
||||
)
|
||||
|
||||
const SubmitButton = memo(
|
||||
({ isSubmitting, disabled, label, icon, className, ...props }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<TooltipComponent {...props}>
|
||||
<ButtonComponent
|
||||
className={clsx(
|
||||
classes.root,
|
||||
className,
|
||||
{ [classes.disabled]: disabled }
|
||||
)}
|
||||
disabled={disabled || isSubmitting}
|
||||
icon={icon}
|
||||
aria-label={label ?? T.Submit}
|
||||
{...props}
|
||||
>
|
||||
{isSubmitting && <CircularProgress color='secondary' size={24} />}
|
||||
{!isSubmitting && (icon ?? label ?? Tr(T.Submit))}
|
||||
</ButtonComponent>
|
||||
</TooltipComponent>
|
||||
)
|
||||
},
|
||||
(prev, next) =>
|
||||
@ -74,9 +104,13 @@ const SubmitButton = React.memo(
|
||||
prev.onClick === next.onClick
|
||||
)
|
||||
|
||||
SubmitButton.propTypes = {
|
||||
icon: PropTypes.bool,
|
||||
export const SubmitButtonPropTypes = {
|
||||
children: PropTypes.any,
|
||||
icon: PropTypes.node,
|
||||
endicon: PropTypes.node,
|
||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
tooltip: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
tooltipProps: PropTypes.object,
|
||||
isSubmitting: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
@ -84,13 +118,11 @@ SubmitButton.propTypes = {
|
||||
size: PropTypes.oneOf(['large', 'medium', 'small'])
|
||||
}
|
||||
|
||||
SubmitButton.defaultProps = {
|
||||
icon: false,
|
||||
label: undefined,
|
||||
isSubmitting: false,
|
||||
disabled: false,
|
||||
className: undefined,
|
||||
color: 'default'
|
||||
}
|
||||
TooltipComponent.propTypes = SubmitButtonPropTypes
|
||||
SubmitButton.propTypes = SubmitButtonPropTypes
|
||||
ButtonComponent.propTypes = SubmitButtonPropTypes
|
||||
|
||||
ButtonComponent.displayName = 'SubmitButtonComponent'
|
||||
SubmitButton.displayName = 'SubmitButton'
|
||||
|
||||
export default SubmitButton
|
||||
|
@ -13,14 +13,14 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { 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 { ErrorHelper } from 'client/components/FormControl'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const TextController = memo(
|
||||
({ control, cy, type, multiline, name, label, error, fieldProps }) => (
|
||||
@ -35,7 +35,7 @@ const TextController = memo(
|
||||
variant='outlined'
|
||||
margin='dense'
|
||||
{...(label && { label: Tr(label) })}
|
||||
inputProps={{ 'data-cy': cy }}
|
||||
inputProps={{ 'data-cy': cy, ...fieldProps }}
|
||||
error={Boolean(error)}
|
||||
helperText={Boolean(error) && <ErrorHelper label={error?.message} />}
|
||||
FormHelperTextProps={{ 'data-cy': `${cy}-error` }}
|
||||
@ -48,7 +48,9 @@ const TextController = memo(
|
||||
/>
|
||||
),
|
||||
(prevProps, nextProps) =>
|
||||
prevProps.error === nextProps.error && prevProps.type === nextProps.type
|
||||
prevProps.error === nextProps.error &&
|
||||
prevProps.type === nextProps.type &&
|
||||
prevProps.label === nextProps.label
|
||||
)
|
||||
|
||||
TextController.propTypes = {
|
||||
@ -62,7 +64,14 @@ TextController.propTypes = {
|
||||
PropTypes.bool,
|
||||
PropTypes.objectOf(PropTypes.any)
|
||||
]),
|
||||
fieldProps: PropTypes.object
|
||||
fieldProps: PropTypes.object,
|
||||
formContext: PropTypes.shape({
|
||||
setValue: PropTypes.func,
|
||||
setError: PropTypes.func,
|
||||
clearErrors: PropTypes.func,
|
||||
watch: PropTypes.func,
|
||||
register: PropTypes.func
|
||||
})
|
||||
}
|
||||
|
||||
TextController.defaultProps = {
|
||||
|
@ -0,0 +1,87 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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 { 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 { ErrorHelper } from 'client/components/FormControl'
|
||||
|
||||
const TimeController = memo(
|
||||
({ control, cy, name, type, label, error, fieldProps }) => (
|
||||
<Controller
|
||||
render={({ value, ...props }) =>
|
||||
<TextField
|
||||
{...props}
|
||||
fullWidth
|
||||
color='secondary'
|
||||
value={value}
|
||||
variant='outlined'
|
||||
margin='dense'
|
||||
{...(label && { label: Tr(label) })}
|
||||
type={type}
|
||||
inputProps={{ 'data-cy': cy, ...fieldProps }}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
error={Boolean(error)}
|
||||
helperText={Boolean(error) && <ErrorHelper label={error?.message} />}
|
||||
FormHelperTextProps={{ 'data-cy': `${cy}-error` }}
|
||||
/>
|
||||
}
|
||||
name={name}
|
||||
control={control}
|
||||
/>
|
||||
),
|
||||
(prevProps, nextProps) =>
|
||||
prevProps.error === nextProps.error &&
|
||||
prevProps.label === nextProps.label
|
||||
)
|
||||
|
||||
TimeController.propTypes = {
|
||||
control: PropTypes.object,
|
||||
cy: PropTypes.string,
|
||||
multiline: PropTypes.bool,
|
||||
name: PropTypes.string.isRequired,
|
||||
label: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
error: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.objectOf(PropTypes.any)
|
||||
]),
|
||||
fieldProps: PropTypes.object,
|
||||
formContext: PropTypes.shape({
|
||||
setValue: PropTypes.func,
|
||||
setError: PropTypes.func,
|
||||
clearErrors: PropTypes.func,
|
||||
watch: PropTypes.func,
|
||||
register: PropTypes.func
|
||||
})
|
||||
}
|
||||
|
||||
TimeController.defaultProps = {
|
||||
control: {},
|
||||
cy: 'cy',
|
||||
name: '',
|
||||
label: '',
|
||||
type: 'datetime-local',
|
||||
error: false,
|
||||
fieldProps: undefined
|
||||
}
|
||||
|
||||
TimeController.displayName = 'TimeController'
|
||||
|
||||
export default TimeController
|
@ -13,28 +13,31 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import TextController from 'client/components/FormControl/TextController'
|
||||
import AutocompleteController from 'client/components/FormControl/AutocompleteController'
|
||||
import CheckboxController from 'client/components/FormControl/CheckboxController'
|
||||
import FileController from 'client/components/FormControl/FileController'
|
||||
import PasswordController from 'client/components/FormControl/PasswordController'
|
||||
import SelectController from 'client/components/FormControl/SelectController'
|
||||
import SliderController from 'client/components/FormControl/SliderController'
|
||||
import CheckboxController from 'client/components/FormControl/CheckboxController'
|
||||
import AutocompleteController from 'client/components/FormControl/AutocompleteController'
|
||||
import FileController from 'client/components/FormControl/FileController'
|
||||
import TextController from 'client/components/FormControl/TextController'
|
||||
import TimeController from 'client/components/FormControl/TimeController'
|
||||
|
||||
import SubmitButton from 'client/components/FormControl/SubmitButton'
|
||||
import SubmitButton, { SubmitButtonPropTypes } from 'client/components/FormControl/SubmitButton'
|
||||
import InputCode from 'client/components/FormControl/InputCode'
|
||||
import ErrorHelper from 'client/components/FormControl/ErrorHelper'
|
||||
|
||||
export {
|
||||
TextController,
|
||||
AutocompleteController,
|
||||
CheckboxController,
|
||||
FileController,
|
||||
PasswordController,
|
||||
SelectController,
|
||||
SliderController,
|
||||
CheckboxController,
|
||||
AutocompleteController,
|
||||
FileController,
|
||||
TextController,
|
||||
TimeController,
|
||||
|
||||
SubmitButton,
|
||||
SubmitButtonPropTypes,
|
||||
InputCode,
|
||||
ErrorHelper
|
||||
}
|
||||
|
@ -14,11 +14,10 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Button, MobileStepper, Typography, Box } from '@material-ui/core'
|
||||
import { makeStyles, fade } from '@material-ui/core/styles'
|
||||
import { makeStyles, alpha } from '@material-ui/core/styles'
|
||||
import {
|
||||
NavArrowLeft as PreviousIcon,
|
||||
NavArrowRight as NextIcon
|
||||
@ -31,7 +30,7 @@ const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
position: 'sticky',
|
||||
top: -15,
|
||||
background: fade(theme.palette.primary.light, 0.65),
|
||||
background: alpha(theme.palette.primary.light, 0.65),
|
||||
zIndex: theme.zIndex.mobileStepper,
|
||||
margin: theme.spacing(2, 0)
|
||||
},
|
||||
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
@ -26,7 +25,7 @@ import {
|
||||
Typography,
|
||||
StepButton
|
||||
} from '@material-ui/core'
|
||||
import { makeStyles, fade } from '@material-ui/core/styles'
|
||||
import { makeStyles, alpha } from '@material-ui/core/styles'
|
||||
|
||||
import { SubmitButton } from 'client/components/FormControl'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
@ -37,7 +36,7 @@ const useStyles = makeStyles(theme => ({
|
||||
position: 'sticky',
|
||||
top: -15,
|
||||
minHeight: 100,
|
||||
background: fade(theme.palette.background.paper, 0.95),
|
||||
background: alpha(theme.palette.background.paper, 0.95),
|
||||
zIndex: theme.zIndex.mobileStepper
|
||||
},
|
||||
icon: {
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useState, useMemo, useCallback, useEffect } from 'react'
|
||||
import { useState, useMemo, useCallback, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
|
@ -14,11 +14,10 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
Button,
|
||||
ClickAwayListener,
|
||||
Grow,
|
||||
Paper,
|
||||
@ -29,27 +28,43 @@ import {
|
||||
import { NavArrowDown } from 'iconoir-react'
|
||||
|
||||
import { useDialog } from 'client/hooks'
|
||||
import { DialogForm } from 'client/components/Dialogs'
|
||||
import { FormWithSchema } from 'client/components/Forms'
|
||||
import { DialogConfirmation, DialogForm, DialogPropTypes } from 'client/components/Dialogs'
|
||||
import { SubmitButton, SubmitButtonPropTypes } from 'client/components/FormControl'
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
import FormStepper from 'client/components/FormStepper'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
|
||||
const ButtonToTriggerForm = ({ buttonProps = {}, title, options = [] }) => {
|
||||
const ButtonToTriggerForm = ({
|
||||
buttonProps = {},
|
||||
isConfirmDialog = false,
|
||||
dialogProps = {},
|
||||
options = []
|
||||
}) => {
|
||||
const buttonId = buttonProps['data-cy'] ?? 'main-button-form'
|
||||
const isGroupButton = options.length > 1
|
||||
|
||||
const [anchorEl, setAnchorEl] = React.useState(null)
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
const open = Boolean(anchorEl)
|
||||
|
||||
const { display, show, hide, values: Form } = useDialog()
|
||||
const { steps, defaultValues, resolver, fields, onSubmit: handleSubmit } = Form ?? {}
|
||||
const {
|
||||
steps,
|
||||
defaultValues,
|
||||
resolver,
|
||||
fields,
|
||||
onSubmit: handleSubmit
|
||||
} = Form ?? {}
|
||||
|
||||
const handleTriggerSubmit = async formData => {
|
||||
await handleSubmit?.(formData)
|
||||
hide()
|
||||
try {
|
||||
await handleSubmit?.(formData)
|
||||
} finally {
|
||||
hide()
|
||||
}
|
||||
}
|
||||
|
||||
const openDialogForm = ({ form = {}, onSubmit }) => {
|
||||
show({ ...form, onSubmit })
|
||||
const openDialogForm = ({ form = {}, ...rest }) => {
|
||||
show({ ...form, ...rest })
|
||||
handleClose()
|
||||
}
|
||||
|
||||
@ -58,29 +73,24 @@ const ButtonToTriggerForm = ({ buttonProps = {}, title, options = [] }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
color='secondary'
|
||||
size='small'
|
||||
variant='contained'
|
||||
aria-describedby={buttonProps.cy ?? 'main-button-form'}
|
||||
<SubmitButton
|
||||
aria-describedby={buttonId}
|
||||
disabled={!options.length}
|
||||
endIcon={isGroupButton && <NavArrowDown />}
|
||||
endicon={isGroupButton ? <NavArrowDown /> : undefined}
|
||||
onClick={evt => !isGroupButton
|
||||
? openDialogForm(options[0])
|
||||
: handleToggle(evt)
|
||||
}
|
||||
{...buttonProps}
|
||||
>
|
||||
<Translate word={title} />
|
||||
</Button>
|
||||
/>
|
||||
|
||||
{isGroupButton && (
|
||||
<Popper
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
id={buttonProps.cy ?? 'main-button-form'}
|
||||
transition
|
||||
disablePortal
|
||||
id={buttonId}
|
||||
open={open}
|
||||
transition
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Grow {...TransitionProps}>
|
||||
@ -105,26 +115,39 @@ const ButtonToTriggerForm = ({ buttonProps = {}, title, options = [] }) => {
|
||||
)}
|
||||
|
||||
{display && (
|
||||
<DialogForm
|
||||
resolver={resolver}
|
||||
values={defaultValues}
|
||||
handleSubmit={!steps ? handleTriggerSubmit : undefined}
|
||||
dialogProps={{ title, handleCancel: hide }}
|
||||
>
|
||||
{steps ? (
|
||||
<FormStepper steps={steps} schema={resolver} onSubmit={handleTriggerSubmit}/>
|
||||
) : (
|
||||
<FormWithSchema cy='form-dg' fields={fields} />
|
||||
)}
|
||||
</DialogForm>
|
||||
isConfirmDialog ? (
|
||||
<DialogConfirmation
|
||||
handleAccept={handleTriggerSubmit}
|
||||
handleCancel={hide}
|
||||
{...dialogProps}
|
||||
/>
|
||||
) : (
|
||||
<DialogForm
|
||||
resolver={resolver}
|
||||
values={defaultValues}
|
||||
handleSubmit={!steps ? handleTriggerSubmit : undefined}
|
||||
dialogProps={{ handleCancel: hide, ...dialogProps }}
|
||||
>
|
||||
{steps ? (
|
||||
<FormStepper
|
||||
steps={steps}
|
||||
schema={resolver}
|
||||
onSubmit={handleTriggerSubmit}
|
||||
/>
|
||||
) : (
|
||||
<FormWithSchema cy='form-dg' fields={fields} />
|
||||
)}
|
||||
</DialogForm>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
ButtonToTriggerForm.propTypes = {
|
||||
buttonProps: PropTypes.object,
|
||||
title: PropTypes.string,
|
||||
buttonProps: PropTypes.shape(SubmitButtonPropTypes),
|
||||
dialogProps: PropTypes.shape(DialogPropTypes),
|
||||
isConfirmDialog: PropTypes.bool,
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
cy: PropTypes.string,
|
||||
@ -135,6 +158,6 @@ ButtonToTriggerForm.propTypes = {
|
||||
handleSubmit: PropTypes.func
|
||||
}
|
||||
|
||||
ButtonToTriggerForm.displayName = 'VmStorageTab'
|
||||
ButtonToTriggerForm.displayName = 'ButtonToTriggerForm'
|
||||
|
||||
export default ButtonToTriggerForm
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import { createElement } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Box, Grid } from '@material-ui/core'
|
||||
@ -31,11 +31,12 @@ const InputController = {
|
||||
[INPUT_TYPES.SLIDER]: FC.SliderController,
|
||||
[INPUT_TYPES.CHECKBOX]: FC.CheckboxController,
|
||||
[INPUT_TYPES.AUTOCOMPLETE]: FC.AutocompleteController,
|
||||
[INPUT_TYPES.FILE]: FC.FileController
|
||||
[INPUT_TYPES.FILE]: FC.FileController,
|
||||
[INPUT_TYPES.TIME]: FC.TimeController
|
||||
}
|
||||
|
||||
const HiddenInput = ({ isHidden, children }) =>
|
||||
isHidden ? <Box display="none">{children}</Box> : children
|
||||
isHidden ? <Box display='none'>{children}</Box> : children
|
||||
|
||||
const FormWithSchema = ({ id, cy, fields }) => {
|
||||
const { control, errors, ...formContext } = useFormContext()
|
||||
@ -43,37 +44,44 @@ const FormWithSchema = ({ id, cy, fields }) => {
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
{fields?.map?.(
|
||||
({ name, type, htmlType, values, dependOf, grid, ...restOfProps }) => {
|
||||
({ dependOf, ...props }) => {
|
||||
let valueOfDependField = null
|
||||
if (dependOf) {
|
||||
const nameOfDependField = id
|
||||
? Array.isArray(dependOf) ? dependOf.map(d => `${id}.${d}`) : `${id}.${dependOf}`
|
||||
: dependOf
|
||||
|
||||
valueOfDependField = useWatch({ control, name: nameOfDependField })
|
||||
}
|
||||
|
||||
const { name, type, htmlType, grid, ...fieldProps } = Object
|
||||
.entries(props)
|
||||
.reduce((field, property) => {
|
||||
const [key, value] = property
|
||||
const finalValue = typeof value === 'function' ? value(valueOfDependField) : value
|
||||
|
||||
return { ...field, [key]: finalValue }
|
||||
}, {})
|
||||
|
||||
const dataCy = `${cy}-${name}`
|
||||
const inputName = id ? `${id}.${name}` : name
|
||||
|
||||
const inputError = get(errors, inputName) ?? false
|
||||
|
||||
const dependValue = dependOf
|
||||
? useWatch({ control, name: id ? `${id}.${dependOf}` : dependOf })
|
||||
: null
|
||||
|
||||
const htmlTypeValue = typeof htmlType === 'function'
|
||||
? htmlType(dependValue)
|
||||
: htmlType
|
||||
|
||||
const isHidden = htmlTypeValue === INPUT_TYPES.HIDDEN
|
||||
const isHidden = htmlType === INPUT_TYPES.HIDDEN
|
||||
|
||||
return (
|
||||
InputController[type] && (
|
||||
<HiddenInput key={`${cy}-${name}`} isHidden={isHidden}>
|
||||
<Grid item xs={12} md={6} {...grid}>
|
||||
{React.createElement(InputController[type], {
|
||||
{createElement(InputController[type], {
|
||||
control,
|
||||
cy: dataCy,
|
||||
error: inputError,
|
||||
formContext,
|
||||
name: inputName,
|
||||
type: htmlTypeValue,
|
||||
values: typeof values === 'function'
|
||||
? values(dependValue)
|
||||
: values,
|
||||
...restOfProps
|
||||
type: htmlType,
|
||||
...fieldProps
|
||||
})}
|
||||
</Grid>
|
||||
</HiddenInput>
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useCallback } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useCallback } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import { useListForm } from 'client/hooks'
|
||||
import { ImagesTable } from 'client/components/Tables'
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useCallback } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useCallback } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useCallback } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useCallback } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import { useListForm } from 'client/hooks'
|
||||
import { VNetworksTable } from 'client/components/Tables'
|
||||
|
@ -0,0 +1,27 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/CreateDiskSnapshotForm/schema'
|
||||
|
||||
const CreateDiskSnapshotForm = ({ snapshot } = {}) => {
|
||||
return {
|
||||
resolver: () => SCHEMA,
|
||||
defaultValues: SCHEMA.cast(snapshot, { stripUnknown: true }),
|
||||
fields: FIELDS
|
||||
}
|
||||
}
|
||||
|
||||
export default CreateDiskSnapshotForm
|
@ -0,0 +1,37 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as yup from 'yup'
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { getValidationFromFields } from 'client/utils'
|
||||
|
||||
const NAME = {
|
||||
name: 'NAME',
|
||||
label: 'Name',
|
||||
type: INPUT_TYPES.TEXT,
|
||||
tooltip: 'Name for the snapshot.',
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.notRequired()
|
||||
.default('')
|
||||
}
|
||||
|
||||
export const FIELDS = [
|
||||
NAME
|
||||
]
|
||||
|
||||
export const SCHEMA = yup.object(getValidationFromFields(FIELDS))
|
@ -0,0 +1,186 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as yup from 'yup'
|
||||
|
||||
import { INPUT_TYPES, VM_ACTIONS, VM_ACTIONS_WITH_SCHEDULE } from 'client/constants'
|
||||
import { getValidationFromFields, capitalize, clearString } from 'client/utils'
|
||||
import { getSnapshotList, getDisks } from 'client/models/VirtualMachine'
|
||||
|
||||
const ARGS_TYPES = {
|
||||
DISK_ID: 'DISK_ID',
|
||||
NAME: 'NAME',
|
||||
SNAPSHOT_ID: 'SNAPSHOT_ID'
|
||||
}
|
||||
|
||||
const SCHED_ACTION_OPTIONS = VM_ACTIONS_WITH_SCHEDULE
|
||||
.map(action => ({
|
||||
text: capitalize(clearString(action)),
|
||||
value: action
|
||||
}))
|
||||
|
||||
const ARGS_BY_ACTION = action => {
|
||||
const { DISK_ID, NAME, SNAPSHOT_ID } = ARGS_TYPES
|
||||
|
||||
return {
|
||||
[VM_ACTIONS.SNAPSHOT_DISK_CREATE]: [DISK_ID, NAME],
|
||||
[VM_ACTIONS.SNAPSHOT_DISK_REVERT]: [DISK_ID, SNAPSHOT_ID],
|
||||
[VM_ACTIONS.SNAPSHOT_DISK_DELETE]: [DISK_ID, SNAPSHOT_ID],
|
||||
[VM_ACTIONS.SNAPSHOT_CREATE]: [NAME],
|
||||
[VM_ACTIONS.SNAPSHOT_REVERT]: [SNAPSHOT_ID],
|
||||
[VM_ACTIONS.SNAPSHOT_DELETE]: [SNAPSHOT_ID]
|
||||
}[action]
|
||||
}
|
||||
|
||||
export const ACTION_FIELD = {
|
||||
name: 'ACTION',
|
||||
label: 'Action',
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: SCHED_ACTION_OPTIONS,
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.required('Action field is required')
|
||||
.default(SCHED_ACTION_OPTIONS[0]?.value),
|
||||
grid: { xs: 12 }
|
||||
}
|
||||
|
||||
export const ARGS_DISK_ID_FIELD = vm => {
|
||||
const diskOptions = getDisks(vm)
|
||||
.map(({ DISK_ID, IMAGE }) => ({ text: IMAGE, value: DISK_ID }))
|
||||
|
||||
return {
|
||||
name: ARGS_TYPES.DISK_ID,
|
||||
label: 'Disk',
|
||||
type: INPUT_TYPES.SELECT,
|
||||
dependOf: ACTION_FIELD.name,
|
||||
htmlType: action => ARGS_BY_ACTION(action)?.includes(ARGS_TYPES.DISK_ID)
|
||||
? undefined
|
||||
: INPUT_TYPES.HIDDEN,
|
||||
values: diskOptions,
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when(
|
||||
ACTION_FIELD.name,
|
||||
(action, schema) => ARGS_BY_ACTION(action)?.includes(ARGS_TYPES.DISK_ID)
|
||||
? schema
|
||||
: schema.strip()
|
||||
)
|
||||
.notRequired()
|
||||
.default(() => diskOptions[0]?.value)
|
||||
}
|
||||
}
|
||||
|
||||
export const ARGS_NAME_FIELD = () => ({
|
||||
name: ARGS_TYPES.NAME,
|
||||
label: 'Snapshot name',
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: ACTION_FIELD.name,
|
||||
htmlType: action => ARGS_BY_ACTION(action)?.includes(ARGS_TYPES.NAME)
|
||||
? undefined
|
||||
: INPUT_TYPES.HIDDEN,
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when(
|
||||
ACTION_FIELD.name,
|
||||
(action, schema) => ARGS_BY_ACTION(action)?.includes(ARGS_TYPES.NAME)
|
||||
? schema
|
||||
: schema.strip()
|
||||
)
|
||||
.notRequired()
|
||||
.default(undefined)
|
||||
})
|
||||
|
||||
export const ARGS_SNAPSHOT_ID_FIELD = vm => {
|
||||
const snapshotOptions = getSnapshotList(vm)
|
||||
.map(({ SNAPSHOT_ID, NAME }) => ({ text: NAME, value: SNAPSHOT_ID }))
|
||||
|
||||
return {
|
||||
name: ARGS_TYPES.SNAPSHOT_ID,
|
||||
label: 'Snapshot',
|
||||
type: INPUT_TYPES.SELECT,
|
||||
dependOf: ACTION_FIELD.name,
|
||||
htmlType: action => ARGS_BY_ACTION(action)?.includes(ARGS_TYPES.SNAPSHOT_ID)
|
||||
? undefined
|
||||
: INPUT_TYPES.HIDDEN,
|
||||
values: snapshotOptions,
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when(
|
||||
ACTION_FIELD.name,
|
||||
(action, schema) => ARGS_BY_ACTION(action)?.includes(ARGS_TYPES.SNAPSHOT_ID)
|
||||
? schema
|
||||
: schema.strip()
|
||||
)
|
||||
.notRequired()
|
||||
.default(() => snapshotOptions[0]?.value)
|
||||
}
|
||||
}
|
||||
|
||||
export const COMMON_FIELDS = vm => [
|
||||
ACTION_FIELD,
|
||||
ARGS_DISK_ID_FIELD,
|
||||
ARGS_NAME_FIELD,
|
||||
ARGS_SNAPSHOT_ID_FIELD
|
||||
].map(field => typeof field === 'function' ? field(vm) : field)
|
||||
|
||||
export const COMMON_SCHEMA = vm => yup
|
||||
.object(getValidationFromFields(COMMON_FIELDS(vm)))
|
||||
.transform(value => {
|
||||
const {
|
||||
ARGS,
|
||||
[ACTION_FIELD.name]: ACTION,
|
||||
[ARGS_TYPES.NAME]: NAME,
|
||||
[ARGS_TYPES.SNAPSHOT_ID]: SNAPSHOT_ID,
|
||||
[ARGS_TYPES.DISK_ID]: DISK_ID,
|
||||
...rest
|
||||
} = value
|
||||
|
||||
let argsValues = {}
|
||||
|
||||
if (ARGS) {
|
||||
// IMPORTANT - String data from ARGS has strict order: DISK_ID,NAME,SNAPSHOT_ID
|
||||
const splittedArgs = ARGS.split(',')
|
||||
|
||||
argsValues = {
|
||||
[VM_ACTIONS.SNAPSHOT_DISK_CREATE]:
|
||||
{ DISK_ID: splittedArgs[0], NAME: splittedArgs[1] },
|
||||
[VM_ACTIONS.SNAPSHOT_DISK_REVERT]:
|
||||
{ DISK_ID: splittedArgs[0], SNAPSHOT_ID: splittedArgs[1] },
|
||||
[VM_ACTIONS.SNAPSHOT_DISK_DELETE]:
|
||||
{ DISK_ID: splittedArgs[0], SNAPSHOT_ID: splittedArgs[1] },
|
||||
[VM_ACTIONS.SNAPSHOT_CREATE]:
|
||||
{ NAME: splittedArgs[0] },
|
||||
[VM_ACTIONS.SNAPSHOT_REVERT]:
|
||||
{ SNAPSHOT_ID: splittedArgs[0] },
|
||||
[VM_ACTIONS.SNAPSHOT_DELETE]:
|
||||
{ SNAPSHOT_ID: splittedArgs[0] }
|
||||
}[ACTION]
|
||||
} else {
|
||||
// Transform to send form data
|
||||
const argsAsString = Object.entries({ DISK_ID, NAME, SNAPSHOT_ID })
|
||||
.map(([name, val]) => ARGS_BY_ACTION(ACTION)?.includes(name) && val)
|
||||
.filter(Boolean)
|
||||
.join(',')
|
||||
|
||||
argsValues = { ARGS: argsAsString }
|
||||
}
|
||||
|
||||
return { ...rest, ACTION, ...argsValues }
|
||||
})
|
@ -0,0 +1,27 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/CreateSchedActionForm/PunctualForm/schema'
|
||||
|
||||
const PunctualForm = ({ vm, schedule } = {}) => ({
|
||||
resolver: () => SCHEMA(vm),
|
||||
defaultValues: schedule
|
||||
? SCHEMA(vm).cast(schedule, { stripUnknown: true })
|
||||
: SCHEMA(vm).default(),
|
||||
fields: FIELDS(vm)
|
||||
})
|
||||
|
||||
export default PunctualForm
|
@ -0,0 +1,254 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as yup from 'yup'
|
||||
import { DateTime } from 'luxon'
|
||||
|
||||
import { T, INPUT_TYPES } from 'client/constants'
|
||||
import { getValidationFromFields, capitalize } from 'client/utils'
|
||||
import { timeFromMilliseconds } from 'client/models/Helper'
|
||||
import { COMMON_FIELDS, COMMON_SCHEMA } from 'client/components/Forms/Vm/CreateSchedActionForm/CommonSchema'
|
||||
|
||||
const ISO_FORMAT = "yyyy-MM-dd'T'HH:mm"
|
||||
const ISO_REG = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/
|
||||
const MONTH_DAYS_REG = /^(3[01]|[12][0-9]|[1-9])(,(3[01]|[12][0-9]|[1-9]))*$/
|
||||
const YEAR_DAYS_REG = /^(36[0-5]|3[0-5]\d|[12]\d{2}|[0-9]\d?)(,(36[0-5]|3[0-5]\d|[12]\d{2}|[1-9]\d?))*$/
|
||||
const HOURS_REG = /^(16[0-8]|1[01][0-9]|[1-9]?[0-9])$/
|
||||
|
||||
const REPEAT_VALUES = {
|
||||
WEEKLY: '0',
|
||||
MONTHLY: '1',
|
||||
YEARLY: '2',
|
||||
HOURLY: '3'
|
||||
}
|
||||
|
||||
const END_TYPE_VALUES = {
|
||||
NEVER: '0',
|
||||
REPETITION: '1',
|
||||
DATE: '2'
|
||||
}
|
||||
|
||||
const REPEAT_OPTIONS = Object.entries(REPEAT_VALUES)
|
||||
.map(([text, value]) => ({ text: capitalize(text), value }))
|
||||
|
||||
const END_TYPE_OPTIONS = Object.entries(END_TYPE_VALUES)
|
||||
.map(([text, value]) => ({ text: capitalize(text), value }))
|
||||
|
||||
const isoDateValidation = nameInput => yup
|
||||
.string()
|
||||
.trim()
|
||||
.default(() => DateTime.local().toFormat(ISO_FORMAT))
|
||||
.matches(
|
||||
ISO_REG,
|
||||
{ message: `${nameInput} should be a date with ISO format: yyyy-MM-ddTHH:mm` }
|
||||
)
|
||||
.transform((value, originalValue) => {
|
||||
if (value.length < 10 || (isNaN(value) && value.match(ISO_REG) === null)) {
|
||||
return value
|
||||
}
|
||||
|
||||
const newValue = isNaN(originalValue)
|
||||
? DateTime.fromISO(originalValue)
|
||||
: timeFromMilliseconds(originalValue)
|
||||
|
||||
return newValue.isValid ? newValue.toFormat(ISO_FORMAT) : originalValue
|
||||
})
|
||||
|
||||
const PERIODIC_FIELD = {
|
||||
name: 'PERIODIC',
|
||||
label: 'Periodic',
|
||||
type: INPUT_TYPES.CHECKBOX,
|
||||
validation: yup
|
||||
.boolean()
|
||||
.default(false),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
const TIME_FIELD = {
|
||||
name: 'TIME',
|
||||
label: 'Time',
|
||||
type: INPUT_TYPES.TIME,
|
||||
validation: yup
|
||||
.string()
|
||||
.required('Time field is required')
|
||||
.concat(isoDateValidation('Time'))
|
||||
}
|
||||
|
||||
const REPEAT_FIELD = {
|
||||
name: 'REPEAT',
|
||||
label: 'Periodicity',
|
||||
type: INPUT_TYPES.SELECT,
|
||||
dependOf: PERIODIC_FIELD.name,
|
||||
htmlType: isPeriodic => !isPeriodic ? INPUT_TYPES.HIDDEN : undefined,
|
||||
values: REPEAT_OPTIONS,
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when(
|
||||
PERIODIC_FIELD.name,
|
||||
(isPeriodic, schema) => isPeriodic ? schema : schema.strip()
|
||||
)
|
||||
.notRequired()
|
||||
.default(REPEAT_OPTIONS[0].value)
|
||||
}
|
||||
|
||||
const DAYS_FIELD = {
|
||||
name: 'DAYS',
|
||||
dependOf: [PERIODIC_FIELD.name, REPEAT_FIELD.name],
|
||||
multiple: (dependValues = {}) => {
|
||||
const { [REPEAT_FIELD.name]: repeat } = dependValues
|
||||
|
||||
return REPEAT_VALUES.WEEKLY === repeat
|
||||
},
|
||||
type: (dependValues = {}) => {
|
||||
const { [REPEAT_FIELD.name]: repeat } = dependValues
|
||||
|
||||
return REPEAT_VALUES.WEEKLY === repeat ? INPUT_TYPES.SELECT : INPUT_TYPES.TEXT
|
||||
},
|
||||
label: (dependValues = {}) => {
|
||||
const { [REPEAT_FIELD.name]: repeat } = dependValues
|
||||
|
||||
return {
|
||||
[REPEAT_VALUES.WEEKLY]: 'Days of week',
|
||||
[REPEAT_VALUES.MONTHLY]: 'Days of month',
|
||||
[REPEAT_VALUES.YEARLY]: 'Days of year',
|
||||
[REPEAT_VALUES.HOURLY]: "Each 'x' hours"
|
||||
}[repeat]
|
||||
},
|
||||
values: [
|
||||
{ text: T.Sunday, value: '0' },
|
||||
{ text: T.Monday, value: '1' },
|
||||
{ text: T.Tuesday, value: '2' },
|
||||
{ text: T.Wednesday, value: '3' },
|
||||
{ text: T.Thursday, value: '4' },
|
||||
{ text: T.Friday, value: '5' },
|
||||
{ text: T.Saturday, value: '6' }
|
||||
],
|
||||
htmlType: (dependValues = {}) => {
|
||||
const { [PERIODIC_FIELD.name]: periodic, [REPEAT_FIELD.name]: repeat } = dependValues
|
||||
|
||||
if (!periodic) return INPUT_TYPES.HIDDEN
|
||||
|
||||
return REPEAT_VALUES.HOURLY === repeat ? 'number' : undefined
|
||||
},
|
||||
validation: yup
|
||||
.string()
|
||||
.default(undefined)
|
||||
.when(
|
||||
REPEAT_FIELD.name,
|
||||
(repeatType, schema) => ({
|
||||
[REPEAT_VALUES.WEEKLY]: schema
|
||||
.transform(value => Array.isArray(value) ? value.join(',') : value)
|
||||
.required('Days field is required: between 0 (Sunday) and 6 (Saturday)'),
|
||||
[REPEAT_VALUES.MONTHLY]: schema
|
||||
.trim()
|
||||
.matches(MONTH_DAYS_REG, { message: 'Days should be between 1 and 31' })
|
||||
.required('Days field is required: between 1 and 31'),
|
||||
[REPEAT_VALUES.YEARLY]: schema
|
||||
.trim()
|
||||
.matches(YEAR_DAYS_REG, { message: 'Days should be between 0 and 365' })
|
||||
.required('Days field is required: between 0 and 365'),
|
||||
[REPEAT_VALUES.HOURLY]: schema
|
||||
.trim()
|
||||
.matches(HOURS_REG, { message: 'Hours should be between 0 and 168' })
|
||||
.required('Hours field is required: between 0 and 168')
|
||||
}[repeatType])
|
||||
),
|
||||
fieldProps: { min: 0, max: 168, step: 1 }
|
||||
}
|
||||
|
||||
const END_TYPE_FIELD = {
|
||||
name: 'END_TYPE',
|
||||
label: 'End type',
|
||||
type: INPUT_TYPES.SELECT,
|
||||
dependOf: PERIODIC_FIELD.name,
|
||||
htmlType: isPeriodic => !isPeriodic ? INPUT_TYPES.HIDDEN : undefined,
|
||||
values: END_TYPE_OPTIONS,
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.when(
|
||||
PERIODIC_FIELD.name,
|
||||
(isPeriodic, schema) => isPeriodic ? schema : schema.strip()
|
||||
)
|
||||
.notRequired()
|
||||
.default(END_TYPE_OPTIONS[0].value)
|
||||
}
|
||||
|
||||
const END_VALUE_FIELD = {
|
||||
name: 'END_VALUE',
|
||||
label: 'When you want that the action finishes',
|
||||
dependOf: [PERIODIC_FIELD.name, END_TYPE_FIELD.name],
|
||||
type: (dependValues = {}) => {
|
||||
const { [END_TYPE_FIELD.name]: endType } = dependValues
|
||||
|
||||
return endType === END_TYPE_VALUES.DATE ? INPUT_TYPES.TIME : INPUT_TYPES.TEXT
|
||||
},
|
||||
htmlType: (dependValues = {}) => {
|
||||
const { [PERIODIC_FIELD.name]: isPeriodic, [END_TYPE_FIELD.name]: endType } = dependValues
|
||||
|
||||
if (!isPeriodic) return INPUT_TYPES.HIDDEN
|
||||
|
||||
return {
|
||||
[END_TYPE_VALUES.NEVER]: INPUT_TYPES.HIDDEN,
|
||||
[END_TYPE_VALUES.REPETITION]: 'number',
|
||||
[END_TYPE_VALUES.DATE]: 'datetime-local'
|
||||
}[endType]
|
||||
},
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.default(undefined)
|
||||
.when(
|
||||
PERIODIC_FIELD.name,
|
||||
(isPeriodic, schema) => isPeriodic ? schema : schema.strip()
|
||||
)
|
||||
.when(
|
||||
END_TYPE_FIELD.name,
|
||||
(endType, schema) => ({
|
||||
[END_TYPE_VALUES.NEVER]: schema.strip(),
|
||||
[END_TYPE_VALUES.REPETITION]: schema
|
||||
.required('Number of repetitions is required'),
|
||||
[END_TYPE_VALUES.DATE]: schema
|
||||
.concat(isoDateValidation('Date'))
|
||||
.required('Date to finish the action is required')
|
||||
}[endType])
|
||||
)
|
||||
}
|
||||
|
||||
export const FIELDS = vm => [
|
||||
...COMMON_FIELDS(vm),
|
||||
TIME_FIELD,
|
||||
PERIODIC_FIELD,
|
||||
REPEAT_FIELD,
|
||||
DAYS_FIELD,
|
||||
END_TYPE_FIELD,
|
||||
END_VALUE_FIELD
|
||||
]
|
||||
|
||||
export const SCHEMA = vm => yup
|
||||
.object(getValidationFromFields(FIELDS(vm)))
|
||||
.concat(COMMON_SCHEMA(vm))
|
||||
.transform(value => {
|
||||
const { [DAYS_FIELD.name]: DAYS, [REPEAT_FIELD.name]: REPEAT, ...rest } = value
|
||||
|
||||
return {
|
||||
...rest,
|
||||
[DAYS_FIELD.name]: DAYS,
|
||||
[REPEAT_FIELD.name]: REPEAT,
|
||||
[PERIODIC_FIELD.name]: !!(DAYS || REPEAT)
|
||||
}
|
||||
})
|
@ -0,0 +1,27 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/CreateSchedActionForm/RelativeForm/schema'
|
||||
|
||||
const RelativeForm = ({ vm, schedule } = {}) => ({
|
||||
resolver: () => SCHEMA(vm),
|
||||
defaultValues: schedule
|
||||
? SCHEMA(vm).cast(schedule, { stripUnknown: true })
|
||||
: SCHEMA(vm).default(),
|
||||
fields: FIELDS(vm)
|
||||
})
|
||||
|
||||
export default RelativeForm
|
@ -0,0 +1,97 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as yup from 'yup'
|
||||
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { getValidationFromFields, capitalize } from 'client/utils'
|
||||
import { COMMON_FIELDS, COMMON_SCHEMA } from 'client/components/Forms/Vm/CreateSchedActionForm/CommonSchema'
|
||||
|
||||
const PERIOD_TYPES = {
|
||||
YEARS: 'years',
|
||||
MONTHS: 'months',
|
||||
WEEKS: 'weeks',
|
||||
DAYS: 'days',
|
||||
HOURS: 'hours',
|
||||
MINUTES: 'minutes'
|
||||
}
|
||||
|
||||
const PERIOD_OPTIONS = Object.entries(PERIOD_TYPES)
|
||||
.map(([text, value]) => ({ text: capitalize(text), value }))
|
||||
|
||||
const TIME_FIELD = {
|
||||
name: 'TIME',
|
||||
label: 'Time after the VM is instantiated',
|
||||
type: INPUT_TYPES.TEXT,
|
||||
htmlType: 'number',
|
||||
validation: yup
|
||||
.number()
|
||||
.typeError('Time value must be a number')
|
||||
.required('Time field is required')
|
||||
.positive()
|
||||
.default(undefined)
|
||||
}
|
||||
|
||||
const PERIOD_FIELD = {
|
||||
name: 'PERIOD',
|
||||
label: 'Period type',
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: PERIOD_OPTIONS,
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.notRequired()
|
||||
.default(PERIOD_OPTIONS[0].value)
|
||||
}
|
||||
|
||||
export const FIELDS = vm => [
|
||||
...COMMON_FIELDS(vm),
|
||||
TIME_FIELD,
|
||||
PERIOD_FIELD
|
||||
]
|
||||
|
||||
export const SCHEMA = vm => yup
|
||||
.object(getValidationFromFields(FIELDS(vm)))
|
||||
.concat(COMMON_SCHEMA(vm))
|
||||
.transform(value => {
|
||||
const { [PERIOD_FIELD.name]: PERIOD, [TIME_FIELD.name]: TIME, ...rest } = value
|
||||
|
||||
if (String(TIME).includes('+')) {
|
||||
const allPeriods = {
|
||||
[PERIOD_TYPES.YEARS]: TIME / 365 / 24 / 3600,
|
||||
[PERIOD_TYPES.MONTHS]: TIME / 30 / 24 / 3600,
|
||||
[PERIOD_TYPES.WEEKS]: TIME / 7 / 24 / 3600,
|
||||
[PERIOD_TYPES.DAYS]: TIME / 24 / 3600,
|
||||
[PERIOD_TYPES.HOURS]: TIME / 3600,
|
||||
[PERIOD_TYPES.MINUTES]: TIME / 60
|
||||
}
|
||||
|
||||
const [period, time] = Object.entries(allPeriods).find(([_, time]) => time >= 1)
|
||||
|
||||
return { ...rest, [PERIOD_FIELD.name]: period, [TIME_FIELD.name]: time }
|
||||
}
|
||||
|
||||
const timeInMilliseconds = {
|
||||
[PERIOD_TYPES.YEARS]: TIME * 365 * 24 * 3600,
|
||||
[PERIOD_TYPES.MONTHS]: TIME * 30 * 24 * 3600,
|
||||
[PERIOD_TYPES.WEEKS]: TIME * 7 * 24 * 3600,
|
||||
[PERIOD_TYPES.DAYS]: TIME * 24 * 3600,
|
||||
[PERIOD_TYPES.HOURS]: TIME * 3600,
|
||||
[PERIOD_TYPES.MINUTES]: TIME * 60
|
||||
}[PERIOD]
|
||||
|
||||
return { ...rest, [TIME_FIELD.name]: timeInMilliseconds }
|
||||
})
|
@ -0,0 +1,22 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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 PunctualForm from 'client/components/Forms/Vm/CreateSchedActionForm/PunctualForm'
|
||||
import RelativeForm from 'client/components/Forms/Vm/CreateSchedActionForm/RelativeForm'
|
||||
|
||||
export {
|
||||
PunctualForm,
|
||||
RelativeForm
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/CreateSnapshotForm/schema'
|
||||
|
||||
const CreateSnapshotForm = () => ({
|
||||
resolver: () => SCHEMA,
|
||||
defaultValues: SCHEMA.default(),
|
||||
fields: FIELDS
|
||||
})
|
||||
|
||||
export default CreateSnapshotForm
|
@ -0,0 +1,37 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as yup from 'yup'
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { getValidationFromFields } from 'client/utils'
|
||||
|
||||
const NAME = {
|
||||
name: 'NAME',
|
||||
label: 'Snapshot name',
|
||||
type: INPUT_TYPES.TEXT,
|
||||
tooltip: 'The new snapshot name. It can be empty.',
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.notRequired()
|
||||
.default('')
|
||||
}
|
||||
|
||||
export const FIELDS = [
|
||||
NAME
|
||||
]
|
||||
|
||||
export const SCHEMA = yup.object(getValidationFromFields(FIELDS))
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import PropTypes from 'prop-types'
|
||||
import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/ResizeCapacityForm/schema'
|
||||
|
||||
const ResizeCapacityForm = ({ vm } = {}) => {
|
||||
@ -27,10 +26,4 @@ const ResizeCapacityForm = ({ vm } = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
ResizeCapacityForm.propTypes = {
|
||||
vm: PropTypes.object
|
||||
}
|
||||
|
||||
ResizeCapacityForm.displayName = 'ResizeCapacityForm'
|
||||
|
||||
export default ResizeCapacityForm
|
||||
|
@ -0,0 +1,25 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/ResizeDiskForm/schema'
|
||||
|
||||
const ResizeDiskForm = ({ disk } = {}) => ({
|
||||
resolver: () => SCHEMA,
|
||||
defaultValues: SCHEMA.cast(disk, { stripUnknown: true }),
|
||||
fields: FIELDS
|
||||
})
|
||||
|
||||
export default ResizeDiskForm
|
@ -0,0 +1,39 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as yup from 'yup'
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { getValidationFromFields } from 'client/utils'
|
||||
|
||||
const SIZE = {
|
||||
name: 'SIZE',
|
||||
label: 'New size',
|
||||
type: INPUT_TYPES.TEXT,
|
||||
htmlType: 'number',
|
||||
tooltip: 'The new size string',
|
||||
validation: yup
|
||||
.number()
|
||||
.typeError('Size value must be a number')
|
||||
.required('Size field is required')
|
||||
.positive()
|
||||
.default(undefined)
|
||||
}
|
||||
|
||||
export const FIELDS = [
|
||||
SIZE
|
||||
]
|
||||
|
||||
export const SCHEMA = yup.object(getValidationFromFields(FIELDS))
|
@ -0,0 +1,25 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/SaveAsDiskForm/schema'
|
||||
|
||||
const SaveAsDiskForm = () => ({
|
||||
resolver: () => SCHEMA,
|
||||
defaultValues: SCHEMA.default(),
|
||||
fields: FIELDS
|
||||
})
|
||||
|
||||
export default SaveAsDiskForm
|
@ -0,0 +1,37 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as yup from 'yup'
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { getValidationFromFields } from 'client/utils'
|
||||
|
||||
const NAME = {
|
||||
name: 'NAME',
|
||||
label: 'New Image name',
|
||||
type: INPUT_TYPES.TEXT,
|
||||
tooltip: 'Name for the new Image where the disk will be saved.',
|
||||
validation: yup
|
||||
.string()
|
||||
.trim()
|
||||
.required('Name field is required')
|
||||
.default('')
|
||||
}
|
||||
|
||||
export const FIELDS = [
|
||||
NAME
|
||||
]
|
||||
|
||||
export const SCHEMA = yup.object(getValidationFromFields(FIELDS))
|
@ -13,11 +13,20 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import ResizeCapacityForm from 'client/components/Forms/Vm/ResizeCapacityForm'
|
||||
import AttachNicForm from 'client/components/Forms/Vm/AttachNicForm'
|
||||
import CreateDiskSnapshotForm from 'client/components/Forms/Vm/CreateDiskSnapshotForm'
|
||||
import CreateSnapshotForm from 'client/components/Forms/Vm/CreateSnapshotForm'
|
||||
import ResizeCapacityForm from 'client/components/Forms/Vm/ResizeCapacityForm'
|
||||
import ResizeDiskForm from 'client/components/Forms/Vm/ResizeDiskForm'
|
||||
import SaveAsDiskForm from 'client/components/Forms/Vm/SaveAsDiskForm'
|
||||
export * from 'client/components/Forms/Vm/AttachDiskForm'
|
||||
export * from 'client/components/Forms/Vm/CreateSchedActionForm'
|
||||
|
||||
export {
|
||||
AttachNicForm,
|
||||
ResizeCapacityForm
|
||||
CreateDiskSnapshotForm,
|
||||
CreateSnapshotForm,
|
||||
ResizeCapacityForm,
|
||||
ResizeDiskForm,
|
||||
SaveAsDiskForm
|
||||
}
|
||||
|
@ -13,6 +13,10 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
export { FormWithSchema }
|
||||
export {
|
||||
ButtonToTriggerForm,
|
||||
FormWithSchema
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import { useRef } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import clsx from 'clsx'
|
||||
|
||||
@ -28,7 +28,7 @@ import internalStyles from 'client/components/HOC/InternalLayout/styles'
|
||||
|
||||
const InternalLayout = ({ children }) => {
|
||||
const classes = internalStyles()
|
||||
const container = React.useRef()
|
||||
const container = useRef()
|
||||
const { isFixMenu } = useGeneral()
|
||||
|
||||
return (
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useContext, useState, useEffect, createContext } from 'react'
|
||||
import { useContext, useState, useEffect, createContext } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import root from 'window-or-global'
|
||||
import { sprintf } from 'sprintf-js'
|
||||
|
@ -14,8 +14,6 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
|
||||
import { Button } from '@material-ui/core'
|
||||
import { Group as GroupIcon, VerifiedBadge as SelectIcon } from 'iconoir-react'
|
||||
|
||||
@ -33,10 +31,6 @@ const Group = () => {
|
||||
const { user, groups, filterPool } = useAuth()
|
||||
const { changeGroup } = useAuthApi()
|
||||
|
||||
const handleChangeGroup = group => {
|
||||
group && changeGroup({ id: user.ID, group })
|
||||
}
|
||||
|
||||
const renderResult = ({ ID, NAME }, handleClose) => {
|
||||
const isSelected =
|
||||
(filterPool === ALL_RESOURCES && ALL_RESOURCES === ID) ||
|
||||
@ -44,11 +38,11 @@ const Group = () => {
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={`term-${ID}`}
|
||||
key={`switcher-group-${ID}`}
|
||||
fullWidth
|
||||
className={classes.groupButton}
|
||||
onClick={() => {
|
||||
handleChangeGroup(ID)
|
||||
ID && changeGroup({ id: user.ID, group: ID })
|
||||
handleClose()
|
||||
}}
|
||||
>
|
||||
@ -73,10 +67,10 @@ const Group = () => {
|
||||
|
||||
return (
|
||||
<HeaderPopover
|
||||
id="group-list"
|
||||
id='group-list'
|
||||
icon={<GroupIcon />}
|
||||
buttonProps={{ 'data-cy': 'header-group-button' }}
|
||||
headerTitle="Switch group"
|
||||
headerTitle='Switch group'
|
||||
>
|
||||
{({ handleClose }) => (
|
||||
<Search
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import { memo, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
@ -31,7 +31,7 @@ import clsx from 'clsx'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import headerStyles from 'client/components/Header/styles'
|
||||
|
||||
const HeaderPopover = React.memo(({
|
||||
const HeaderPopover = memo(({
|
||||
id,
|
||||
icon,
|
||||
buttonLabel,
|
||||
@ -44,7 +44,7 @@ const HeaderPopover = React.memo(({
|
||||
const classes = headerStyles()
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'))
|
||||
|
||||
const [anchorEl, setAnchorEl] = React.useState(null)
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
|
||||
const handleOpen = event => setAnchorEl(event.currentTarget)
|
||||
const handleClose = () => setAnchorEl(null)
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { memo } from 'react'
|
||||
|
||||
import { MenuItem, MenuList, Link } from '@material-ui/core'
|
||||
import { ProfileCircled as UserIcon } from 'iconoir-react'
|
||||
@ -25,7 +25,7 @@ import { Tr } from 'client/components/HOC'
|
||||
import { isDevelopment } from 'client/utils'
|
||||
import { T, APPS, APP_URL } from 'client/constants'
|
||||
|
||||
const User = React.memo(() => {
|
||||
const User = memo(() => {
|
||||
const { user } = useAuth()
|
||||
const { logout } = useAuthApi()
|
||||
|
||||
@ -45,7 +45,11 @@ const User = React.memo(() => {
|
||||
{isDevelopment() &&
|
||||
APPS?.map(appName => (
|
||||
<MenuItem key={appName}>
|
||||
<Link color='secondary' href={`${APP_URL}/${appName}`} style={{ width: '100%' }}>
|
||||
<Link
|
||||
color='secondary'
|
||||
href={`${APP_URL}/${appName}`}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<DevTypography label={appName} />
|
||||
</Link>
|
||||
</MenuItem>
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { Button } from '@material-ui/core'
|
||||
import { ViewGrid as ViewIcon, VerifiedBadge as SelectIcon } from 'iconoir-react'
|
||||
@ -30,26 +30,22 @@ const View = () => {
|
||||
const { view, views = {} } = useAuth()
|
||||
const { changeView } = useAuthApi()
|
||||
|
||||
const handleChangeView = newView => {
|
||||
newView && newView !== view && changeView(newView)
|
||||
}
|
||||
|
||||
const renderResult = (viewName, handleClose) => (
|
||||
const renderResult = (newView, handleClose) => (
|
||||
<Button
|
||||
key={`view-${viewName}`}
|
||||
key={`view-${newView}`}
|
||||
fullWidth
|
||||
className={classes.groupButton}
|
||||
onClick={() => {
|
||||
handleChangeView(viewName)
|
||||
newView && newView !== view && changeView(newView)
|
||||
handleClose()
|
||||
}}
|
||||
>
|
||||
{viewName}
|
||||
{viewName === view && <SelectIcon size='1em' />}
|
||||
{newView}
|
||||
{newView === view && <SelectIcon size='1em' />}
|
||||
</Button>
|
||||
)
|
||||
|
||||
const viewNames = React.useMemo(() => Object.keys(views), [view])
|
||||
const viewNames = useMemo(() => Object.keys(views), [view])
|
||||
|
||||
return (
|
||||
<HeaderPopover
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
|
||||
import { MenuItem, MenuList } from '@material-ui/core'
|
||||
import { Language as ZoneIcon } from 'iconoir-react'
|
||||
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
|
@ -13,10 +13,10 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { memo } from 'react'
|
||||
import { number, string, oneOfType } from 'prop-types'
|
||||
|
||||
const DockerLogo = React.memo(({ viewBox, width, height, color, ...props }) => {
|
||||
const DockerLogo = memo(({ viewBox, width, height, color, ...props }) => {
|
||||
return (
|
||||
<svg viewBox={viewBox} width={width} height={height} {...props} {...props}>
|
||||
<path
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import { number, string, bool, oneOfType } from 'prop-types'
|
||||
import { useTheme } from '@material-ui/core'
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { useState, memo } from 'react'
|
||||
import { useState, memo, JSXElementConstructor } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { DEFAULT_IMAGE, IMAGE_FORMATS } from 'client/constants'
|
||||
@ -27,7 +27,7 @@ const INITIAL_STATE = { fail: false, retries: 0 }
|
||||
* @param {string} props.src - Image source
|
||||
* @param {boolean} props.withSources - Add image formats to picture: webp, png and jpg
|
||||
* @param {string} props.imgProps - Properties to image element
|
||||
* @returns {React.JSXElementConstructor} Picture with all images format
|
||||
* @returns {JSXElementConstructor} Picture with all images format
|
||||
*/
|
||||
const Image = memo(({ src, imageInError, withSources, imgProps }) => {
|
||||
const [error, setError] = useState(INITIAL_STATE)
|
||||
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { CSSTransition, TransitionGroup } from 'react-transition-group'
|
||||
|
||||
@ -74,7 +73,7 @@ const ListCards = ({
|
||||
|
||||
return (
|
||||
<CSSTransition
|
||||
// use key to render transition (default: id or ID)
|
||||
// use key to render transition (default: id or ID)
|
||||
key={`card-${key.replace(/\s/g, '')}`}
|
||||
classNames={classes.item}
|
||||
timeout={400}
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import React, { memo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Box, Typography, InputBase } from '@material-ui/core'
|
||||
@ -44,7 +44,7 @@ const ListHeader = memo(({
|
||||
<Box className={classes.root}>
|
||||
<Box className={classes.title}>
|
||||
{!!(hasReloadButton || reloadButtonProps) && (
|
||||
<SubmitButton icon label={<RefreshIcon />} {...reloadButtonProps} />
|
||||
<SubmitButton icon={<RefreshIcon />} {...reloadButtonProps} />
|
||||
)}
|
||||
{title && (
|
||||
<Typography variant='h5' className={classes.titleText}>
|
||||
@ -54,7 +54,7 @@ const ListHeader = memo(({
|
||||
</Box>
|
||||
<Box className={classes.actions}>
|
||||
{!!(hasAddButton || addButtonProps) && (
|
||||
<SubmitButton color='secondary' icon label={<AddIcon />} {...addButtonProps} />
|
||||
<SubmitButton color='secondary' icon={<AddIcon />} {...addButtonProps} />
|
||||
)}
|
||||
{!!(hasSearch || searchProps) && (
|
||||
<Box className={classes.search}>
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { makeStyles, fade } from '@material-ui/core'
|
||||
import { makeStyles, alpha } from '@material-ui/core'
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
root: {
|
||||
@ -54,9 +54,9 @@ export default makeStyles(theme => ({
|
||||
search: {
|
||||
position: 'relative',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: fade(theme.palette.primary.dark, 0.15),
|
||||
backgroundColor: alpha(theme.palette.primary.dark, 0.15),
|
||||
'&:hover': {
|
||||
backgroundColor: fade(theme.palette.primary.dark, 0.25)
|
||||
backgroundColor: alpha(theme.palette.primary.dark, 0.25)
|
||||
},
|
||||
margin: theme.spacing(1, 0),
|
||||
width: '100%',
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useRef, useEffect, useCallback, createRef } from 'react'
|
||||
import { useRef, useEffect, useCallback, createRef } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { debounce, LinearProgress } from '@material-ui/core'
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
import { useRef, useCallback, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { useVirtual } from 'react-virtual'
|
||||
@ -59,7 +59,7 @@ const ListVirtualized = ({
|
||||
const classes = useStyles()
|
||||
|
||||
// OBSERVER
|
||||
const loaderRef = React.useRef()
|
||||
const loaderRef = useRef()
|
||||
const { isNearScreen } = useNearScreen({
|
||||
distance: '100px',
|
||||
externalRef: isLoading ? null : loaderRef,
|
||||
@ -67,18 +67,18 @@ const ListVirtualized = ({
|
||||
})
|
||||
|
||||
// VIRTUALIZER
|
||||
const parentRef = React.useRef()
|
||||
const parentRef = useRef()
|
||||
const rowVirtualizer = useVirtual({
|
||||
size: data.length,
|
||||
parentRef,
|
||||
overscan: 20,
|
||||
estimateSize: React.useCallback(() => 40, []),
|
||||
estimateSize: useCallback(() => 40, []),
|
||||
keyExtractor: index => data[index]?.id
|
||||
})
|
||||
|
||||
const debounceHandleNextPage = React.useCallback(debounce(fetchMore, 200), [])
|
||||
const debounceHandleNextPage = useCallback(debounce(fetchMore, 200), [])
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (isNearScreen && !canFetchMore) debounceHandleNextPage()
|
||||
}, [isNearScreen, canFetchMore, debounceHandleNextPage])
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { JSXElementConstructor } from 'react'
|
||||
|
||||
import { makeStyles, Box } from '@material-ui/core'
|
||||
import { OpenNebulaLogo } from 'client/components/Icons'
|
||||
@ -34,7 +34,7 @@ const useStyles = makeStyles(theme => ({
|
||||
/**
|
||||
* Component with OpenNebula logo as spinner in full width and height.
|
||||
*
|
||||
* @returns {React.JSXElementConstructor} Container with logo inside
|
||||
* @returns {JSXElementConstructor} Container with logo inside
|
||||
*/
|
||||
const LoadingScreen = () => {
|
||||
const classes = useStyles()
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import React, { useEffect } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { useSnackbar } from 'notistack'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
/**
|
||||
@ -25,9 +25,9 @@ import PropTypes from 'prop-types'
|
||||
* @returns {string} Returns a count number
|
||||
*/
|
||||
const NumberEasing = ({ value = '0', speed = 200 }) => {
|
||||
const [count, setCount] = React.useState('0')
|
||||
const [count, setCount] = useState('0')
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
let start = 0
|
||||
const end = parseInt(String(value).substring(0, 3))
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Redirect, Route } from 'react-router-dom'
|
||||
@ -23,7 +23,7 @@ import { useAuth } from 'client/features/Auth'
|
||||
* Public route.
|
||||
*
|
||||
* @param {object} props - Route props
|
||||
* @param {React.JSXElementConstructor} props.redirectWhenAuth
|
||||
* @param {JSXElementConstructor} props.redirectWhenAuth
|
||||
* - Route to redirect in case of user is authenticated
|
||||
* @returns {Redirect|Route}
|
||||
* - If current user is authenticated, then redirect to private route
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as React from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { Redirect, Route } from 'react-router-dom'
|
||||
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
@ -29,7 +29,7 @@ const ProtectedRoute = props => {
|
||||
const { isLogged, jwt } = useAuth()
|
||||
const { getAuthUser } = useAuthApi()
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
jwt && getAuthUser()
|
||||
}, [])
|
||||
|
||||
|
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