Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions admin-ui/app/components/App/AuthenticatedRouteSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ export default function AuthenticatedRouteSelector() {
}

return (
<AppAuthProvider>
<AppLayout>
<RoutedContent />
<LazyRoutes.GluuToast />
<LazyRoutes.GluuWebhookErrorDialog />
<PermissionsPolicyInitializer />
</AppLayout>
</AppAuthProvider>
<>
<LazyRoutes.GluuToast />
<AppAuthProvider>
<AppLayout>
<RoutedContent />
<LazyRoutes.GluuWebhookErrorDialog />
<PermissionsPolicyInitializer />
</AppLayout>
</AppAuthProvider>
</>
)
}
18 changes: 13 additions & 5 deletions admin-ui/app/redux/features/toastSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,21 @@ const initialState = {
showToast: false,
message: '',
type: 'success',
onCloseRedirectUrl: '',
}

export const updateToast = (showToast = false, type = 'success', message = '') => ({
export const updateToast = (
showToast = false,
type = 'success',
message = '',
onCloseRedirectUrl = '',
) => ({
type: 'toast/updateToast',
payload: {
showToast: showToast,
message: message,
type: type,
showToast,
message,
type,
onCloseRedirectUrl,
},
})

Expand All @@ -21,10 +28,11 @@ const toastSlice = createSlice({
initialState,
reducers: {
updateToast: (state, action) => {
const { showToast, type, message } = action.payload
const { showToast, type, message, onCloseRedirectUrl = '' } = action.payload
state.showToast = showToast
state.type = type
state.message = message
state.onCloseRedirectUrl = onCloseRedirectUrl
},
},
})
Expand Down
1 change: 1 addition & 0 deletions admin-ui/app/redux/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export interface ToastState {
showToast: boolean
message: string
type: ToastType
onCloseRedirectUrl: string
}

// Profile Details State
Expand Down
25 changes: 19 additions & 6 deletions admin-ui/app/routes/Apps/Gluu/GluuToast.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'
import { ToastContainer, toast } from 'react-toastify'
import { useSelector, useDispatch } from 'react-redux'
import { useAppSelector, useAppDispatch } from '@/redux/hooks'
import { useTranslation } from 'react-i18next'
import { updateToast } from 'Redux/features/toastSlice'

function GluuToast() {
const dispatch = useDispatch()
const dispatch = useAppDispatch()
const { t } = useTranslation()
const { showToast, message, type } = useSelector((state: any) => state.toastReducer)
const { showToast, message, type, onCloseRedirectUrl } = useAppSelector(
(state) => state.toastReducer,
)
const redirectUrlRef = useRef('')

const ToastDesign = () => {
return (
Expand All @@ -21,16 +25,25 @@ function GluuToast() {
</div>
)
}

const handleToastClose = () => {
if (redirectUrlRef.current) {
window.location.href = redirectUrlRef.current
}
}

const showTheToast = () => {
const options = onCloseRedirectUrl ? { onClose: handleToastClose } : {}
if (type == 'success') {
toast.success(<ToastDesign />)
toast.success(<ToastDesign />, options)
} else {
toast.error(<ToastDesign />)
toast.error(<ToastDesign />, options)
}
}

useEffect(() => {
if (showToast) {
redirectUrlRef.current = onCloseRedirectUrl || ''
showTheToast()
dispatch(updateToast(false, 'success', ''))
}
Expand Down
29 changes: 17 additions & 12 deletions admin-ui/app/utils/AppAuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import { checkLicenseConfigValid, getUserInfoResponse } from '../redux/actions'
import { getAPIAccessToken, checkLicensePresent } from 'Redux/actions'
import GluuTimeoutModal from 'Routes/Apps/Gluu/GluuTimeoutModal'
import GluuErrorModal from 'Routes/Apps/Gluu/GluuErrorModal'
import GluuToast from 'Routes/Apps/Gluu/GluuToast'
import { toast } from 'react-toastify'
import { updateToast } from 'Redux/features/toastSlice'
import {
FetchRequestor,
Expand Down Expand Up @@ -244,18 +242,26 @@ export default function AppAuthProvider({ children }: Readonly<AppAuthProviderPr
setShowAdminUI(false)
setRoleNotFound(true)
const state = uuidv4()
const sessionEndpoint = `${authConfigs?.endSessionEndpoint ?? ''}?state=${state}&post_logout_redirect_uri=${localStorage.getItem('postLogoutRedirectUri') ?? ''}`
const redirect = () => {
window.location.href = sessionEndpoint
}
toast.error(t('messages.no_valid_role_logout', { seconds: LOGOUT_DELAY_SECONDS }), {
autoClose: LOGOUT_DELAY_SECONDS * 1000,
onClose: redirect,
})
const endSessionUrl = new URL(
authConfigs?.endSessionEndpoint || window.location.origin,
)
endSessionUrl.searchParams.set('state', state)
endSessionUrl.searchParams.set(
'post_logout_redirect_uri',
localStorage.getItem('postLogoutRedirectUri') ?? '',
)
const sessionEndpoint = endSessionUrl.toString()
dispatch(
updateToast(
true,
'error',
t('messages.no_valid_role_logout', { seconds: LOGOUT_DELAY_SECONDS }),
sessionEndpoint,
),
)
return
}

// Rely on useEffect to dispatch getAPIAccessToken when userinfo/userinfo_jwt are set (avoids duplicate api-protection-token + session calls)
setShowAdminUI(true)
})
.catch((oError: Error) => {
Expand Down Expand Up @@ -285,7 +291,6 @@ export default function AppAuthProvider({ children }: Readonly<AppAuthProviderPr

return (
<React.Fragment>
<GluuToast />
<SessionTimeout isAuthenticated={showAdminUI} />
<GluuTimeoutModal
description={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ const PasswordChangeModal = ({
formik={passwordFormik}
lsize={4}
rsize={8}
isDark={false}
showError={
!!passwordFormik.errors.userPassword &&
(passwordFormik.touched.userPassword || !!passwordFormik.values.userPassword)
Expand All @@ -270,6 +271,7 @@ const PasswordChangeModal = ({
formik={passwordFormik}
lsize={4}
rsize={8}
isDark={false}
showError={
!!passwordFormik.errors.userConfirmPassword &&
(passwordFormik.touched.userConfirmPassword ||
Expand Down
5 changes: 3 additions & 2 deletions admin-ui/plugins/user-management/components/UserForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ function UserForm({ onSubmitData, userDetails, isSubmitting = false }: Readonly<
const updateModifiedFields = useCallback(
(name: string, value: unknown) => {
setModifiedFields((prev) => {
if (isEmptyValue(value)) {
if (isEmptyValue(value) && !Array.isArray(value)) {
const { [name]: _removed, ...rest } = prev
void _removed
return rest
Expand Down Expand Up @@ -231,7 +231,8 @@ function UserForm({ onSubmitData, userDetails, isSubmitting = false }: Readonly<
const cleanedFields: Record<string, string | string[] | boolean> = {}

for (const [key, value] of Object.entries(newFields)) {
if (!isEmptyValue(value)) {
// Preserve empty arrays as they represent intentional clearing of multi-valued fields
if (!isEmptyValue(value) || Array.isArray(value)) {
cleanedFields[key] = value
}
}
Expand Down
Loading