mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-16 22:50:10 +03:00
F OpenNebula/one#5422: Add doc to client/hooks
This commit is contained in:
parent
38084c1851
commit
647b2f060b
@ -13,9 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/**
|
||||
* @enum {string} Scheme type
|
||||
*/
|
||||
/** @enum {string} Scheme type */
|
||||
export const SCHEMES = {
|
||||
SYSTEM: 'system',
|
||||
DARK: 'dark',
|
||||
|
@ -13,6 +13,9 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/** @typedef {{color: string, name: string, meaning: string}} StateInfo */
|
||||
|
||||
export const ACTIVE = 'ACTIVE'
|
||||
export const BOOT = 'BOOT'
|
||||
export const BOOT_FAILURE = 'BOOT_FAILURE'
|
||||
|
@ -16,6 +16,7 @@
|
||||
import * as STATES from 'client/constants/states'
|
||||
import COLOR from 'client/constants/color'
|
||||
|
||||
/** @type {STATES.StateInfo[]} Virtual machine states */
|
||||
export const VM_STATES = [
|
||||
{ // 0
|
||||
name: STATES.INIT,
|
||||
|
@ -56,7 +56,7 @@ const useStyles = makeStyles(() => ({
|
||||
* @param {Array} props.dataFields - List of keys
|
||||
* @param {Function} props.handleCreate - Create a new Node
|
||||
* @param {Function} props.handleEdit - Edit a current Node
|
||||
* @param {boolean} props.handleSetData - Set new list of nodes
|
||||
* @param {Function} props.handleSetData - Set new list of nodes
|
||||
* @returns {React.JSXElementConstructor} ReactFlow component
|
||||
*/
|
||||
const Flow = memo(({ dataFields, handleCreate, handleEdit, handleSetData }) => {
|
||||
|
@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useReducer, useCallback, useEffect, useRef } from 'react'
|
||||
import { useReducer, useCallback, useEffect, useRef, ReducerState, ReducerAction } from 'react'
|
||||
import { fakeDelay } from 'client/utils'
|
||||
|
||||
/** @enum {string} Status of request */
|
||||
const STATUS = {
|
||||
INIT: 'INIT',
|
||||
PENDING: 'PENDING',
|
||||
@ -23,6 +24,7 @@ const STATUS = {
|
||||
FETCHED: 'FETCHED'
|
||||
}
|
||||
|
||||
/** @enum {string} Type of action */
|
||||
const ACTIONS = {
|
||||
REQUEST: 'REQUEST',
|
||||
SUCCESS: 'SUCCESS',
|
||||
@ -37,6 +39,11 @@ const INITIAL_STATE = {
|
||||
reloading: false
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReducerState} state - Current state of reducer
|
||||
* @param {ReducerAction} action - Action will be triggered
|
||||
* @returns {ReducerState} The reducer state modified with payload
|
||||
*/
|
||||
const fetchReducer = (state, action) => {
|
||||
const { type, payload, reload = false } = action
|
||||
const { data: currentData } = state
|
||||
@ -62,6 +69,24 @@ const fetchReducer = (state, action) => {
|
||||
}[type] ?? state
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to manage a request.
|
||||
*
|
||||
* @param {Promise} request - Request to fetch
|
||||
* @param {object} socket - Socket to update the global state after success
|
||||
* @param {Function} socket.connect - Connect to socket
|
||||
* @param {Function} socket.disconnect - Disconnect to socket
|
||||
* @returns {{
|
||||
* status: STATUS,
|
||||
* error: object|string,
|
||||
* data: object|Array,
|
||||
* loading: boolean,
|
||||
* reloading: boolean,
|
||||
* fetchRequest: Function,
|
||||
* STATUS: STATUS,
|
||||
* ACTIONS: ACTIONS
|
||||
* }} - List of functions to interactive with FireEdge sockets
|
||||
*/
|
||||
const useFetch = (request, socket) => {
|
||||
const cancelRequest = useRef(false)
|
||||
const [state, dispatch] = useReducer(fetchReducer, INITIAL_STATE)
|
||||
@ -101,22 +126,33 @@ const useFetch = (request, socket) => {
|
||||
const errorMessage = typeof error === 'string' ? error : error?.message
|
||||
|
||||
dispatch({ type: ACTIONS.FAILURE, payload: errorMessage })
|
||||
|
||||
return error
|
||||
}
|
||||
}, [request, cancelRequest.current, dispatch])
|
||||
|
||||
const fetchRequest = useCallback((payload, options = {}) => {
|
||||
const { reload = false, delay = 0 } = options
|
||||
const fetchRequest = useCallback(
|
||||
/**
|
||||
* @param {Array|string|number|object} [payload] - Payload to request
|
||||
* @param {object} options - Options to trigger the request
|
||||
* @param {boolean} options.reload
|
||||
* - If `true`, the state will be change `reloading` instead of `loading`
|
||||
* @param {number} options.delay - Delay to trigger the request
|
||||
* @returns {Promise} - Returns a promise with response or error
|
||||
*/
|
||||
(payload, options = {}) => {
|
||||
const { reload = false, delay = 0 } = options
|
||||
|
||||
if (!(Number.isInteger(delay) && delay >= 0)) {
|
||||
console.error(`
|
||||
if (!(Number.isInteger(delay) && delay >= 0)) {
|
||||
console.error(`
|
||||
Delay must be a number >= 0!
|
||||
If you're using it as a function, it must also return a number >= 0.`)
|
||||
}
|
||||
}
|
||||
|
||||
return fakeDelay(delay).then(() => doFetch(payload, reload))
|
||||
}, [request])
|
||||
return fakeDelay(delay).then(() => doFetch(payload, reload))
|
||||
}, [request])
|
||||
|
||||
return { ...state, fetchRequest, STATUS, ACTIONS, INITIAL_STATE }
|
||||
return { ...state, fetchRequest, STATUS, ACTIONS }
|
||||
}
|
||||
|
||||
export default useFetch
|
||||
|
@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useReducer, useCallback, useEffect, useRef } from 'react'
|
||||
import { useReducer, useCallback, useEffect, useRef, ReducerState, ReducerAction } from 'react'
|
||||
import { fakeDelay } from 'client/utils'
|
||||
|
||||
/** @enum {string} Status of request */
|
||||
const STATUS = {
|
||||
INIT: 'INIT',
|
||||
PENDING: 'PENDING',
|
||||
@ -23,6 +24,7 @@ const STATUS = {
|
||||
FETCHED: 'FETCHED'
|
||||
}
|
||||
|
||||
/** @enum {string} Type of action */
|
||||
const ACTIONS = {
|
||||
REQUEST: 'REQUEST',
|
||||
SUCCESS: 'SUCCESS',
|
||||
@ -37,6 +39,11 @@ const INITIAL_STATE = {
|
||||
reloading: false
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReducerState} state - Current state of reducer
|
||||
* @param {ReducerAction} action - Action will be triggered
|
||||
* @returns {ReducerState} The reducer state modified with payload
|
||||
*/
|
||||
const fetchReducer = (state, action) => {
|
||||
const { type, payload, reload = false } = action
|
||||
const { data: currentData } = state
|
||||
@ -62,6 +69,20 @@ const fetchReducer = (state, action) => {
|
||||
}[type] ?? state
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to manage a group of requests.
|
||||
*
|
||||
* @returns {{
|
||||
* status: STATUS,
|
||||
* error: object|string,
|
||||
* data: object|Array,
|
||||
* loading: boolean,
|
||||
* reloading: boolean,
|
||||
* fetchRequestAll: Function,
|
||||
* STATUS: STATUS,
|
||||
* ACTIONS: ACTIONS
|
||||
* }} - List of properties to fetch a group of requests
|
||||
*/
|
||||
const useFetchAll = () => {
|
||||
const cancelRequest = useRef(false)
|
||||
const [state, dispatch] = useReducer(fetchReducer, INITIAL_STATE)
|
||||
@ -80,28 +101,41 @@ const useFetchAll = () => {
|
||||
if (cancelRequest.current) return
|
||||
|
||||
dispatch({ type: ACTIONS.SUCCESS, payload: response })
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
if (cancelRequest.current) return
|
||||
|
||||
const errorMessage = typeof error === 'string' ? error : error?.message
|
||||
|
||||
dispatch({ type: ACTIONS.FAILURE, payload: errorMessage })
|
||||
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
const fetchRequestAll = useCallback((requests, options = {}) => {
|
||||
const { reload = false, delay = 0 } = options
|
||||
const fetchRequestAll = useCallback(
|
||||
/**
|
||||
* @param {Function[]} requests - Array of requests to fetch
|
||||
* @param {object} options - Options to trigger the request
|
||||
* @param {boolean} options.reload
|
||||
* - If `true`, the state will be change `reloading` instead of `loading`
|
||||
* @param {number} options.delay - Delay to trigger the request
|
||||
* @returns {Promise} - Returns a promise with responses or error
|
||||
*/
|
||||
(requests, options = {}) => {
|
||||
const { reload = false, delay = 0 } = options
|
||||
|
||||
if (!(Number.isInteger(delay) && delay >= 0)) {
|
||||
console.error(`
|
||||
if (!(Number.isInteger(delay) && delay >= 0)) {
|
||||
console.error(`
|
||||
Delay must be a number >= 0!
|
||||
If you're using it as a function, it must also return a number >= 0.`)
|
||||
}
|
||||
}
|
||||
|
||||
fakeDelay(delay).then(() => doFetches(requests, reload))
|
||||
}, [])
|
||||
return fakeDelay(delay).then(() => doFetches(requests, reload))
|
||||
}, [])
|
||||
|
||||
return { ...state, fetchRequestAll, STATUS, ACTIONS, INITIAL_STATE }
|
||||
return { ...state, fetchRequestAll, STATUS, ACTIONS }
|
||||
}
|
||||
|
||||
export default useFetchAll
|
||||
|
@ -17,7 +17,23 @@ import { useState, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { fakeDelay } from 'client/utils'
|
||||
|
||||
function useList ({ list, initLength }) {
|
||||
/**
|
||||
* Hook to manage an infinite list.
|
||||
* TODO: Make the hook with a reducer.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {Array} props.list - List of elements
|
||||
* @param {number} [props.initLength=50] - Initial length of the list
|
||||
* @returns {{
|
||||
* loading: boolean,
|
||||
* loadingNextPage: boolean,
|
||||
* shortList: Array,
|
||||
* finish: boolean,
|
||||
* reset: Function,
|
||||
* setLength: Function
|
||||
* }} - Properties to manage the infinite list
|
||||
*/
|
||||
const useList = ({ list, initLength }) => {
|
||||
const [fullList, setFullList] = useState([])
|
||||
const [shortList, setShortList] = useState([])
|
||||
const [finish, setFinish] = useState(false)
|
||||
@ -63,13 +79,11 @@ function useList ({ list, initLength }) {
|
||||
|
||||
useList.propTypes = {
|
||||
list: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
fetchList: PropTypes.func.isRequired,
|
||||
initLength: PropTypes.string
|
||||
}
|
||||
|
||||
useList.defaultProps = {
|
||||
list: [],
|
||||
fetchList: () => undefined,
|
||||
initLength: 50
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useCallback, useState, SetStateAction } from 'react'
|
||||
import { set } from 'client/utils'
|
||||
|
||||
const NEXT_INDEX = index => index + 1
|
||||
@ -22,36 +22,98 @@ const EXISTS_INDEX = index => index !== -1
|
||||
const getIndexById = (list, id) =>
|
||||
list.findIndex(({ id: itemId }) => itemId === id)
|
||||
|
||||
/**
|
||||
* Hook to manage a list with selectable elements in a form data.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {boolean} props.multiple - If `true`, can be more than one select elements
|
||||
* @param {string} props.key - Key of list in the form
|
||||
* @param {object[]} props.list - Form data
|
||||
* @param {SetStateAction} props.setList - State action from the form
|
||||
* @param {{ id: string|number }} props.defaultValue - Default value of element
|
||||
* @example <caption>Example usage.</caption>
|
||||
* // const INITIAL_STATE = { listKey: [{ id: 'item1' }, { id: 'item2' }] }
|
||||
* // const [formData, setFormData] = useState(INITIAL_STATE)
|
||||
* //
|
||||
* // const listFunctions = useListForm({
|
||||
* // key: 'listKey',
|
||||
* // list: formData,
|
||||
* // setList: setFormData
|
||||
* // defaultValue: { id: 'default' }
|
||||
* // })
|
||||
* @returns {{
|
||||
* editingData: Function,
|
||||
* handleSelect: Function,
|
||||
* handleUnselect: Function,
|
||||
* handleClear: Function,
|
||||
* handleClone: Function,
|
||||
* handleRemove: Function,
|
||||
* handleSetList: Function,
|
||||
* handleSave: Function,
|
||||
* handleEdit: Function
|
||||
* }} - Functions to manage the list
|
||||
*/
|
||||
const useListForm = ({ multiple, key, list, setList, defaultValue }) => {
|
||||
const [editingData, setEditingData] = useState({})
|
||||
|
||||
const handleSetList = useCallback(
|
||||
newList => setList(data => ({ ...set(data, key, newList) })),
|
||||
/**
|
||||
* Resets the list with a new value.
|
||||
*
|
||||
* @param {Array} newList - New list
|
||||
*/
|
||||
newList => {
|
||||
setList(data => ({ ...set(data, key, newList) }))
|
||||
},
|
||||
[key, setList, list]
|
||||
)
|
||||
|
||||
const handleSelect = useCallback(
|
||||
id =>
|
||||
/**
|
||||
* Add an item to data form list.
|
||||
*
|
||||
* @param {string|number} id - Element id
|
||||
*/
|
||||
id => {
|
||||
setList(prevList => ({
|
||||
...prevList,
|
||||
[key]: multiple ? [...(prevList[key] ?? []), id] : [id]
|
||||
})),
|
||||
}))
|
||||
},
|
||||
[key, setList, multiple]
|
||||
)
|
||||
|
||||
const handleUnselect = useCallback(
|
||||
(id, filter = item => item !== id) =>
|
||||
/**
|
||||
* Removes an item from data form list.
|
||||
*
|
||||
* @param {string|number} id - Element id
|
||||
* @param {Function} [filter] - Filter function to remove the item.
|
||||
*/
|
||||
(id, filter) => {
|
||||
setList(prevList => ({
|
||||
...prevList,
|
||||
[key]: prevList[key]?.filter(filter)
|
||||
})),
|
||||
[key]: prevList[key]?.filter(filter ?? (item => item !== id))
|
||||
}))
|
||||
},
|
||||
[key, setList]
|
||||
)
|
||||
|
||||
const handleClear = useCallback(
|
||||
() => setList(prevList => ({ ...prevList, [key]: [] })), [key]
|
||||
/** Clear the data form list. */
|
||||
() => {
|
||||
setList(prevList => ({ ...prevList, [key]: [] }))
|
||||
},
|
||||
[key]
|
||||
)
|
||||
|
||||
/**
|
||||
* Clones an item and change two attributes:
|
||||
* - id: id from default value
|
||||
* - name: same name of element cloned, with the suffix '_clone'
|
||||
*
|
||||
* @param {string|number} id - Element id
|
||||
*/
|
||||
const handleClone = useCallback(
|
||||
id => {
|
||||
const itemIndex = getIndexById(list, id)
|
||||
@ -71,16 +133,16 @@ const useListForm = ({ multiple, key, list, setList, defaultValue }) => {
|
||||
[list]
|
||||
)
|
||||
|
||||
const handleRemove = useCallback(
|
||||
id => {
|
||||
const newList = list?.filter(item => item.id !== id)
|
||||
|
||||
handleSetList(newList)
|
||||
},
|
||||
[key, list]
|
||||
)
|
||||
/** Removes an item from data form list. */
|
||||
const handleRemove = useCallback(handleUnselect, [key, list])
|
||||
|
||||
const handleSave = useCallback(
|
||||
/**
|
||||
* Saves the data from editing state.
|
||||
*
|
||||
* @param {object} values - New element data
|
||||
* @param {string|number} [id] - Element id
|
||||
*/
|
||||
(values, id = editingData?.id) => {
|
||||
const itemIndex = getIndexById(list, id)
|
||||
const index = EXISTS_INDEX(itemIndex) ? itemIndex : list.length
|
||||
@ -93,6 +155,11 @@ const useListForm = ({ multiple, key, list, setList, defaultValue }) => {
|
||||
)
|
||||
|
||||
const handleEdit = useCallback(
|
||||
/**
|
||||
* Find the element by id and set value to editing state.
|
||||
*
|
||||
* @param {string|number} id - Element id
|
||||
*/
|
||||
id => {
|
||||
const index = list.findIndex(item => item.id === id)
|
||||
const openData = list[index] ?? defaultValue
|
||||
|
@ -13,8 +13,21 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { useEffect, useState, useRef, RefObject } from 'react'
|
||||
|
||||
/**
|
||||
* Hook to manage the intersection of a target element with
|
||||
* an ancestor element or with a top-level document's viewport.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {RefObject} props.externalRef - Element to be observed
|
||||
* @param {string} props.distance - Margin around the element
|
||||
* @param {boolean} props.once - If `true`, the observer will be triggered once
|
||||
* @returns {{
|
||||
* isNearScreen: boolean,
|
||||
* fromRef: RefObject
|
||||
* }} - Intersection observer information
|
||||
*/
|
||||
const useNearScreen = ({ externalRef, distance, once = true }) => {
|
||||
const [isNearScreen, setShow] = useState(false)
|
||||
const fromRef = useRef()
|
||||
|
@ -14,12 +14,23 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useState, useMemo, useCallback } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { debounce } from '@material-ui/core'
|
||||
import Fuse from 'fuse.js'
|
||||
|
||||
function useSearch ({ list, listOptions }) {
|
||||
/**
|
||||
* Hook to manage a search in a list.
|
||||
*
|
||||
* @param {object} params - Search parameters
|
||||
* @param {Array} params.list - List of elements
|
||||
* @param {Fuse.IFuseOptions} params.listOptions - Search options
|
||||
* @returns {{
|
||||
* query: string,
|
||||
* result: Array,
|
||||
* handleChange: Function
|
||||
* }} - Returns information about the search
|
||||
*/
|
||||
const useSearch = ({ list, listOptions }) => {
|
||||
const [query, setQuery] = useState('')
|
||||
const [result, setResult] = useState(undefined)
|
||||
|
||||
@ -47,19 +58,4 @@ function useSearch ({ list, listOptions }) {
|
||||
return { query, result, handleChange }
|
||||
}
|
||||
|
||||
useSearch.propTypes = {
|
||||
list: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
listOptions: PropTypes.shape({
|
||||
isCaseSensitive: PropTypes.bool,
|
||||
shouldSort: PropTypes.bool,
|
||||
sortFn: PropTypes.func,
|
||||
keys: PropTypes.arrayOf(PropTypes.string)
|
||||
})
|
||||
}
|
||||
|
||||
useSearch.defaultProps = {
|
||||
list: [],
|
||||
listOptions: { keys: [] }
|
||||
}
|
||||
|
||||
export default useSearch
|
||||
|
@ -32,11 +32,25 @@ const createWebsocket = (path, query) => socketIO({
|
||||
reconnectionAttempts: 5
|
||||
})
|
||||
|
||||
export default function useSocket () {
|
||||
/**
|
||||
* Hook to manage the OpenNebula sockets.
|
||||
*
|
||||
* @returns {{
|
||||
* getHooksSocket: Function,
|
||||
* getProvisionSocket: Function
|
||||
* }} - List of functions to interactive with FireEdge sockets
|
||||
*/
|
||||
const useSocket = () => {
|
||||
const dispatch = useDispatch()
|
||||
const { jwt } = useAuth()
|
||||
const { zone } = useGeneral()
|
||||
|
||||
/**
|
||||
* @param {('vm'|'host'|'image')} resource - Resource name
|
||||
* @param {string} id - Resource id
|
||||
* @returns {{ connect: Function, disconnect: Function }}
|
||||
* - Functions to manage the socket connections
|
||||
*/
|
||||
const getHooksSocket = useCallback(({ resource, id }) => {
|
||||
const socket = createWebsocket(
|
||||
SOCKETS.HOOKS,
|
||||
@ -46,7 +60,7 @@ export default function useSocket () {
|
||||
return {
|
||||
connect: ({ dataFromFetch, callback }) => {
|
||||
dataFromFetch && socket.on(SOCKETS.CONNECT, () => {
|
||||
// update from data fetched
|
||||
// update redux state from data fetched
|
||||
dispatch(updateResourceFromFetch({ data: dataFromFetch, resource }))
|
||||
})
|
||||
|
||||
@ -63,10 +77,15 @@ export default function useSocket () {
|
||||
}
|
||||
}, [jwt, zone])
|
||||
|
||||
const getProvisionSocket = useCallback(func => {
|
||||
/**
|
||||
* @param {Function} callback - Callback from socket
|
||||
* @returns {{ on: Function, off: Function }}
|
||||
* - Functions to manage the socket connections
|
||||
*/
|
||||
const getProvisionSocket = useCallback(callback => {
|
||||
const socket = createWebsocket(SOCKETS.PROVISION, { token: jwt, zone })
|
||||
|
||||
socket.on(SOCKETS.PROVISION, func)
|
||||
socket.on(SOCKETS.PROVISION, callback)
|
||||
|
||||
return {
|
||||
on: () => socket.connect(),
|
||||
@ -79,3 +98,5 @@ export default function useSocket () {
|
||||
getProvisionSocket
|
||||
}
|
||||
}
|
||||
|
||||
export default useSocket
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { STATES, VM_STATES, VM_LCM_STATES } from 'client/constants'
|
||||
import { STATES, VM_STATES, VM_LCM_STATES, StateInfo } from 'client/constants'
|
||||
|
||||
/* const EXTERNAL_IP_ATTRS = [
|
||||
'GUEST_IP',
|
||||
@ -72,11 +72,11 @@ export const getHypervisor = vm => String(getLastHistory(vm)?.VM_MAD).toLowerCas
|
||||
export const isVCenter = vm => getHypervisor(vm) === 'vcenter'
|
||||
|
||||
/**
|
||||
* @type {{color: string, name: string, meaning: string}} StateInfo
|
||||
* @param {object} vm - Virtual machine
|
||||
* @returns {StateInfo} State information from resource
|
||||
*/
|
||||
export const getState = ({ STATE, LCM_STATE } = {}) => {
|
||||
export const getState = vm => {
|
||||
const { STATE, LCM_STATE } = vm ?? {}
|
||||
const state = VM_STATES[+STATE]
|
||||
|
||||
return state?.name === STATES.ACTIVE ? VM_LCM_STATES[+LCM_STATE] : state
|
||||
|
Loading…
x
Reference in New Issue
Block a user