Skip to content

Commit 5ff9dee

Browse files
fix(admin-ui): ensure confirmation popup closes after accept in apply… (#2676)
* fix(admin-ui): resolve navigation hang when redirecting to SSA page (#2672) * coderabbit ai suggestions * show error page when Config API is unavailable instead of rendering SSA upload screen * show error page when Config API is unavailable instead of rendering SSA upload screen * show error page when Config API is unavailable instead of rendering SSA upload screen * fix(admin-ui): ensure confirmation popup closes after accept in apply changes flow (#2671) * code rabbit suggestions * code rabbit suggestions * code rabbit suggestions * code rabbit suggestions
1 parent df4eaf4 commit 5ff9dee

File tree

11 files changed

+66
-57
lines changed

11 files changed

+66
-57
lines changed

admin-ui/app/routes/Apps/Gluu/GluuCommitDialog.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const GluuCommitDialog = ({
4141
const isDark = themeState.theme === THEME_DARK
4242
const { classes } = useStyles({ isDark })
4343
const [userMessage, setUserMessage] = useState('')
44+
const [isSubmitting, setIsSubmitting] = useState(false)
4445
const { loadingWebhooks, webhookModal } = useSelector((state: RootState) => state.webhookReducer)
4546

4647
const webhookResourceId = useMemo(() => ADMIN_UI_RESOURCES.Webhooks, [])
@@ -101,13 +102,20 @@ const GluuCommitDialog = ({
101102
setUserMessage('')
102103
}, [handler, onCloseModal])
103104

104-
const handleAccept = useCallback(() => {
105+
const handleAccept = useCallback(async () => {
106+
if (isSubmitting) return
107+
setIsSubmitting(true)
105108
if (formik) {
106109
formik.setFieldValue('action_message', userMessage)
107110
}
108-
onAccept(userMessage)
109-
closeModal()
110-
}, [formik, onAccept, userMessage, closeModal])
111+
try {
112+
const result = onAccept(userMessage)
113+
await Promise.resolve(result)
114+
closeModal()
115+
} finally {
116+
setIsSubmitting(false)
117+
}
118+
}, [formik, onAccept, userMessage, closeModal, isSubmitting])
111119

112120
const handleOverlayKeyDown = useCallback(
113121
(e: React.KeyboardEvent) => {
@@ -195,7 +203,7 @@ const GluuCommitDialog = ({
195203
<div className={classes.buttonRow}>
196204
<GluuButton
197205
onClick={handleAccept}
198-
disabled={!isValid}
206+
disabled={!isValid || isSubmitting}
199207
backgroundColor={customColors.statusActive}
200208
textColor={customColors.white}
201209
borderColor="transparent"

admin-ui/app/routes/Apps/Gluu/GluuCommitDialogLegacy.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const GluuCommitDialogLegacy = ({
6060
const themeColors = getThemeColor(selectedTheme)
6161
const [isOpen, setIsOpen] = useState<number | null>(null)
6262
const [userMessage, setUserMessage] = useState('')
63+
const [isSubmitting, setIsSubmitting] = useState(false)
6364
const { loadingWebhooks, webhookModal } = useSelector((state: RootState) => state.webhookReducer)
6465

6566
const webhookResourceId = ADMIN_UI_RESOURCES.Webhooks
@@ -90,14 +91,21 @@ const GluuCommitDialogLegacy = ({
9091
prevModalRef.current = modal
9192
}, [modal])
9293

93-
function handleAccept() {
94+
async function handleAccept() {
95+
if (isSubmitting) return
9496
if (formik) {
9597
formik.setFieldValue('action_message', userMessage)
9698
}
97-
onAccept(userMessage)
98-
handler()
99-
onCloseModal()
100-
setUserMessage('')
99+
setIsSubmitting(true)
100+
try {
101+
const result = onAccept(userMessage)
102+
await Promise.resolve(result)
103+
} finally {
104+
setIsSubmitting(false)
105+
handler()
106+
onCloseModal()
107+
setUserMessage('')
108+
}
101109
}
102110

103111
const closeModal = () => {
@@ -324,7 +332,7 @@ const GluuCommitDialogLegacy = ({
324332
backgroundColor={customColors.primaryDark}
325333
textColor={customColors.white}
326334
disableHoverStyles
327-
disabled={!active}
335+
disabled={!active || isSubmitting}
328336
>
329337
<i className="fa fa-check-circle me-2" />
330338
{t('actions.accept')}

admin-ui/app/utils/hooks/useWebhookDialogAction.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { useCedarling } from '@/cedarling'
2222
import { ADMIN_UI_RESOURCES } from '@/cedarling/utility'
2323
import { CEDAR_RESOURCE_SCOPES } from '@/cedarling/constants/resourceScopes'
2424
import customColors from '@/customColors'
25+
import GluuLoader from 'Routes/Apps/Gluu/GluuLoader'
2526

2627
interface UseWebhookDialogActionProps {
2728
feature?: string
@@ -111,9 +112,7 @@ const useWebhookDialogAction = ({ feature, modal }: UseWebhookDialogActionProps)
111112
className="modal-outline-primary"
112113
>
113114
<ModalHeader toggle={closeWebhookTriggerModal}>
114-
{loadingWebhooks ? (
115-
<>Loading....</>
116-
) : (
115+
{!loadingWebhooks && (
117116
<>
118117
<i
119118
onClick={closeWebhookTriggerModal}
@@ -127,7 +126,11 @@ const useWebhookDialogAction = ({ feature, modal }: UseWebhookDialogActionProps)
127126
</>
128127
)}
129128
</ModalHeader>
130-
{!loadingWebhooks ? (
129+
{loadingWebhooks ? (
130+
<ModalBody>
131+
<GluuLoader blocking />
132+
</ModalBody>
133+
) : (
131134
<>
132135
<ModalBody>
133136
<Box sx={{ display: 'flex', flexDirection: 'column' }} px={2}>
@@ -184,8 +187,6 @@ const useWebhookDialogAction = ({ feature, modal }: UseWebhookDialogActionProps)
184187
</Button>
185188
</ModalFooter>
186189
</>
187-
) : (
188-
<></>
189190
)}
190191
</Modal>
191192
)

admin-ui/plugins/auth-server/components/Configuration/AuthPage.tsx

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useEffect, useState, useMemo, useCallback, useRef } from 'react'
22
import { useTranslation } from 'react-i18next'
3-
import { useDispatch, useSelector } from 'react-redux'
3+
import { useDispatch, useSelector, useStore } from 'react-redux'
44
import { useQueryClient } from '@tanstack/react-query'
55
import { useFormik, setIn } from 'formik'
66
import * as Yup from 'yup'
@@ -29,12 +29,7 @@ import { CEDAR_RESOURCE_SCOPES } from '@/cedarling/constants/resourceScopes'
2929
import { ADMIN_UI_RESOURCES } from '@/cedarling/utility'
3030
import { useAppNavigation, ROUTES } from '@/helpers/navigation'
3131
import { useAcrAudit } from '../AuthN/hooks'
32-
import {
33-
generateLabel,
34-
isRenamedKey,
35-
renamedFieldFromObject,
36-
useAuthServerPropertiesActions,
37-
} from './Properties/utils'
32+
import { generateLabel, isRenamedKey, renamedFieldFromObject } from './Properties/utils'
3833
import { createAppConfigurationSchema } from './Properties/utils/validations'
3934
import type { AppConfiguration, RootState, JsonPatch, AcrPutOperation, Script } from './types'
4035
import type { GluuCommitDialogOperation, JsonValue } from 'Routes/Apps/Gluu/types/index'
@@ -45,12 +40,13 @@ const AuthPage: React.FC = () => {
4540
SetTitle(t('titles.jans_json_property'))
4641

4742
const dispatch = useDispatch()
43+
const store = useStore()
4844
const queryClient = useQueryClient()
4945
const { navigateBack } = useAppNavigation()
5046
const { hasCedarWritePermission, authorizeHelper } = useCedarling()
51-
const { logAuthServerPropertiesUpdate } = useAuthServerPropertiesActions()
5247
const { logAcrUpdate } = useAcrAudit()
5348
const configuration = useSelector((state: RootState) => state.jsonConfigReducer.configuration)
49+
const jsonConfigLoading = useSelector((state: RootState) => state.jsonConfigReducer.loading)
5450
const scripts = useSelector((state: RootState) => state.initReducer.scripts)
5551
const { data: acrs, isLoading: acrLoading } = useGetAcrs({
5652
query: { staleTime: 30000 },
@@ -294,6 +290,16 @@ const AuthPage: React.FC = () => {
294290
} as unknown as ActionData
295291
buildPayload(userAction, message, postBody)
296292
dispatch(patchJsonConfig({ action: userAction }))
293+
await new Promise<void>((resolve) => {
294+
const unsub = store.subscribe(() => {
295+
const loading = (store.getState() as { jsonConfigReducer?: { loading?: boolean } })
296+
?.jsonConfigReducer?.loading
297+
if (!loading) {
298+
unsub()
299+
resolve()
300+
}
301+
})
302+
})
297303
}
298304
if (put && put.value) {
299305
const newAcr = { defaultAcr: put.value || acrs?.defaultAcr }
@@ -304,21 +310,8 @@ const AuthPage: React.FC = () => {
304310
console.error('Error updating ACR:', error)
305311
}
306312
}
307-
let auditSuccess = true
308-
try {
309-
const auditPayload = {
310-
requestBody: patches,
311-
...(put && put.value ? { defaultAcr: put.value } : {}),
312-
}
313-
auditSuccess = await logAuthServerPropertiesUpdate(message, auditPayload)
314-
} catch (auditError) {
315-
console.error('Error logging audit:', auditError)
316-
auditSuccess = false
317-
}
318-
if (auditSuccess) {
313+
if (patches.length === 0) {
319314
toast.success(t('messages.success_in_saving'))
320-
} else {
321-
toast.warning(t('messages.success_in_saving_audit_failed'))
322315
}
323316
} catch (err) {
324317
console.error('Error updating auth server properties:', err)
@@ -327,18 +320,19 @@ const AuthPage: React.FC = () => {
327320
toast.error(errorMsg)
328321
}
329322
},
330-
[patches, put, acrs, dispatch, putAcrsMutation, logAcrUpdate, logAuthServerPropertiesUpdate, t],
323+
[patches, put, acrs, dispatch, store, putAcrsMutation, logAcrUpdate, t],
331324
)
332325
const submitForm = useCallback(
333326
async (message: string) => {
334-
toggle()
335327
await handleSubmit(message)
336328
},
337-
[toggle, handleSubmit],
329+
[handleSubmit],
338330
)
339331

340332
return (
341-
<GluuLoader blocking={isConfigEmpty || acrLoading || putAcrsMutation.isPending}>
333+
<GluuLoader
334+
blocking={isConfigEmpty || acrLoading || putAcrsMutation.isPending || jsonConfigLoading}
335+
>
342336
<Card style={{ borderRadius: 24 }}>
343337
<CardHeader>
344338
<div style={{ display: 'flex' }}>

admin-ui/plugins/auth-server/components/Configuration/ConfigApiConfiguration/ApiConfigForm.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,6 @@ const ApiConfigForm: React.FC<ApiConfigFormProps> = ({ configuration, onSubmit }
256256

257257
const submitForm = useCallback(
258258
async (userMessage: string) => {
259-
toggle()
260259
try {
261260
await onSubmit(patches, userMessage)
262261
setPatches([])
@@ -265,7 +264,7 @@ const ApiConfigForm: React.FC<ApiConfigFormProps> = ({ configuration, onSubmit }
265264
toast.error(t('messages.error_in_saving'))
266265
}
267266
},
268-
[toggle, onSubmit, patches, t],
267+
[onSubmit, patches, t],
269268
)
270269

271270
const handleBack = useCallback(() => {

admin-ui/plugins/fido/components/DynamicConfiguration.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,9 @@ const DynamicConfiguration: React.FC<DynamicConfigurationProps> = ({
5353
if (readOnly) {
5454
return
5555
}
56-
toggle()
5756
handleSubmit(formik.values, userMessage)
5857
},
59-
[handleSubmit, toggle, formik.values, readOnly],
58+
[handleSubmit, formik.values, readOnly],
6059
)
6160

6261
const handleCancel = useCallback(() => {

admin-ui/plugins/fido/components/StaticConfiguration.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,9 @@ const StaticConfiguration: React.FC<StaticConfigurationProps> = ({
7171
if (readOnly) {
7272
return
7373
}
74-
toggle()
7574
handleSubmit(formik.values, userMessage)
7675
},
77-
[handleSubmit, toggle, formik.values, readOnly],
76+
[handleSubmit, formik.values, readOnly],
7877
)
7978

8079
const handleCancel = useCallback(() => {

admin-ui/plugins/scim/components/ScimConfiguration.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,13 @@ const ScimConfiguration: React.FC<ScimConfigurationProps> = ({
5151
}, [formik.values])
5252

5353
const submitForm = useCallback(
54-
(userMessage: string): void => {
55-
toggle()
56-
handleSubmit({ ...formik.values, action_message: userMessage })
54+
(userMessage: string): void | Promise<unknown> => {
55+
return handleSubmit({
56+
...formik.values,
57+
action_message: userMessage,
58+
}) as void | Promise<unknown>
5759
},
58-
[handleSubmit, toggle, formik.values],
60+
[handleSubmit, formik.values],
5961
)
6062

6163
const handleCancel = useCallback(() => {

admin-ui/plugins/scim/components/ScimPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ const ScimPage: React.FC = () => {
143143
})
144144

145145
const handleSubmit = useCallback(
146-
(formValues: ScimFormValues): void => {
146+
(formValues: ScimFormValues): void | Promise<unknown> => {
147147
if (!scimConfiguration) {
148148
dispatch(updateToast(true, 'error', t('messages.no_configuration_loaded')))
149149
return
@@ -155,7 +155,7 @@ const ScimPage: React.FC = () => {
155155
return
156156
}
157157
userMessageRef.current = action_message || ''
158-
patchScimMutation.mutate({ data: patches })
158+
return patchScimMutation.mutateAsync({ data: patches })
159159
},
160160
[scimConfiguration, patchScimMutation, dispatch, t],
161161
)

admin-ui/plugins/scim/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface ScimFormValues {
2525

2626
export interface ScimConfigurationProps {
2727
scimConfiguration: AppConfiguration3 | undefined
28-
handleSubmit: (formValues: ScimFormValues) => void
28+
handleSubmit: (formValues: ScimFormValues) => void | Promise<unknown>
2929
isSubmitting: boolean
3030
canWriteScim?: boolean
3131
}

0 commit comments

Comments
 (0)