mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-22 18:50:08 +03:00
(cherry picked from commit 60dbc2b734c04515b1d06def1b350b272dc7a538)
This commit is contained in:
parent
3e4f1c49bb
commit
5f8924c342
@ -20,14 +20,14 @@ import { ENDPOINTS, PATH } from 'client/apps/provision/routes'
|
||||
import { ENDPOINTS as DEV_ENDPOINTS } from 'client/router/dev'
|
||||
|
||||
import { useGeneral, useGeneralApi } from 'client/features/General'
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import provisionApi from 'client/features/OneApi/provision'
|
||||
import providerApi from 'client/features/OneApi/provider'
|
||||
import { useSocket } from 'client/hooks'
|
||||
|
||||
import Sidebar from 'client/components/Sidebar'
|
||||
import Notifier from 'client/components/Notifier'
|
||||
import LoadingScreen from 'client/components/LoadingScreen'
|
||||
import { AuthLayout } from 'client/components/HOC'
|
||||
import { isDevelopment } from 'client/utils'
|
||||
import { _APPS } from 'client/constants'
|
||||
|
||||
@ -42,27 +42,14 @@ const MESSAGE_PROVISION_SUCCESS_CREATED = 'Provision successfully created'
|
||||
*/
|
||||
const ProvisionApp = () => {
|
||||
const { getProvisionSocket } = useSocket()
|
||||
const { isLogged, jwt, firstRender } = useAuth()
|
||||
const { getAuthUser, logout } = useAuthApi()
|
||||
const { isLogged, jwt } = useAuth()
|
||||
|
||||
const { appTitle, zone } = useGeneral()
|
||||
const { changeAppTitle, enqueueSuccess } = useGeneralApi()
|
||||
|
||||
const queryProps = [undefined, { skip: !jwt }]
|
||||
provisionApi.endpoints.getProvisionTemplates.useQuery(...queryProps)
|
||||
providerApi.endpoints.getProviderConfig.useQuery(...queryProps)
|
||||
const { zone } = useGeneral()
|
||||
const { enqueueSuccess, changeAppTitle } = useGeneralApi()
|
||||
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
appTitle !== APP_NAME && changeAppTitle(APP_NAME)
|
||||
|
||||
try {
|
||||
jwt && getAuthUser()
|
||||
} catch {
|
||||
logout()
|
||||
}
|
||||
})()
|
||||
}, [jwt])
|
||||
changeAppTitle(APP_NAME)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!jwt || !zone) return
|
||||
@ -86,12 +73,13 @@ const ProvisionApp = () => {
|
||||
[]
|
||||
)
|
||||
|
||||
if (jwt && firstRender) {
|
||||
return <LoadingScreen />
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AuthLayout
|
||||
subscriptions={[
|
||||
provisionApi.endpoints.getProvisionTemplates,
|
||||
providerApi.endpoints.getProviderConfig,
|
||||
]}
|
||||
>
|
||||
{isLogged && (
|
||||
<>
|
||||
<Sidebar endpoints={endpoints} />
|
||||
@ -99,7 +87,7 @@ const ProvisionApp = () => {
|
||||
</>
|
||||
)}
|
||||
<Router redirectWhenAuth={PATH.DASHBOARD} endpoints={endpoints} />
|
||||
</>
|
||||
</AuthLayout>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useEffect, useMemo, JSXElementConstructor } from 'react'
|
||||
import { useEffect, useMemo, ReactElement } from 'react'
|
||||
|
||||
import Router from 'client/router'
|
||||
import {
|
||||
@ -24,13 +24,12 @@ import {
|
||||
import { ENDPOINTS as ONE_ENDPOINTS } from 'client/apps/sunstone/routesOne'
|
||||
import { ENDPOINTS as DEV_ENDPOINTS } from 'client/router/dev'
|
||||
|
||||
import { useGeneral, useGeneralApi } from 'client/features/General'
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
import { useAuth, useViews } from 'client/features/Auth'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import systemApi from 'client/features/OneApi/system'
|
||||
|
||||
import Sidebar from 'client/components/Sidebar'
|
||||
import Notifier from 'client/components/Notifier'
|
||||
import LoadingScreen from 'client/components/LoadingScreen'
|
||||
import { AuthLayout } from 'client/components/HOC'
|
||||
import { isDevelopment } from 'client/utils'
|
||||
import { _APPS } from 'client/constants'
|
||||
|
||||
@ -39,47 +38,38 @@ export const APP_NAME = _APPS.sunstone.name
|
||||
/**
|
||||
* Sunstone App component.
|
||||
*
|
||||
* @returns {JSXElementConstructor} App rendered.
|
||||
* @returns {ReactElement} App rendered.
|
||||
*/
|
||||
const SunstoneApp = () => {
|
||||
const { isLogged, jwt, firstRender, view } = useAuth()
|
||||
const { getAuthUser, logout } = useAuthApi()
|
||||
|
||||
const { appTitle } = useGeneral()
|
||||
const { changeAppTitle } = useGeneralApi()
|
||||
|
||||
const queryProps = [undefined, { skip: !jwt }]
|
||||
systemApi.endpoints.getOneConfig.useQuery(...queryProps)
|
||||
systemApi.endpoints.getSunstoneConfig.useQuery(...queryProps)
|
||||
const views = systemApi.endpoints.getSunstoneViews.useQuery(...queryProps)
|
||||
const { isLogged } = useAuth()
|
||||
const { views, view } = useViews()
|
||||
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
appTitle !== APP_NAME && changeAppTitle(APP_NAME)
|
||||
changeAppTitle(APP_NAME)
|
||||
}, [])
|
||||
|
||||
try {
|
||||
jwt && getAuthUser()
|
||||
} catch {
|
||||
logout()
|
||||
}
|
||||
})()
|
||||
}, [jwt])
|
||||
|
||||
const endpoints = useMemo(
|
||||
() => [
|
||||
const endpoints = useMemo(() => {
|
||||
const fixedEndpoints = [
|
||||
...ENDPOINTS,
|
||||
...(view ? getEndpointsByView(views?.data?.[view], ONE_ENDPOINTS) : []),
|
||||
...(isDevelopment() ? DEV_ENDPOINTS : []),
|
||||
],
|
||||
[view]
|
||||
)
|
||||
]
|
||||
|
||||
if (jwt && firstRender) {
|
||||
return <LoadingScreen />
|
||||
}
|
||||
if (!view) return fixedEndpoints
|
||||
|
||||
const viewEndpoints = getEndpointsByView(views?.[view], ONE_ENDPOINTS)
|
||||
|
||||
return fixedEndpoints.concat(viewEndpoints)
|
||||
}, [view])
|
||||
|
||||
return (
|
||||
<>
|
||||
<AuthLayout
|
||||
subscriptions={[
|
||||
systemApi.endpoints.getOneConfig,
|
||||
systemApi.endpoints.getSunstoneConfig,
|
||||
systemApi.endpoints.getSunstoneViews,
|
||||
]}
|
||||
>
|
||||
{isLogged && (
|
||||
<>
|
||||
<Sidebar endpoints={endpoints} />
|
||||
@ -87,7 +77,7 @@ const SunstoneApp = () => {
|
||||
</>
|
||||
)}
|
||||
<Router redirectWhenAuth={PATH.DASHBOARD} endpoints={endpoints} />
|
||||
</>
|
||||
</AuthLayout>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,12 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { useMemo, JSXElementConstructor } from 'react'
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { useFormContext, FieldErrors } from 'react-hook-form'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
|
||||
import Tabs from 'client/components/Tabs'
|
||||
@ -36,13 +36,13 @@ import Numa from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfi
|
||||
import { STEP_ID as GENERAL_ID } from 'client/components/Forms/VmTemplate/CreateForm/Steps/General'
|
||||
import { SCHEMA } from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/schema'
|
||||
import { getActionsAvailable as getSectionsAvailable } from 'client/models/Helper'
|
||||
import { T } from 'client/constants'
|
||||
import { T, RESOURCE_NAMES } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @typedef {object} TabType
|
||||
* @property {string} id - Id will be to use in view yaml to hide/display the tab
|
||||
* @property {string} name - Label of tab
|
||||
* @property {JSXElementConstructor} Content - Content tab
|
||||
* @property {ReactElement} Content - Content tab
|
||||
* @property {object} [icon] - Icon of tab
|
||||
* @property {function(FieldErrors):boolean} [getError] - Returns `true` if the tab contains an error in form
|
||||
*/
|
||||
@ -67,12 +67,13 @@ const Content = ({ data, setFormData }) => {
|
||||
formState: { errors },
|
||||
control,
|
||||
} = useFormContext()
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
|
||||
const hypervisor = useMemo(() => watch(`${GENERAL_ID}.HYPERVISOR`), [])
|
||||
|
||||
const sectionsAvailable = useMemo(() => {
|
||||
const dialog = getResourceView('VM-TEMPLATE')?.dialogs?.create_dialog
|
||||
const resource = RESOURCE_NAMES.VM_TEMPLATE
|
||||
const dialog = getResourceView(resource)?.dialogs?.create_dialog
|
||||
|
||||
return getSectionsAvailable(dialog, hypervisor)
|
||||
}, [view])
|
||||
|
@ -18,7 +18,7 @@ import { useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useWatch } from 'react-hook-form'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
import useStyles from 'client/components/Forms/VmTemplate/CreateForm/Steps/General/styles'
|
||||
|
||||
@ -28,17 +28,18 @@ import {
|
||||
SECTIONS,
|
||||
} from 'client/components/Forms/VmTemplate/CreateForm/Steps/General/schema'
|
||||
import { getActionsAvailable as getSectionsAvailable } from 'client/models/Helper'
|
||||
import { T } from 'client/constants'
|
||||
import { T, RESOURCE_NAMES } from 'client/constants'
|
||||
|
||||
export const STEP_ID = 'general'
|
||||
|
||||
const Content = ({ isUpdate }) => {
|
||||
const classes = useStyles()
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const hypervisor = useWatch({ name: `${STEP_ID}.HYPERVISOR` })
|
||||
|
||||
const sections = useMemo(() => {
|
||||
const dialog = getResourceView('VM-TEMPLATE')?.dialogs?.create_dialog
|
||||
const resource = RESOURCE_NAMES.VM_TEMPLATE
|
||||
const dialog = getResourceView(resource)?.dialogs?.create_dialog
|
||||
const sectionsAvailable = getSectionsAvailable(dialog, hypervisor)
|
||||
|
||||
return SECTIONS(hypervisor, isUpdate).filter(
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
import useStyles from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/styles'
|
||||
|
||||
@ -27,18 +27,19 @@ import {
|
||||
FIELDS,
|
||||
} from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema'
|
||||
import { getActionsAvailable as getSectionsAvailable } from 'client/models/Helper'
|
||||
import { T } from 'client/constants'
|
||||
import { T, RESOURCE_NAMES } from 'client/constants'
|
||||
|
||||
export const STEP_ID = 'configuration'
|
||||
|
||||
const Content = () => {
|
||||
const classes = useStyles()
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { watch } = useFormContext()
|
||||
|
||||
const sections = useMemo(() => {
|
||||
const hypervisor = watch(`${TEMPLATE_ID}[0].TEMPLATE.HYPERVISOR`)
|
||||
const dialog = getResourceView('VM-TEMPLATE')?.dialogs?.instantiate_dialog
|
||||
const resource = RESOURCE_NAMES.VM_TEMPLATE
|
||||
const dialog = getResourceView(resource)?.dialogs?.instantiate_dialog
|
||||
const sectionsAvailable = getSectionsAvailable(dialog, hypervisor)
|
||||
|
||||
return FIELDS(hypervisor).filter(({ id }) => sectionsAvailable.includes(id))
|
||||
|
@ -17,9 +17,9 @@
|
||||
import { useMemo } from 'react'
|
||||
import { string, number, boolean } from 'yup'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { getActionsAvailable } from 'client/models/Helper'
|
||||
import { INPUT_TYPES, VM_ACTIONS } from 'client/constants'
|
||||
import { RESOURCE_NAMES, INPUT_TYPES, VM_ACTIONS } from 'client/constants'
|
||||
|
||||
const NAME = {
|
||||
name: 'name',
|
||||
@ -51,10 +51,11 @@ const HOLD = {
|
||||
label: 'Start VM on hold state',
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
htmlType: () => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
|
||||
return useMemo(() => {
|
||||
const actions = getResourceView('VM')?.actions
|
||||
const resource = RESOURCE_NAMES.VM
|
||||
const actions = getResourceView(resource)?.actions
|
||||
const actionsAvailable = getActionsAvailable(actions)
|
||||
|
||||
return (
|
||||
|
@ -19,7 +19,7 @@ import PropTypes from 'prop-types'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
import { SystemShut as OsIcon } from 'iconoir-react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
|
||||
import Tabs from 'client/components/Tabs'
|
||||
@ -33,7 +33,7 @@ import BootOrder from 'client/components/Forms/VmTemplate/CreateForm/Steps/Extra
|
||||
import { STEP_ID as TEMPLATE_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable'
|
||||
import { SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema'
|
||||
import { getActionsAvailable as getSectionsAvailable } from 'client/models/Helper'
|
||||
import { T } from 'client/constants'
|
||||
import { T, RESOURCE_NAMES } from 'client/constants'
|
||||
|
||||
export const STEP_ID = 'extra'
|
||||
|
||||
@ -58,12 +58,13 @@ const Content = ({ data, setFormData }) => {
|
||||
formState: { errors },
|
||||
control,
|
||||
} = useFormContext()
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
|
||||
const hypervisor = useMemo(() => watch(`${TEMPLATE_ID}.0.HYPERVISOR`), [])
|
||||
|
||||
const sectionsAvailable = useMemo(() => {
|
||||
const dialog = getResourceView('VM-TEMPLATE')?.dialogs?.instantiate_dialog
|
||||
const resource = RESOURCE_NAMES.VM_TEMPLATE
|
||||
const dialog = getResourceView(resource)?.dialogs?.instantiate_dialog
|
||||
|
||||
return getSectionsAvailable(dialog, hypervisor)
|
||||
}, [view])
|
||||
|
82
src/fireedge/src/client/components/HOC/AuthLayout.js
Normal file
82
src/fireedge/src/client/components/HOC/AuthLayout.js
Normal file
@ -0,0 +1,82 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* 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 { useEffect, ReactElement } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
import { authApi } from 'client/features/AuthApi'
|
||||
import groupApi from 'client/features/OneApi/group'
|
||||
import FullscreenProgress from 'client/components/LoadingScreen'
|
||||
import { findStorageData } from 'client/utils'
|
||||
import { JWT_NAME } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Renders loading screen while validate JWT.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {object[]} [props.subscriptions] - Subscriptions after login
|
||||
* @param {ReactElement} [props.children] - Children
|
||||
* @returns {ReactElement} App rendered.
|
||||
*/
|
||||
const AuthLayout = ({ subscriptions = [], children }) => {
|
||||
const dispatch = useDispatch()
|
||||
const { changeJwt, stopFirstRender } = useAuthApi()
|
||||
const { jwt, isLogged, isLoginInProgress, firstRender } = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
if (!jwt) return
|
||||
|
||||
const endpoints = [
|
||||
groupApi.endpoints.getGroups,
|
||||
authApi.endpoints.getAuthUser,
|
||||
...subscriptions,
|
||||
].map((endpoint) =>
|
||||
dispatch(endpoint.initiate(undefined, { forceRefetch: true }))
|
||||
)
|
||||
|
||||
return () => {
|
||||
endpoints.forEach((endpoint) => {
|
||||
endpoint.unsubscribe()
|
||||
})
|
||||
}
|
||||
}, [dispatch, jwt])
|
||||
|
||||
useEffect(() => {
|
||||
if (!jwt) {
|
||||
const token = findStorageData(JWT_NAME)
|
||||
token && changeJwt(token)
|
||||
}
|
||||
|
||||
// first rendering on client
|
||||
stopFirstRender()
|
||||
}, [])
|
||||
|
||||
if ((jwt && !isLoginInProgress && !isLogged) || firstRender) {
|
||||
return <FullscreenProgress />
|
||||
}
|
||||
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
AuthLayout.propTypes = {
|
||||
subscriptions: PropTypes.array,
|
||||
children: PropTypes.any,
|
||||
}
|
||||
|
||||
AuthLayout.displayName = 'AuthLayout'
|
||||
|
||||
export default AuthLayout
|
@ -56,7 +56,7 @@ const RemoveScript = () => {
|
||||
|
||||
const TranslateProvider = ({ children = [] }) => {
|
||||
const [hash, setHash] = useState({})
|
||||
const { settings: { lang } = {} } = useAuth()
|
||||
const { settings: { LANG: lang } = {} } = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
GenerateScript(lang, setHash)
|
||||
@ -71,14 +71,8 @@ const TranslateProvider = ({ children = [] }) => {
|
||||
GenerateScript(language, setHash)
|
||||
}
|
||||
|
||||
const value = {
|
||||
lang,
|
||||
hash,
|
||||
changeLang,
|
||||
}
|
||||
|
||||
return (
|
||||
<TranslateContext.Provider value={value}>
|
||||
<TranslateContext.Provider value={{ lang, hash, changeLang }}>
|
||||
{children}
|
||||
</TranslateContext.Provider>
|
||||
)
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
export { default as AuthLayout } from 'client/components/HOC/AuthLayout'
|
||||
export { default as ConditionalWrap } from 'client/components/HOC/ConditionalWrap'
|
||||
export { default as InternalLayout } from 'client/components/HOC/InternalLayout'
|
||||
export * from 'client/components/HOC/Translate'
|
||||
|
@ -16,10 +16,11 @@
|
||||
import { useMemo, memo, JSXElementConstructor } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Button } from '@mui/material'
|
||||
import { Button, Stack, CircularProgress } from '@mui/material'
|
||||
import { Group as GroupIcon, VerifiedBadge as SelectIcon } from 'iconoir-react'
|
||||
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useChangeAuthGroupMutation } from 'client/features/AuthApi'
|
||||
import Search from 'client/components/Search'
|
||||
import HeaderPopover from 'client/components/Header/Popover'
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
@ -28,10 +29,8 @@ import { T, FILTER_POOL } from 'client/constants'
|
||||
const { ALL_RESOURCES, PRIMARY_GROUP_RESOURCES } = FILTER_POOL
|
||||
|
||||
const ButtonGroup = memo(
|
||||
({ group, handleClick }) => {
|
||||
const { changeGroup } = useAuthApi()
|
||||
({ group, handleClick, disabled }) => {
|
||||
const { user, filterPool } = useAuth()
|
||||
|
||||
const { ID, NAME } = group
|
||||
|
||||
const isSelected =
|
||||
@ -41,12 +40,10 @@ const ButtonGroup = memo(
|
||||
return (
|
||||
<Button
|
||||
fullWidth
|
||||
disabled={disabled}
|
||||
color="debug"
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
ID && changeGroup({ id: user.ID, group: ID })
|
||||
handleClick()
|
||||
}}
|
||||
onClick={handleClick}
|
||||
sx={{
|
||||
color: (theme) => theme.palette.text.primary,
|
||||
justifyContent: 'start',
|
||||
@ -58,7 +55,8 @@ const ButtonGroup = memo(
|
||||
</Button>
|
||||
)
|
||||
},
|
||||
(prev, next) => prev.group.ID === next.group.ID
|
||||
(prev, next) =>
|
||||
prev.group.ID === next.group.ID && prev.disabled === next.disabled
|
||||
)
|
||||
|
||||
/**
|
||||
@ -68,6 +66,7 @@ const ButtonGroup = memo(
|
||||
* @returns {JSXElementConstructor} Returns group list
|
||||
*/
|
||||
const Group = () => {
|
||||
const [changeGroup, { isLoading }] = useChangeAuthGroupMutation()
|
||||
const { user, groups } = useAuth()
|
||||
|
||||
const sortGroupAsMainFirst = (a, b) =>
|
||||
@ -86,7 +85,12 @@ const Group = () => {
|
||||
icon={<GroupIcon />}
|
||||
tooltip={<Translate word={T.SwitchGroup} />}
|
||||
buttonProps={{ 'data-cy': 'header-group-button' }}
|
||||
headerTitle={<Translate word={T.SwitchGroup} />}
|
||||
headerTitle={
|
||||
<Stack direction="row" alignItems="center" gap="1em" component="span">
|
||||
<Translate word={T.SwitchGroup} />
|
||||
{isLoading && <CircularProgress size={20} />}
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
{({ handleClose }) => (
|
||||
<Search
|
||||
@ -101,7 +105,11 @@ const Group = () => {
|
||||
<ButtonGroup
|
||||
key={`switcher-group-${group?.ID}`}
|
||||
group={group}
|
||||
handleClick={handleClose}
|
||||
disabled={isLoading}
|
||||
handleClick={async () => {
|
||||
group?.ID && (await changeGroup({ group: group.ID }))
|
||||
handleClose()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -111,11 +119,9 @@ const Group = () => {
|
||||
}
|
||||
|
||||
ButtonGroup.propTypes = {
|
||||
group: PropTypes.shape({
|
||||
ID: PropTypes.string,
|
||||
NAME: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
}).isRequired,
|
||||
group: PropTypes.object,
|
||||
handleClick: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
}
|
||||
|
||||
ButtonGroup.displayName = 'ButtonGroup'
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
import { MenuItem, MenuList, Link } from '@mui/material'
|
||||
import { ProfileCircled as UserIcon } from 'iconoir-react'
|
||||
@ -28,7 +28,7 @@ import { T, APPS, APP_URL } from 'client/constants'
|
||||
/**
|
||||
* Menu with actions about App: signOut, etc.
|
||||
*
|
||||
* @returns {JSXElementConstructor} Returns user actions list
|
||||
* @returns {ReactElement} Returns user actions list
|
||||
*/
|
||||
const User = () => {
|
||||
const { user } = useAuth()
|
||||
|
@ -22,7 +22,7 @@ import {
|
||||
VerifiedBadge as SelectIcon,
|
||||
} from 'iconoir-react'
|
||||
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
import { useAuthApi, useViews } from 'client/features/Auth'
|
||||
import Search from 'client/components/Search'
|
||||
import HeaderPopover from 'client/components/Header/Popover'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
@ -31,7 +31,7 @@ import { T } from 'client/constants'
|
||||
const ButtonView = memo(
|
||||
({ view, handleClick }) => {
|
||||
const { changeView } = useAuthApi()
|
||||
const { view: currentView } = useAuth()
|
||||
const { view: currentView } = useViews()
|
||||
const isCurrentView = currentView === view
|
||||
|
||||
return (
|
||||
@ -73,7 +73,7 @@ ButtonView.displayName = 'ButtonView'
|
||||
* @returns {ReactElement} Returns interface views list
|
||||
*/
|
||||
const View = () => {
|
||||
const { view: currentView, views = {} } = useAuth()
|
||||
const { view: currentView, views = {} } = useViews()
|
||||
const viewNames = useMemo(() => Object.keys(views), [currentView])
|
||||
|
||||
return (
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
import { Box } from '@mui/material'
|
||||
import { OpenNebulaLogo } from 'client/components/Icons'
|
||||
@ -21,21 +21,10 @@ import { OpenNebulaLogo } from 'client/components/Icons'
|
||||
/**
|
||||
* Component with OpenNebula logo as spinner in full width and height.
|
||||
*
|
||||
* @returns {JSXElementConstructor} Container with logo inside
|
||||
* @returns {ReactElement} Container with logo inside
|
||||
*/
|
||||
const LoadingScreen = () => (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: '100vh',
|
||||
backgroundColor: 'background.default',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'fixed',
|
||||
zIndex: 10000,
|
||||
}}
|
||||
>
|
||||
<Box className="loading_screen">
|
||||
<OpenNebulaLogo width={360} height={360} spinner withText />
|
||||
</Box>
|
||||
)
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import { ReactElement } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Redirect, Route } from 'react-router-dom'
|
||||
@ -23,14 +23,13 @@ import { useAuth } from 'client/features/Auth'
|
||||
* Public route.
|
||||
*
|
||||
* @param {object} props - Route props
|
||||
* @param {JSXElementConstructor} props.redirectWhenAuth
|
||||
* @param {ReactElement} props.redirectWhenAuth
|
||||
* - Route to redirect in case of user is authenticated
|
||||
* @returns {Redirect|Route}
|
||||
* - If current user is authenticated, then redirect to private route
|
||||
*/
|
||||
const NoAuthRoute = ({ redirectWhenAuth, ...props }) => {
|
||||
const { isLogged, isLoginInProgress, isLoading } = useAuth()
|
||||
const isAuthenticated = isLogged && !isLoginInProgress && !isLoading
|
||||
const { isLogged: isAuthenticated } = useAuth()
|
||||
|
||||
return isAuthenticated ? (
|
||||
<Redirect to={redirectWhenAuth} />
|
||||
|
@ -13,10 +13,9 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useEffect } from 'react'
|
||||
import { Redirect, Route } from 'react-router-dom'
|
||||
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
|
||||
/**
|
||||
* Private route.
|
||||
@ -26,14 +25,9 @@ import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
* - If current user isn't authenticated, then redirect to landing page
|
||||
*/
|
||||
const ProtectedRoute = (props) => {
|
||||
const { isLogged, jwt } = useAuth()
|
||||
const { getAuthUser } = useAuthApi()
|
||||
const { isLogged: isAuthenticated } = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
jwt && getAuthUser()
|
||||
}, [])
|
||||
|
||||
return isLogged ? <Route {...props} /> : <Redirect to="/" />
|
||||
return isAuthenticated ? <Route {...props} /> : <Redirect to="/" />
|
||||
}
|
||||
|
||||
export default ProtectedRoute
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetClustersQuery } from 'client/features/OneApi/cluster'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -34,7 +34,7 @@ const ClustersTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetClustersQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetDatastoresQuery } from 'client/features/OneApi/datastore'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -39,7 +39,7 @@ const DatastoresTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -20,8 +20,8 @@ import { GlobalAction } from 'client/components/Tables/Enhanced/Utils/GlobalActi
|
||||
/**
|
||||
* Add filters defined in view yaml to columns.
|
||||
*
|
||||
* @param {object} config -
|
||||
* @param {object[]} config.filters - List of criteria to filter the columns.
|
||||
* @param {object} config - Config
|
||||
* @param {object} config.filters - List of criteria to filter the columns.
|
||||
* @param {Column[]} config.columns - Columns
|
||||
* @returns {object} Column with filters
|
||||
*/
|
||||
@ -64,7 +64,7 @@ export const createCategoryFilter = (title) => ({
|
||||
* Add filters defined in view yaml to bulk actions.
|
||||
*
|
||||
* @param {object} params - Config parameters
|
||||
* @param {object[]} params.filters - Which buttons are visible to operate over the resources
|
||||
* @param {object} params.filters - Which buttons are visible to operate over the resources
|
||||
* @param {GlobalAction[]} params.actions - Actions
|
||||
* @returns {object} Action with filters
|
||||
*/
|
||||
@ -82,7 +82,7 @@ export const createActions = ({ filters = {}, actions = [] }) => {
|
||||
if (accessor) return action
|
||||
|
||||
const groupActions = options?.filter(
|
||||
(option) => filters[String(option.accessor?.toLowerCase())] === true
|
||||
(option) => filters[`${option.accessor?.toLowerCase()}`] === true
|
||||
)
|
||||
|
||||
return groupActions?.length > 0
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetGroupsQuery } from 'client/features/OneApi/group'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -34,7 +34,7 @@ const GroupsTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetGroupsQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetHostsQuery } from 'client/features/OneApi/host'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -39,7 +39,7 @@ const HostsTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetImagesQuery } from 'client/features/OneApi/image'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -34,7 +34,7 @@ const ImagesTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetImagesQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -18,7 +18,7 @@ import { useMemo } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { AddSquare, CloudDownload } from 'iconoir-react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { useExportAppMutation } from 'client/features/OneApi/marketplaceApp'
|
||||
@ -48,7 +48,7 @@ MessageToConfirmAction.displayName = 'MessageToConfirmAction'
|
||||
|
||||
const Actions = () => {
|
||||
const history = useHistory()
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { enqueueSuccess } = useGeneralApi()
|
||||
const [exportApp] = useExportAppMutation()
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetMarketplaceAppsQuery } from 'client/features/OneApi/marketplaceApp'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -34,7 +34,7 @@ const MarketplaceAppsTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetMarketplaceAppsQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetMarketplacesQuery } from 'client/features/OneApi/marketplace'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -41,7 +41,7 @@ const MarketplacesTable = ({ filter, ...props }) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetUsersQuery } from 'client/features/OneApi/user'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -34,7 +34,7 @@ const UsersTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetUsersQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetVNTemplatesQuery } from 'client/features/OneApi/networkTemplate'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -35,7 +35,7 @@ const VNetworkTemplatesTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetVNTemplatesQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetVNetworksQuery } from 'client/features/OneApi/network'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -39,7 +39,7 @@ const VNetworksTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetVRoutersQuery } from 'client/features/OneApi/vrouter'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -34,7 +34,7 @@ const VRoutersTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetVRoutersQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -26,7 +26,7 @@ import {
|
||||
Cart,
|
||||
} from 'iconoir-react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import {
|
||||
useLockTemplateMutation,
|
||||
useUnlockTemplateMutation,
|
||||
@ -66,7 +66,7 @@ MessageToConfirmAction.displayName = 'MessageToConfirmAction'
|
||||
|
||||
const Actions = () => {
|
||||
const history = useHistory()
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const [lock] = useLockTemplateMutation()
|
||||
const [unlock] = useUnlockTemplateMutation()
|
||||
const [clone] = useCloneTemplateMutation()
|
||||
@ -281,7 +281,7 @@ const Actions = () => {
|
||||
const marketplaceAppActions = useMemo(
|
||||
() =>
|
||||
createActions({
|
||||
filters: getResourceView('MARKETPLACE-APP')?.actions,
|
||||
filters: getResourceView(RESOURCE_NAMES.APP)?.actions,
|
||||
actions: [
|
||||
{
|
||||
accessor: MARKETPLACE_APP_ACTIONS.CREATE_DIALOG,
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetTemplatesQuery } from 'client/features/OneApi/vmTemplate'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -34,7 +34,7 @@ const VmTemplatesTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetTemplatesQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -28,7 +28,7 @@ import {
|
||||
Cart,
|
||||
} from 'iconoir-react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { useGetDatastoresQuery } from 'client/features/OneApi/datastore'
|
||||
import {
|
||||
@ -106,7 +106,7 @@ const MessageToConfirmAction = (rows) => (
|
||||
*/
|
||||
const Actions = () => {
|
||||
const history = useHistory()
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { enqueueSuccess } = useGeneralApi()
|
||||
|
||||
const [saveAsTemplate] = useSaveAsTemplateMutation()
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useState, useEffect, useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetVmsQuery } from 'client/features/OneApi/vm'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -43,7 +43,7 @@ const VmsTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const [totalData, setTotalData] = useState(() => [])
|
||||
const [args, setArgs] = useState(() => INITIAL_ARGS)
|
||||
const { data, isSuccess, isFetching } = useGetVmsQuery(args, {
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useMemo, ReactElement } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetZonesQuery } from 'client/features/OneApi/zone'
|
||||
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
@ -34,7 +34,7 @@ const ZonesTable = (props) => {
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetZonesQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetClusterQuery } from 'client/features/OneApi/cluster'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -31,7 +31,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const ClusterTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetClusterQuery({ id })
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetDatastoreQuery } from 'client/features/OneApi/datastore'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -31,7 +31,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const DatastoreTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetDatastoreQuery({ id })
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetGroupQuery } from 'client/features/OneApi/group'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -31,7 +31,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const GroupTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetGroupQuery(id)
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetImageQuery } from 'client/features/OneApi/image'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -31,7 +31,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const ImageTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetImageQuery({ id })
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetMarketplaceQuery } from 'client/features/OneApi/marketplace'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -31,7 +31,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const MarketplaceTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetMarketplaceQuery({ id })
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetMarketplaceAppQuery } from 'client/features/OneApi/marketplaceApp'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -33,7 +33,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const MarketplaceAppTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetMarketplaceAppQuery(id)
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetUserQuery } from 'client/features/OneApi/user'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -31,7 +31,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const UserTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetUserQuery(id)
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetVNetworkQuery } from 'client/features/OneApi/network'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -31,7 +31,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const VNetworkTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetVNetworkQuery({ id })
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetVNTemplateQuery } from 'client/features/OneApi/networkTemplate'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -31,7 +31,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const VNetTemplateTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetVNTemplateQuery({ id })
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetVmQuery } from 'client/features/OneApi/vm'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -45,7 +45,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const VmTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetVmQuery(id)
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetTemplateQuery } from 'client/features/OneApi/vmTemplate'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -33,7 +33,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const VmTemplateTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetTemplateQuery({ id })
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -17,7 +17,7 @@ import { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { LinearProgress } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetZoneQuery } from 'client/features/OneApi/zone'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
@ -31,7 +31,7 @@ const getTabComponent = (tabName) =>
|
||||
}[tabName])
|
||||
|
||||
const ZoneTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useAuth()
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading } = useGetZoneQuery(id)
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
|
@ -27,7 +27,6 @@ export const BY = {
|
||||
url: 'https://opennebula.io/',
|
||||
}
|
||||
|
||||
export const TIME_HIDE_LOGO = 1500
|
||||
export const _APPS = defaultApps
|
||||
export const APPS = Object.keys(defaultApps)
|
||||
export const APPS_IN_BETA = [_APPS.sunstone.name]
|
||||
|
@ -37,12 +37,12 @@ import { T } from 'client/constants'
|
||||
|
||||
/** @returns {ReactElement} Provision dashboard container */
|
||||
function ProvisionDashboard() {
|
||||
const { settings: { disableanimations } = {} } = useAuth()
|
||||
const { settings: { DISABLE_ANIMATIONS } = {} } = useAuth()
|
||||
|
||||
return (
|
||||
<Container
|
||||
disableGutters
|
||||
{...(stringToBoolean(disableanimations) && {
|
||||
{...(stringToBoolean(DISABLE_ANIMATIONS) && {
|
||||
sx: {
|
||||
'& *, & *::before, & *::after': {
|
||||
animation: 'none !important',
|
||||
|
@ -35,12 +35,12 @@ import { T } from 'client/constants'
|
||||
|
||||
/** @returns {ReactElement} Sunstone dashboard container */
|
||||
function SunstoneDashboard() {
|
||||
const { settings: { disableanimations } = {} } = useAuth()
|
||||
const { settings: { DISABLE_ANIMATIONS } = {} } = useAuth()
|
||||
|
||||
return (
|
||||
<Container
|
||||
disableGutters
|
||||
{...(stringToBoolean(disableanimations) && {
|
||||
{...(stringToBoolean(DISABLE_ANIMATIONS) && {
|
||||
sx: {
|
||||
'& *, & *::before, & *::after': {
|
||||
animation: 'none !important',
|
||||
|
@ -24,7 +24,10 @@ import {
|
||||
} from '@mui/material'
|
||||
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
import { useFetch } from 'client/hooks'
|
||||
import {
|
||||
useLoginMutation,
|
||||
useChangeAuthGroupMutation,
|
||||
} from 'client/features/AuthApi'
|
||||
|
||||
import Form from 'client/containers/Login/Form'
|
||||
import * as FORMS from 'client/containers/Login/schema'
|
||||
@ -41,14 +44,14 @@ function Login() {
|
||||
const classes = loginStyles()
|
||||
const isMobile = useMediaQuery((theme) => theme.breakpoints.only('xs'))
|
||||
|
||||
const {
|
||||
error,
|
||||
isLoading: authLoading,
|
||||
isLoginInProgress: needGroupToContinue,
|
||||
} = useAuth()
|
||||
const { error: otherError, isLoginInProgress: needGroupToContinue } =
|
||||
useAuth()
|
||||
const { logout } = useAuthApi()
|
||||
|
||||
const { login, getAuthUser, changeGroup, logout } = useAuthApi()
|
||||
const { fetchRequest: fetchLogin, loading: loginIsLoading } = useFetch(login)
|
||||
const [changeAuthGroup, changeAuthGroupState] = useChangeAuthGroupMutation()
|
||||
const [login, loginState] = useLoginMutation()
|
||||
const isLoading = loginState.isLoading || changeAuthGroupState.isLoading
|
||||
const errorMessage = loginState.error?.data?.message ?? otherError
|
||||
|
||||
const [dataUserForm, setDataUserForm] = useState(undefined)
|
||||
const [step, setStep] = useState(() =>
|
||||
@ -56,19 +59,20 @@ function Login() {
|
||||
)
|
||||
|
||||
const handleSubmitUser = async (dataForm) => {
|
||||
const response = await fetchLogin({ ...dataUserForm, ...dataForm })
|
||||
const { jwt, user, isLoginInProgress } = response || {}
|
||||
try {
|
||||
const response = await login({ ...dataUserForm, ...dataForm }).unwrap()
|
||||
const { jwt, user, isLoginInProgress } = response || {}
|
||||
|
||||
if (jwt && isLoginInProgress) {
|
||||
getAuthUser()
|
||||
setStep(STEPS.GROUP_FORM)
|
||||
} else if (!jwt && user?.ID) {
|
||||
setStep(STEPS.FA2_FORM)
|
||||
setDataUserForm(dataForm)
|
||||
}
|
||||
if (jwt && isLoginInProgress) {
|
||||
setStep(STEPS.GROUP_FORM)
|
||||
} else if (!jwt && user?.ID) {
|
||||
setStep(STEPS.FA2_FORM)
|
||||
setDataUserForm(dataForm)
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const handleSubmitGroup = (dataForm) => changeGroup(dataForm)
|
||||
const handleSubmitGroup = (dataForm) => changeAuthGroup(dataForm)
|
||||
|
||||
const handleBack = () => {
|
||||
logout()
|
||||
@ -76,8 +80,6 @@ function Login() {
|
||||
setStep(STEPS.USER_FORM)
|
||||
}
|
||||
|
||||
const isLoading = loginIsLoading || authLoading
|
||||
|
||||
return (
|
||||
<Container
|
||||
component="main"
|
||||
@ -111,7 +113,7 @@ function Login() {
|
||||
onSubmit={handleSubmitUser}
|
||||
resolver={FORMS.FORM_USER_SCHEMA}
|
||||
fields={FORMS.FORM_USER_FIELDS}
|
||||
error={error}
|
||||
error={errorMessage}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
)}
|
||||
@ -125,7 +127,7 @@ function Login() {
|
||||
onSubmit={handleSubmitUser}
|
||||
resolver={FORMS.FORM_2FA_SCHEMA}
|
||||
fields={FORMS.FORM_2FA_FIELDS}
|
||||
error={error}
|
||||
error={errorMessage}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
)}
|
||||
@ -139,7 +141,7 @@ function Login() {
|
||||
onSubmit={handleSubmitGroup}
|
||||
resolver={FORMS.FORM_GROUP_SCHEMA}
|
||||
fields={FORMS.FORM_GROUP_FIELDS}
|
||||
error={error}
|
||||
error={errorMessage}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
)}
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import { ReactElement, useMemo } from 'react'
|
||||
import { Container, Paper, Box, Typography, Divider } from '@mui/material'
|
||||
import { useForm, FormProvider } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers/yup'
|
||||
@ -21,7 +21,8 @@ import { yupResolver } from '@hookform/resolvers/yup'
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
import SubmitButton from 'client/components/FormControl/SubmitButton'
|
||||
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useLazyGetAuthUserQuery } from 'client/features/AuthApi'
|
||||
import { useUpdateUserMutation } from 'client/features/OneApi/user'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { Translate, Tr } from 'client/components/HOC'
|
||||
@ -30,24 +31,28 @@ import { T } from 'client/constants'
|
||||
import { FORM_FIELDS, FORM_SCHEMA } from 'client/containers/Settings/schema'
|
||||
import * as Helper from 'client/models/Helper'
|
||||
|
||||
/** @returns {JSXElementConstructor} Settings container */
|
||||
/** @returns {ReactElement} Settings container */
|
||||
const Settings = () => {
|
||||
const { user, settings } = useAuth()
|
||||
const { getAuthUser } = useAuthApi()
|
||||
const [getAuthUser] = useLazyGetAuthUserQuery()
|
||||
const [updateUser] = useUpdateUserMutation()
|
||||
const { enqueueError } = useGeneralApi()
|
||||
|
||||
const { handleSubmit, setError, reset, formState, ...methods } = useForm({
|
||||
const { handleSubmit, reset, formState, ...methods } = useForm({
|
||||
reValidateMode: 'onSubmit',
|
||||
defaultValues: FORM_SCHEMA.cast(settings),
|
||||
defaultValues: useMemo(() => FORM_SCHEMA.cast(settings), [settings]),
|
||||
resolver: yupResolver(FORM_SCHEMA),
|
||||
})
|
||||
|
||||
const onSubmit = async (dataForm) => {
|
||||
const onSubmit = async (formData) => {
|
||||
try {
|
||||
const template = Helper.jsonToXml({ FIREEDGE: dataForm })
|
||||
const data = FORM_SCHEMA.cast(formData, { isSubmit: true })
|
||||
const template = Helper.jsonToXml({ FIREEDGE: data })
|
||||
await updateUser({ id: user.ID, template })
|
||||
getAuthUser()
|
||||
await getAuthUser()
|
||||
|
||||
// Reset either the entire form state or part of the form state
|
||||
reset(formData)
|
||||
} catch {
|
||||
enqueueError(T.SomethingWrong)
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import {
|
||||
import { getValidationFromFields } from 'client/utils'
|
||||
|
||||
const SCHEME = {
|
||||
name: 'scheme',
|
||||
name: 'SCHEME',
|
||||
label: T.Schema,
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: [
|
||||
@ -40,7 +40,7 @@ const SCHEME = {
|
||||
}
|
||||
|
||||
const LANGUAGES = {
|
||||
name: 'lang',
|
||||
name: 'LANG',
|
||||
label: T.Language,
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: () =>
|
||||
@ -53,7 +53,7 @@ const LANGUAGES = {
|
||||
}
|
||||
|
||||
const DISABLE_ANIMATIONS = {
|
||||
name: 'disableanimations',
|
||||
name: 'DISABLE_ANIMATIONS',
|
||||
label: T.DisableDashboardAnimations,
|
||||
type: INPUT_TYPES.CHECKBOX,
|
||||
validation: boolean()
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
import SunstoneApp from 'client/apps/sunstone'
|
||||
import ProvisionApp from 'client/apps/provision'
|
||||
@ -26,7 +26,7 @@ import { _APPS, APPS } from 'client/constants'
|
||||
* Render App by url: http://<host:port>/fireedge/<APP>.
|
||||
*
|
||||
* @param {object} props - Props from server
|
||||
* @returns {JSXElementConstructor} Returns App
|
||||
* @returns {ReactElement} Returns App
|
||||
*/
|
||||
const DevelopmentApp = (props) => {
|
||||
let appName = ''
|
||||
|
@ -1,135 +0,0 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* 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 { createAction, createAsyncThunk } from '@reduxjs/toolkit'
|
||||
|
||||
import { authService } from 'client/features/Auth/services'
|
||||
import { dismissSnackbar } from 'client/features/General/actions'
|
||||
import apiUser from 'client/features/OneApi/user'
|
||||
|
||||
import { httpCodes } from 'server/utils/constants'
|
||||
import { removeStoreData, storage } from 'client/utils'
|
||||
import { FILTER_POOL, JWT_NAME, ONEADMIN_ID, T } from 'client/constants'
|
||||
|
||||
export const login = createAsyncThunk(
|
||||
'auth/login',
|
||||
async ({ remember, ...user }, { rejectWithValue, dispatch }) => {
|
||||
try {
|
||||
const response = await authService.login({ ...user, remember })
|
||||
const { id, token } = response
|
||||
const isOneAdmin = id === ONEADMIN_ID
|
||||
|
||||
if (token) {
|
||||
storage(JWT_NAME, token, remember)
|
||||
dispatch(dismissSnackbar({ dismissAll: true }))
|
||||
}
|
||||
|
||||
return {
|
||||
jwt: token,
|
||||
user: { ID: id },
|
||||
isOneAdmin,
|
||||
isLoginInProgress: !!token && !isOneAdmin,
|
||||
}
|
||||
} catch (error) {
|
||||
const { message, data, statusText } = error
|
||||
|
||||
return rejectWithValue({ error: message ?? data?.message ?? statusText })
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const getUser = createAsyncThunk(
|
||||
'auth/user',
|
||||
async (_, { dispatch, getState }) => {
|
||||
try {
|
||||
const { auth = {} } = getState()
|
||||
|
||||
const user = await authService.getUser()
|
||||
const isOneAdmin = user?.ID === ONEADMIN_ID
|
||||
const userSettings = user?.TEMPLATE?.FIREEDGE ?? {}
|
||||
|
||||
// Merge user settings with the existing one
|
||||
const settings = {
|
||||
...auth?.settings,
|
||||
...Object.entries(userSettings).reduce(
|
||||
(res, [key, value]) => ({
|
||||
...res,
|
||||
[String(key).toLowerCase()]: value,
|
||||
}),
|
||||
{}
|
||||
),
|
||||
}
|
||||
|
||||
return { user, settings, isOneAdmin }
|
||||
} catch (error) {
|
||||
console.log({ error })
|
||||
dispatch(logout(T.SessionExpired))
|
||||
}
|
||||
},
|
||||
{
|
||||
condition: (_, { getState }) => {
|
||||
const { isLoading } = getState().auth
|
||||
|
||||
return !isLoading
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
export const logout = createAction('logout', (errorMessage) => {
|
||||
removeStoreData([JWT_NAME])
|
||||
|
||||
return { error: errorMessage }
|
||||
})
|
||||
|
||||
export const changeFilter = createAction(
|
||||
'auth/change-filter',
|
||||
(filterPool) => ({ payload: { filterPool, isLoginInProgress: false } })
|
||||
)
|
||||
|
||||
export const changeGroup = createAsyncThunk(
|
||||
'auth/change-group',
|
||||
async ({ group }, { getState, dispatch, rejectWithValue }) => {
|
||||
try {
|
||||
if (group === FILTER_POOL.ALL_RESOURCES) {
|
||||
dispatch(changeFilter(FILTER_POOL.ALL_RESOURCES))
|
||||
} else {
|
||||
const { user } = getState().auth
|
||||
|
||||
const data = { id: user?.ID, group }
|
||||
dispatch(apiUser.endpoints.changeGroup.initiate(data)).reset()
|
||||
|
||||
dispatch(changeFilter(FILTER_POOL.PRIMARY_GROUP_RESOURCES))
|
||||
|
||||
return {
|
||||
user: {
|
||||
...user,
|
||||
GID: group,
|
||||
},
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const { message, data, status, statusText } = error
|
||||
|
||||
status === httpCodes.unauthorized.id && dispatch(logout(T.SessionExpired))
|
||||
|
||||
return rejectWithValue({ error: message ?? data?.message ?? statusText })
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const changeView = createAction('auth/change-view', (view) => ({
|
||||
payload: { view },
|
||||
}))
|
@ -14,29 +14,38 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useDispatch, useSelector, shallowEqual } from 'react-redux'
|
||||
import { unwrapResult } from '@reduxjs/toolkit'
|
||||
|
||||
import * as actions from 'client/features/Auth/actions'
|
||||
import { name as authSlice } from 'client/features/Auth/slice'
|
||||
import apiGroup from 'client/features/OneApi/group'
|
||||
import apiSystem from 'client/features/OneApi/system'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
import { name as generalSlice } from 'client/features/General/slice'
|
||||
import { name as authSlice, actions } from 'client/features/Auth/slice'
|
||||
import groupApi from 'client/features/OneApi/group'
|
||||
import systemApi from 'client/features/OneApi/system'
|
||||
import { _APPS, RESOURCE_NAMES, ONEADMIN_ID } from 'client/constants'
|
||||
import { ResourceView } from 'client/apps/sunstone/routes'
|
||||
|
||||
const APPS_WITH_VIEWS = [_APPS.sunstone.name].map((app) => app.toLowerCase())
|
||||
|
||||
const appNeedViews = () => {
|
||||
const { appTitle } = useSelector((state) => state[generalSlice], shallowEqual)
|
||||
|
||||
return useMemo(() => APPS_WITH_VIEWS.includes(appTitle), [appTitle])
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Authenticate Hooks
|
||||
// --------------------------------------------------------------
|
||||
|
||||
export const useAuth = () => {
|
||||
const auth = useSelector((state) => state[authSlice], shallowEqual)
|
||||
const { user, jwt, view, isLoginInProgress } = auth
|
||||
const { jwt, user, view, settings, isLoginInProgress } = auth
|
||||
|
||||
const { data: views } = apiSystem.endpoints.getSunstoneViews.useQuery(
|
||||
undefined,
|
||||
{ skip: !jwt }
|
||||
)
|
||||
const waitViewToLogin = appNeedViews() ? !!view : true
|
||||
|
||||
const { data: userGroups } = apiGroup.endpoints.getGroups.useQuery(
|
||||
const { data: authGroups } = groupApi.endpoints.getGroups.useQueryState(
|
||||
undefined,
|
||||
{
|
||||
skip: !jwt,
|
||||
skip: !jwt || !user?.GROUPS?.ID,
|
||||
selectFromResult: ({ data: groups = [] }) => ({
|
||||
data: [user?.GROUPS?.ID]
|
||||
.flat()
|
||||
@ -46,52 +55,62 @@ export const useAuth = () => {
|
||||
}
|
||||
)
|
||||
|
||||
const isLogged = !!jwt && !!userGroups?.length && !isLoginInProgress
|
||||
|
||||
/**
|
||||
* Looking for resource view of user authenticated.
|
||||
*
|
||||
* @param {RESOURCE_NAMES} resourceName - Name of resource
|
||||
* @returns {{
|
||||
* resource_name: string,
|
||||
* actions: object[],
|
||||
* filters: object[],
|
||||
* info-tabs: object,
|
||||
* dialogs: object[]
|
||||
* }} Returns view of resource
|
||||
*/
|
||||
const getResourceView = useCallback(
|
||||
(resourceName) =>
|
||||
views?.[view]?.find(
|
||||
({ resource_name: name }) =>
|
||||
String(name).toLowerCase() === String(resourceName).toLowerCase()
|
||||
),
|
||||
[view]
|
||||
return useMemo(
|
||||
() => ({
|
||||
...auth,
|
||||
user,
|
||||
isOneAdmin: user?.ID === ONEADMIN_ID,
|
||||
groups: authGroups,
|
||||
// Merge user settings with the existing one
|
||||
settings: { ...settings, ...(user?.TEMPLATE?.FIREEDGE ?? {}) },
|
||||
isLogged:
|
||||
!!jwt &&
|
||||
!!user &&
|
||||
!!authGroups?.length &&
|
||||
!isLoginInProgress &&
|
||||
waitViewToLogin,
|
||||
}),
|
||||
[user, jwt, isLoginInProgress, authGroups, auth, waitViewToLogin]
|
||||
)
|
||||
|
||||
return {
|
||||
...auth,
|
||||
groups: userGroups,
|
||||
isLogged,
|
||||
getResourceView,
|
||||
views,
|
||||
}
|
||||
}
|
||||
|
||||
export const useAuthApi = () => {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const unwrapDispatch = useCallback(
|
||||
(action) => dispatch(action).then(unwrapResult),
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
return {
|
||||
login: (user) => unwrapDispatch(actions.login(user)),
|
||||
getAuthUser: () => dispatch(actions.getUser()),
|
||||
changeGroup: (group) => unwrapDispatch(actions.changeGroup(group)),
|
||||
stopFirstRender: () => dispatch(actions.stopFirstRender()),
|
||||
logout: () => dispatch(actions.logout()),
|
||||
|
||||
changeView: (view) => dispatch(actions.changeView(view)),
|
||||
changeJwt: (jwt) => dispatch(actions.changeJwt(jwt)),
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// View Hooks
|
||||
// --------------------------------------------------------------
|
||||
|
||||
export const useViews = () => {
|
||||
const { jwt, view } = useSelector((state) => state[authSlice], shallowEqual)
|
||||
|
||||
const { data: views } = systemApi.endpoints.getSunstoneViews.useQueryState(
|
||||
undefined,
|
||||
{ skip: !jwt }
|
||||
)
|
||||
|
||||
/**
|
||||
* Looking for resource view of user authenticated.
|
||||
*
|
||||
* @param {RESOURCE_NAMES} resourceName - Name of resource
|
||||
* @returns {ResourceView} Returns view of resource
|
||||
*/
|
||||
const getResourceView = useCallback(
|
||||
(resourceName) =>
|
||||
views?.[view]?.find(
|
||||
({ resource_name: name }) =>
|
||||
`${name}`.toLowerCase() === `${resourceName}`.toLowerCase()
|
||||
),
|
||||
[view]
|
||||
)
|
||||
|
||||
return useMemo(() => ({ getResourceView, views, view }), [views, view])
|
||||
}
|
||||
|
@ -13,82 +13,65 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
import { createAction, createSlice } from '@reduxjs/toolkit'
|
||||
|
||||
import {
|
||||
login,
|
||||
getUser,
|
||||
logout,
|
||||
changeFilter,
|
||||
changeGroup,
|
||||
changeView,
|
||||
} from 'client/features/Auth/actions'
|
||||
import { removeStoreData } from 'client/utils'
|
||||
import {
|
||||
JWT_NAME,
|
||||
FILTER_POOL,
|
||||
DEFAULT_SCHEME,
|
||||
DEFAULT_LANGUAGE,
|
||||
} from 'client/constants'
|
||||
import { isBackend } from 'client/utils'
|
||||
|
||||
export const logout = createAction('logout')
|
||||
|
||||
const initial = () => ({
|
||||
jwt: !isBackend()
|
||||
? window.localStorage.getItem(JWT_NAME) ??
|
||||
window.sessionStorage.getItem(JWT_NAME) ??
|
||||
null
|
||||
: null,
|
||||
jwt: null,
|
||||
user: null,
|
||||
error: null,
|
||||
filterPool: FILTER_POOL.ALL_RESOURCES,
|
||||
settings: {
|
||||
scheme: DEFAULT_SCHEME,
|
||||
lang: DEFAULT_LANGUAGE,
|
||||
disableanimations: 'NO',
|
||||
SCHEME: DEFAULT_SCHEME,
|
||||
LANG: DEFAULT_LANGUAGE,
|
||||
DISABLE_ANIMATIONS: 'NO',
|
||||
},
|
||||
isLoginInProgress: false,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
const { name, actions, reducer } = createSlice({
|
||||
const slice = createSlice({
|
||||
name: 'auth',
|
||||
initialState: { ...initial(), firstRender: true },
|
||||
reducers: {
|
||||
changeAuthUser: (state, { payload }) => ({
|
||||
...state,
|
||||
...payload,
|
||||
}),
|
||||
changeJwt: (state, { payload }) => {
|
||||
state.jwt = payload
|
||||
},
|
||||
changeSettings: (state, { payload }) => {
|
||||
state.settings = { ...state.settings, payload }
|
||||
},
|
||||
changeFilterPool: (state, { payload: filterPool }) => {
|
||||
state.filterPool = filterPool
|
||||
state.isLoginInProgress = false
|
||||
},
|
||||
changeView: (state, { payload }) => {
|
||||
state.view = payload
|
||||
},
|
||||
stopFirstRender: (state) => {
|
||||
state.firstRender = false
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addMatcher(
|
||||
({ type }) => type === logout.type,
|
||||
(_, { error }) => ({ ...initial(), error })
|
||||
)
|
||||
.addMatcher(
|
||||
({ type }) =>
|
||||
[
|
||||
changeFilter.type,
|
||||
login.fulfilled.type,
|
||||
getUser.fulfilled.type,
|
||||
changeGroup.fulfilled.type,
|
||||
changeView.type,
|
||||
].includes(type),
|
||||
(state, { payload }) => ({ ...state, ...payload })
|
||||
)
|
||||
.addMatcher(
|
||||
({ type }) => type.startsWith('auth/') && type.endsWith('/pending'),
|
||||
(state) => ({ ...state, isLoading: true, error: null })
|
||||
)
|
||||
.addMatcher(
|
||||
({ type }) => type.startsWith('auth/') && type.endsWith('/fulfilled'),
|
||||
(state) => ({ ...state, isLoading: false, firstRender: false })
|
||||
)
|
||||
.addMatcher(
|
||||
({ type }) => type.startsWith('auth/') && type.endsWith('/rejected'),
|
||||
(state, { payload }) => ({
|
||||
...state,
|
||||
...payload,
|
||||
isLoginInProgress: false,
|
||||
isLoading: false,
|
||||
firstRender: false,
|
||||
jwt: null,
|
||||
})
|
||||
)
|
||||
builder.addCase(logout, (_, { payload }) => {
|
||||
removeStoreData([JWT_NAME])
|
||||
|
||||
return { ...initial(), error: payload }
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
export { name, actions, reducer }
|
||||
export const { name, reducer } = slice
|
||||
|
||||
const actions = { ...slice.actions, logout }
|
||||
export { actions }
|
||||
|
131
src/fireedge/src/client/features/AuthApi/index.js
Normal file
131
src/fireedge/src/client/features/AuthApi/index.js
Normal file
@ -0,0 +1,131 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* 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 { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||||
|
||||
import { dismissSnackbar } from 'client/features/General/actions'
|
||||
import { actions } from 'client/features/Auth/slice'
|
||||
import userApi from 'client/features/OneApi/user'
|
||||
|
||||
import { storage } from 'client/utils'
|
||||
import { APP_URL, JWT_NAME, FILTER_POOL, ONEADMIN_ID } from 'client/constants'
|
||||
|
||||
const { ALL_RESOURCES, PRIMARY_GROUP_RESOURCES } = FILTER_POOL
|
||||
|
||||
const authApi = createApi({
|
||||
reducerPath: 'authApi',
|
||||
baseQuery: fetchBaseQuery({
|
||||
baseUrl: `${APP_URL}/api/`,
|
||||
prepareHeaders: (headers, { getState }) => {
|
||||
const token = getState().auth.jwt
|
||||
|
||||
// If we have a token set in state,
|
||||
// let's assume that we should be passing it.
|
||||
token && headers.set('authorization', `Bearer ${token}`)
|
||||
|
||||
return headers
|
||||
},
|
||||
}),
|
||||
endpoints: (builder) => ({
|
||||
getAuthUser: builder.query({
|
||||
/**
|
||||
* @returns {object} Information about authenticated user
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: () => ({ url: 'user/info' }),
|
||||
transformResponse: (response) => response?.data?.USER,
|
||||
async onQueryStarted(_, { queryFulfilled, dispatch }) {
|
||||
try {
|
||||
const { data: user } = await queryFulfilled
|
||||
dispatch(actions.changeAuthUser({ user }))
|
||||
} catch {}
|
||||
},
|
||||
}),
|
||||
login: builder.mutation({
|
||||
/**
|
||||
* @param {object} data - User credentials
|
||||
* @param {string} data.user - Username
|
||||
* @param {string} data.token - Password
|
||||
* @param {boolean} [data.remember] - Remember session
|
||||
* @param {string} [data.token2fa] - Token for Two factor authentication
|
||||
* @returns {object} Response data from request
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (data) => ({ url: 'auth', method: 'POST', body: data }),
|
||||
transformResponse: (response) => {
|
||||
const { id, token } = response?.data
|
||||
const isOneAdmin = id === ONEADMIN_ID
|
||||
|
||||
return {
|
||||
jwt: token,
|
||||
user: { ID: id },
|
||||
isLoginInProgress: !!token && !isOneAdmin,
|
||||
}
|
||||
},
|
||||
async onQueryStarted({ remember }, { queryFulfilled, dispatch }) {
|
||||
try {
|
||||
const { data: queryData } = await queryFulfilled
|
||||
|
||||
if (queryData?.jwt) {
|
||||
storage(JWT_NAME, queryData?.jwt, remember)
|
||||
dispatch(dismissSnackbar({ dismissAll: true }))
|
||||
}
|
||||
|
||||
dispatch(actions.changeAuthUser(queryData))
|
||||
} catch {}
|
||||
},
|
||||
}),
|
||||
changeAuthGroup: builder.mutation({
|
||||
/**
|
||||
* @param {object} data - User credentials
|
||||
* @param {string} data.group - Group id
|
||||
* @returns {Promise} Response data from request
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
queryFn: async ({ group } = {}, { getState, dispatch }) => {
|
||||
try {
|
||||
if (group === ALL_RESOURCES) {
|
||||
dispatch(actions.changeFilterPool(ALL_RESOURCES))
|
||||
|
||||
return { data: '' }
|
||||
}
|
||||
|
||||
const authUser = getState().auth.user
|
||||
const queryData = { id: authUser.ID, group: group }
|
||||
|
||||
const response = await dispatch(
|
||||
userApi.endpoints.changeGroup.initiate(queryData)
|
||||
).unwrap()
|
||||
|
||||
dispatch(actions.changeFilterPool(PRIMARY_GROUP_RESOURCES))
|
||||
|
||||
return { data: response }
|
||||
} catch (error) {
|
||||
return { error }
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
export const {
|
||||
useGetAuthUserQuery,
|
||||
useLazyGetAuthUserQuery,
|
||||
|
||||
useLoginMutation,
|
||||
useChangeAuthGroupMutation,
|
||||
} = authApi
|
||||
|
||||
export { authApi }
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
|
||||
import { logout } from 'client/features/Auth/actions'
|
||||
import { actions as authActions } from 'client/features/Auth/slice'
|
||||
import * as actions from 'client/features/General/actions'
|
||||
import { generateKey } from 'client/utils'
|
||||
import { APPS_IN_BETA } from 'client/constants'
|
||||
@ -36,6 +36,13 @@ const { name, reducer } = createSlice({
|
||||
initialState: initial,
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
/* LOGOUT ACTION */
|
||||
.addCase(authActions.logout, (state) => ({
|
||||
...initial,
|
||||
appTitle: state.appTitle,
|
||||
isBeta: state.isBeta,
|
||||
}))
|
||||
|
||||
/* UI ACTIONS */
|
||||
.addCase(actions.fixMenu, (state, { payload }) => ({
|
||||
...state,
|
||||
@ -92,10 +99,6 @@ const { name, reducer } = createSlice({
|
||||
})
|
||||
|
||||
/* REQUESTS API MATCHES */
|
||||
.addMatcher(
|
||||
({ type }) => type === logout.type,
|
||||
() => initial
|
||||
)
|
||||
.addMatcher(
|
||||
({ type }) => type.endsWith('/pending') && !type.includes('auth'),
|
||||
(state) => ({ ...state, isLoading: true })
|
||||
|
@ -16,10 +16,8 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react'
|
||||
|
||||
import { enqueueSnackbar } from 'client/features/General/actions'
|
||||
import { logout } from 'client/features/Auth/actions'
|
||||
import { httpCodes } from 'server/utils/constants'
|
||||
import { requestConfig, generateKey } from 'client/utils'
|
||||
import { T } from 'client/constants'
|
||||
import http from 'client/utils/rest'
|
||||
|
||||
const ONE_RESOURCES = {
|
||||
@ -92,15 +90,14 @@ const oneApi = createApi({
|
||||
|
||||
const error = message ?? errorFromOned ?? messageFromServer ?? statusText
|
||||
|
||||
status === httpCodes.unauthorized.id
|
||||
? dispatch(logout(T.SessionExpired))
|
||||
: dispatch(
|
||||
enqueueSnackbar({
|
||||
key: generateKey(),
|
||||
message: error,
|
||||
options: { variant: 'error' },
|
||||
})
|
||||
)
|
||||
status !== httpCodes.unauthorized.id &&
|
||||
dispatch(
|
||||
enqueueSnackbar({
|
||||
key: generateKey(),
|
||||
message: error,
|
||||
options: { variant: 'error' },
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
error: {
|
||||
|
@ -297,7 +297,7 @@ const marketAppApi = oneApi.injectEndpoints({
|
||||
* @returns {number} Marketplace app id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
queryFn: async (params) => {
|
||||
query: (params) => {
|
||||
const name = ExtraActions.MARKETAPP_IMPORT
|
||||
const command = { name, ...ExtraCommands[name] }
|
||||
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
Actions as SunstoneActions,
|
||||
Commands as SunstoneCommands,
|
||||
} from 'server/routes/api/sunstone/routes'
|
||||
import { changeView } from 'client/features/Auth/actions'
|
||||
import { actions } from 'client/features/Auth/slice'
|
||||
import { oneApi, ONE_RESOURCES } from 'client/features/OneApi'
|
||||
|
||||
const { SYSTEM } = ONE_RESOURCES
|
||||
@ -86,10 +86,12 @@ const systemApi = oneApi.injectEndpoints({
|
||||
|
||||
return { command }
|
||||
},
|
||||
async onQueryStarted(_, { dispatch, queryFulfilled }) {
|
||||
async onQueryStarted(_, { dispatch, getState, queryFulfilled }) {
|
||||
try {
|
||||
const { data: views = {} } = await queryFulfilled
|
||||
dispatch(changeView(Object.keys(views)[0]))
|
||||
|
||||
const currentView = getState().auth?.view
|
||||
!currentView && dispatch(actions.changeView(Object.keys(views)[0]))
|
||||
} catch {}
|
||||
},
|
||||
providesTags: [{ type: SYSTEM, id: 'sunstone-views' }],
|
||||
|
@ -192,6 +192,12 @@ const userApi = oneApi.injectEndpoints({
|
||||
user && (user.GID = group)
|
||||
})
|
||||
)
|
||||
|
||||
dispatch(
|
||||
userApi.util.updateQueryData('getUser', id, (draftUser) => {
|
||||
draftUser.GID = group
|
||||
})
|
||||
)
|
||||
} catch {}
|
||||
},
|
||||
}),
|
||||
|
@ -13,42 +13,21 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { httpCodes } from 'server/utils/constants'
|
||||
import { RestClient } from 'client/utils'
|
||||
import { isRejectedWithValue, Middleware, Dispatch } from '@reduxjs/toolkit'
|
||||
import { actions } from 'client/features/Auth/slice'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
export const authService = {
|
||||
/**
|
||||
* @param {object} data - User credentials
|
||||
* @param {string} data.user - Username
|
||||
* @param {string} data.token - Password
|
||||
* @param {boolean} [data.remember] - Remember session
|
||||
* @param {string} [data.token2fa] - Token for Two factor authentication
|
||||
* @returns {object} Response data from request
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
login: async (data) => {
|
||||
const res = await RestClient.request({
|
||||
url: '/api/auth',
|
||||
data,
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) {
|
||||
if (res?.id === httpCodes.accepted.id) return res
|
||||
throw res
|
||||
/**
|
||||
* @param {{ dispatch: Dispatch }} params - Redux parameters
|
||||
* @returns {Middleware} - Unauthenticated middleware
|
||||
*/
|
||||
export const unauthenticatedMiddleware =
|
||||
({ dispatch }) =>
|
||||
(next) =>
|
||||
(action) => {
|
||||
if (isRejectedWithValue(action) && action.payload.status === 401) {
|
||||
dispatch(actions.logout(T.SessionExpired))
|
||||
}
|
||||
|
||||
return res?.data
|
||||
},
|
||||
/**
|
||||
* @returns {object} Information about user authenticated
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
getUser: async () => {
|
||||
const res = await RestClient.request({ url: '/api/user/info' })
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
|
||||
return res?.data?.USER ?? {}
|
||||
},
|
||||
}
|
||||
return next(action)
|
||||
}
|
@ -34,12 +34,12 @@ import { SCHEMES } from 'client/constants'
|
||||
const { DARK, LIGHT, SYSTEM } = SCHEMES
|
||||
|
||||
const MuiProvider = ({ theme: appTheme, children }) => {
|
||||
const { settings: { scheme } = {} } = useAuth()
|
||||
const { settings: { SCHEME } = {} } = useAuth()
|
||||
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)')
|
||||
|
||||
const changeScheme = () => {
|
||||
const prefersScheme = prefersDarkMode ? DARK : LIGHT
|
||||
const newScheme = scheme === SYSTEM ? prefersScheme : scheme
|
||||
const newScheme = SCHEME === SYSTEM ? prefersScheme : SCHEME
|
||||
|
||||
return createTheme(appTheme, newScheme)
|
||||
}
|
||||
@ -55,7 +55,7 @@ const MuiProvider = ({ theme: appTheme, children }) => {
|
||||
|
||||
useEffect(() => {
|
||||
setTheme(changeScheme)
|
||||
}, [scheme, prefersDarkMode])
|
||||
}, [SCHEME, prefersDarkMode])
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterLuxon}>
|
||||
|
@ -20,7 +20,9 @@ import { isDevelopment } from 'client/utils'
|
||||
|
||||
import * as Auth from 'client/features/Auth/slice'
|
||||
import * as General from 'client/features/General/slice'
|
||||
import { authApi } from 'client/features/AuthApi'
|
||||
import { oneApi } from 'client/features/OneApi'
|
||||
import { unauthenticatedMiddleware } from 'client/features/middleware'
|
||||
|
||||
/**
|
||||
* @param {object} props - Props
|
||||
@ -32,13 +34,18 @@ export const createStore = ({ initState = {} }) => {
|
||||
reducer: {
|
||||
[Auth.name]: Auth.reducer,
|
||||
[General.name]: General.reducer,
|
||||
[authApi.reducerPath]: authApi.reducer,
|
||||
[oneApi.reducerPath]: oneApi.reducer,
|
||||
},
|
||||
devTools: isDevelopment(),
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware({
|
||||
immutableCheck: true,
|
||||
}).concat(oneApi.middleware),
|
||||
}).concat([
|
||||
unauthenticatedMiddleware,
|
||||
authApi.middleware,
|
||||
oneApi.middleware,
|
||||
]),
|
||||
preloadedState: initState,
|
||||
})
|
||||
|
||||
|
@ -13,14 +13,16 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
const { combineReducers } = require('redux')
|
||||
const { combineReducers } = require('@reduxjs/toolkit')
|
||||
const Auth = require('client/features/Auth/slice')
|
||||
const General = require('client/features/General/slice')
|
||||
const { authApi } = require('client/features/AuthApi')
|
||||
const { oneApi } = require('client/features/OneApi')
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
general: General.reducer,
|
||||
auth: Auth.reducer,
|
||||
[authApi.reducerPath]: authApi.reducer,
|
||||
[oneApi.reducerPath]: oneApi.reducer,
|
||||
})
|
||||
|
||||
|
@ -71,7 +71,7 @@ const buttonSvgStyle = {
|
||||
*/
|
||||
export default (appTheme, mode = SCHEMES.DARK) => {
|
||||
const { primary, secondary } = appTheme.palette
|
||||
const isDarkMode = mode === SCHEMES.DARK
|
||||
const isDarkMode = `${mode}`.toLowerCase() === SCHEMES.DARK
|
||||
|
||||
return {
|
||||
palette: {
|
||||
@ -237,6 +237,16 @@ export default (appTheme, mode = SCHEMES.DARK) => {
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: {
|
||||
'@font-face': UbuntuFont,
|
||||
'.loading_screen': {
|
||||
width: '100%',
|
||||
height: '100vh',
|
||||
backgroundColor: 'background.default',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'fixed',
|
||||
zIndex: 10000,
|
||||
},
|
||||
'.description__link': {
|
||||
margin: 0,
|
||||
color: isDarkMode ? secondary.main : secondary.dark,
|
||||
|
Loading…
x
Reference in New Issue
Block a user