1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-21 14:50:08 +03:00

F #5422: Fix switcher group on header (#2089)

This commit is contained in:
Sergio Betanzos 2022-05-26 13:46:16 +02:00 committed by GitHub
parent b59dc6b355
commit b81b3e3d24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 71 additions and 70 deletions

View File

@ -18,8 +18,8 @@ import { useDispatch } from 'react-redux'
import PropTypes from 'prop-types'
import { useAuth, useAuthApi } from 'client/features/Auth'
import { authApi } from 'client/features/AuthApi'
import { oneApi } from 'client/features/OneApi'
import authApi from 'client/features/OneApi/auth'
import groupApi from 'client/features/OneApi/group'
import FullscreenProgress from 'client/components/LoadingScreen'
import { findStorageData, findExternalToken, storage } from 'client/utils'
@ -47,7 +47,6 @@ const AuthLayout = ({ subscriptions = [], children }) => {
return () => {
authSubscription.unsubscribe()
dispatch(authApi.util.resetApiState())
dispatch(oneApi.util.resetApiState())
}
}, [dispatch, jwt])

View File

@ -16,11 +16,11 @@
import { useMemo, memo, JSXElementConstructor } from 'react'
import PropTypes from 'prop-types'
import { Button, Stack, CircularProgress } from '@mui/material'
import { Group as GroupIcon, VerifiedBadge as SelectIcon } from 'iconoir-react'
import { Stack, Button, Typography, CircularProgress } from '@mui/material'
import { Group as GroupIcon, VerifiedUser, Check } from 'iconoir-react'
import { useAuth } from 'client/features/Auth'
import { useChangeAuthGroupMutation } from 'client/features/AuthApi'
import { useChangeAuthGroupMutation } from 'client/features/OneApi/auth'
import Search from 'client/components/Search'
import HeaderPopover from 'client/components/Header/Popover'
import { Tr, Translate } from 'client/components/HOC'
@ -33,25 +33,32 @@ const ButtonGroup = memo(
const { user, filterPool } = useAuth()
const { ID, NAME } = group
const isPrimaryGroup = user?.GID === ID
const isSelected =
(filterPool === ALL_RESOURCES && ALL_RESOURCES === ID) ||
(filterPool === PRIMARY_GROUP_RESOURCES && user?.GID === ID)
(filterPool === PRIMARY_GROUP_RESOURCES && isPrimaryGroup)
return (
<Button
fullWidth
disabled={disabled}
color="debug"
variant="outlined"
variant={isSelected ? 'contained' : 'outlined'}
color="secondary"
onClick={handleClick}
sx={{
color: (theme) => theme.palette.text.primary,
boxShadow: 'none !important',
justifyContent: 'start',
'& svg:first-of-type': { my: 0, mx: 2 },
}}
>
{NAME}
{isSelected && <SelectIcon />}
<Stack component="span" direction="row" alignItems="center" gap=".5em">
{isSelected && <Check style={{ margin: 0 }} />}
<Typography component="span" noWrap variant="subtitle2">
{NAME}
</Typography>
{isPrimaryGroup && <VerifiedUser />}
</Stack>
</Button>
)
},
@ -88,7 +95,7 @@ const Group = () => {
headerTitle={
<Stack direction="row" alignItems="center" gap="1em" component="span">
<Translate word={T.SwitchGroup} />
{isLoading && <CircularProgress size={20} />}
{isLoading && <CircularProgress size={20} color="secondary" />}
</Stack>
}
>

View File

@ -18,7 +18,7 @@ import { useCallback, useMemo } from 'react'
import { useDispatch, useSelector, shallowEqual } from 'react-redux'
import { name as generalSlice } from 'client/features/General/slice'
import { name as authSlice, actions } from 'client/features/Auth/slice'
import { name as authSlice, actions, logout } from 'client/features/Auth/slice'
import groupApi from 'client/features/OneApi/group'
import systemApi from 'client/features/OneApi/system'
import { ResourceView } from 'client/apps/sunstone/routes'
@ -91,10 +91,10 @@ export const useAuthApi = () => {
return {
stopFirstRender: () => dispatch(actions.stopFirstRender()),
logout: () => dispatch(actions.logout()),
logout: () => dispatch(logout()),
changeView: (view) => dispatch(actions.changeView(view)),
changeJwt: (jwt) => dispatch(actions.changeJwt(jwt)),
changeAuthUser: (user) => dispatch(actions.changeAuthUser({ user })),
changeAuthUser: (user) => dispatch(actions.changeAuthUser(user)),
}
}

View File

@ -31,10 +31,13 @@ const slice = createSlice({
name: 'auth',
initialState: { ...initial(), firstRender: true },
reducers: {
changeAuthUser: (state, { payload }) => ({
...state,
...payload,
}),
changeAuthUser: (state, { payload: { isLoginInProgress, ...user } }) => {
state.user = { ...state.user, ...user }
if (isLoginInProgress !== undefined) {
state.isLoginInProgress = isLoginInProgress
}
},
changeJwt: (state, { payload }) => {
state.jwt = payload
},
@ -58,7 +61,4 @@ const slice = createSlice({
},
})
export const { name, reducer } = slice
const actions = { ...slice.actions, logout }
export { actions }
export const { name, reducer, actions } = slice

View File

@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { createSlice } from '@reduxjs/toolkit'
import { actions as authActions } from 'client/features/Auth/slice'
import { logout } from 'client/features/Auth/slice'
import * as actions from 'client/features/General/actions'
import { generateKey } from 'client/utils'
import { APPS_IN_BETA, APPS_WITH_SWITCHER } from 'client/constants'
@ -30,13 +30,13 @@ const initial = {
notifications: [],
}
const { name, reducer } = createSlice({
const slice = createSlice({
name: 'general',
initialState: initial,
extraReducers: (builder) => {
builder
/* LOGOUT ACTION */
.addCase(authActions.logout, (state) => ({
.addCase(logout, (state) => ({
...initial,
// persistent app state
appTitle: state.appTitle,
@ -127,4 +127,4 @@ const { name, reducer } = createSlice({
},
})
export { name, reducer }
export const { name, reducer } = slice

View File

@ -16,7 +16,7 @@
import { createSlice } from '@reduxjs/toolkit'
import { Status } from 'guacamole-common-js'
import { actions as authActions } from 'client/features/Auth/slice'
import { logout } from 'client/features/Auth/slice'
import { GUACAMOLE_STATES_STR } from 'client/constants'
const {
@ -119,7 +119,8 @@ const slice = createSlice({
},
},
extraReducers: (builder) => {
builder.addCase(authActions.logout, () => ({}))
/* LOGOUT ACTION */
builder.addCase(logout, () => ({}))
},
})

View File

@ -13,37 +13,20 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { createApi } from '@reduxjs/toolkit/query/react'
import { Actions, Commands } from 'server/routes/api/auth/routes'
import { actions as authActions } from 'client/features/Auth/slice'
import { dismissSnackbar } from 'client/features/General/actions'
import { actions } from 'client/features/Auth/slice'
import { oneApi, ONE_RESOURCES_POOL } from 'client/features/OneApi'
import userApi from 'client/features/OneApi/user'
import http from 'client/utils/rest'
import { requestConfig, storage } from 'client/utils'
import { storage } from 'client/utils'
import { JWT_NAME, FILTER_POOL, ONEADMIN_ID } from 'client/constants'
const { GROUP_POOL, ...restOfPool } = ONE_RESOURCES_POOL
const { ALL_RESOURCES, PRIMARY_GROUP_RESOURCES } = FILTER_POOL
const authApi = createApi({
reducerPath: 'authApi',
baseQuery: async ({ params, command, needState }, { getState, signal }) => {
try {
const config = requestConfig(params, command)
const response = await http.request({ ...config, signal })
const state = needState ? getState() : {}
return { data: response.data ?? {}, meta: { state } }
} catch (axiosError) {
const { message, data = {}, status, statusText } = axiosError
const { message: messageFromServer, data: errorFromOned } = data
const error = message ?? errorFromOned ?? messageFromServer ?? statusText
return { error: { status: status, data: error } }
}
},
const authApi = oneApi.injectEndpoints({
endpoints: (builder) => ({
getAuthUser: builder.query({
/**
@ -55,7 +38,7 @@ const authApi = createApi({
async onQueryStarted(_, { queryFulfilled, dispatch }) {
try {
const { data: user } = await queryFulfilled
dispatch(actions.changeAuthUser({ user }))
dispatch(authActions.changeAuthUser(user))
} catch {}
},
}),
@ -91,13 +74,15 @@ const authApi = createApi({
async onQueryStarted(_, { queryFulfilled, dispatch }) {
try {
const { data: queryData } = await queryFulfilled
const { jwt, ...user } = queryData
if (queryData?.jwt) {
storage(JWT_NAME, queryData?.jwt)
if (jwt) {
storage(JWT_NAME, jwt)
dispatch(dismissSnackbar({ dismissAll: true }))
}
dispatch(actions.changeAuthUser(queryData))
dispatch(authActions.changeJwt(queryData))
dispatch(authActions.changeAuthUser(user))
} catch {}
},
}),
@ -111,35 +96,39 @@ const authApi = createApi({
queryFn: async ({ group } = {}, { getState, dispatch }) => {
try {
if (group === ALL_RESOURCES) {
dispatch(actions.changeFilterPool(ALL_RESOURCES))
dispatch(authActions.changeFilterPool(ALL_RESOURCES))
return { data: '' }
}
const authUser = getState().auth.user
const queryData = { id: authUser.ID, group: group }
const queryData = { id: authUser.ID, group }
const response = await dispatch(
const newGroup = await dispatch(
userApi.endpoints.changeGroup.initiate(queryData)
).unwrap()
dispatch(actions.changeFilterPool(PRIMARY_GROUP_RESOURCES))
dispatch(authActions.changeFilterPool(PRIMARY_GROUP_RESOURCES))
dispatch(authActions.changeAuthUser({ GID: `${group}` }))
return { data: response }
return { data: newGroup }
} catch (error) {
return { error }
}
},
invalidatesTags: [...Object.values(restOfPool)],
}),
}),
})
export const {
// Queries
useGetAuthUserQuery,
useLazyGetAuthUserQuery,
// Mutations
useLoginMutation,
useChangeAuthGroupMutation,
} = authApi
export { authApi }
export default authApi

View File

@ -78,12 +78,21 @@ const PROVISION_RESOURCES = {
const oneApi = createApi({
reducerPath: 'oneApi',
baseQuery: async ({ params, command }, { dispatch, signal }) => {
baseQuery: async (
{ params = {}, command, needStateInMeta = false },
{ getState, dispatch, signal }
) => {
try {
// set filter flag if filter is present in command params
if (command?.params?.filter) {
params.filter = getState().auth?.filterPool
}
const config = requestConfig(params, command)
const response = await http.request({ ...config, signal })
const state = needStateInMeta ? getState() : {}
return { data: response.data ?? {} }
return { data: response.data ?? {}, meta: { state } }
} catch (axiosError) {
const { message, data = {}, status, statusText } = axiosError
const { message: messageFromServer, data: errorFromOned } = data

View File

@ -14,12 +14,13 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { Actions, Commands } from 'server/utils/constants/commands/user'
import {
oneApi,
ONE_RESOURCES,
ONE_RESOURCES_POOL,
} from 'client/features/OneApi'
import { authApi } from 'client/features/AuthApi'
import authApi from 'client/features/OneApi/auth'
import { User } from 'client/constants'
const { USER } = ONE_RESOURCES

View File

@ -21,7 +21,6 @@ import { isDevelopment } from 'client/utils'
import * as Auth from 'client/features/Auth/slice'
import * as General from 'client/features/General/slice'
import * as Guacamole from 'client/features/Guacamole/slice'
import { authApi } from 'client/features/AuthApi'
import { oneApi } from 'client/features/OneApi'
import { unauthenticatedMiddleware } from 'client/features/middleware'
@ -37,7 +36,6 @@ export const createStore = ({ initState = {}, extraMiddleware = [] }) => {
[Auth.name]: Auth.reducer,
[General.name]: General.reducer,
[Guacamole.name]: Guacamole.reducer,
[authApi.reducerPath]: authApi.reducer,
[oneApi.reducerPath]: oneApi.reducer,
},
devTools: isDevelopment(),
@ -47,7 +45,6 @@ export const createStore = ({ initState = {}, extraMiddleware = [] }) => {
}).concat([
...extraMiddleware,
unauthenticatedMiddleware,
authApi.middleware,
oneApi.middleware,
]),
preloadedState: initState,

View File

@ -16,13 +16,11 @@
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,
})