From 7e66a79f9f6f2c6f2475bfd723ea938bbd4fb0f6 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Thu, 8 Feb 2024 10:27:51 +0100 Subject: [PATCH] feat: add disabled state handling on slow network (#6165) --- .../ChangeRequestConfirmDialog.tsx | 3 + .../ChangeRequestOverview.tsx | 58 ++++++++++++++----- .../ChangeRequestRejectDialog.tsx | 3 + .../ChangeRequestScheduledDialog.tsx | 2 + .../ScheduleChangeRequestDialog.tsx | 1 + .../MultiActionButton/MultiActionButton.tsx | 1 + .../EnvironmentChangeRequest.tsx | 42 +++++++++----- .../useFeatureToggleSwitch.tsx | 2 + frontend/src/hooks/useChangeRequestToggle.ts | 5 ++ 9 files changed, 91 insertions(+), 26 deletions(-) diff --git a/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog.tsx b/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog.tsx index 331aa3c83e..ef732903a0 100644 --- a/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog.tsx @@ -13,10 +13,12 @@ interface IChangeRequestDialogueProps { environment?: string; showBanner?: boolean; messageComponent: JSX.Element; + disabled?: boolean; } export const ChangeRequestDialogue: FC = ({ isOpen, + disabled = false, onConfirm, onClose, showBanner, @@ -40,6 +42,7 @@ export const ChangeRequestDialogue: FC = ({ open={isOpen} primaryButtonText={primaryButtonText} secondaryButtonText='Cancel' + disabledPrimaryButton={disabled} onClick={onConfirm} onClose={onClose} title='Request changes' diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx index 0746576f7a..d0d87ab115 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx @@ -97,13 +97,14 @@ export const ChangeRequestOverview: FC = () => { projectId, id, ); - const { changeState, addComment, loading } = useChangeRequestApi(); + const { changeState, addComment } = useChangeRequestApi(); const { refetch: refetchChangeRequestOpen } = usePendingChangeRequests(projectId); const { setToastData, setToastApiError } = useToast(); const { isChangeRequestConfiguredForReview } = useChangeRequestsEnabled(projectId); const scheduleChangeRequests = useUiFlag('scheduledConfigurationChanges'); + const [disabled, setDisabled] = useState(false); if (!changeRequest) { return null; @@ -124,11 +125,12 @@ export const ChangeRequestOverview: FC = () => { const onApplyChanges = async () => { try { + setDisabled(true); await changeState(projectId, Number(id), getCurrentState(), { state: 'Applied', }); setShowApplyScheduledDialog(false); - refetchChangeRequest(); + await refetchChangeRequest(); refetchChangeRequestOpen(); setToastData({ type: 'success', @@ -137,11 +139,14 @@ export const ChangeRequestOverview: FC = () => { }); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); + } finally { + setDisabled(false); } }; const onScheduleChangeRequest = async (scheduledDate: Date) => { try { + setDisabled(true); await changeState(projectId, Number(id), getCurrentState(), { state: 'Scheduled', scheduledAt: scheduledDate.toISOString(), @@ -156,14 +161,17 @@ export const ChangeRequestOverview: FC = () => { }); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); + } finally { + setDisabled(false); } }; const onAddComment = async () => { try { + setDisabled(true); await addComment(projectId, id, commentText); setCommentText(''); - refetchChangeRequest(); + await refetchChangeRequest(); setToastData({ type: 'success', title: 'Success', @@ -171,16 +179,19 @@ export const ChangeRequestOverview: FC = () => { }); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); + } finally { + setDisabled(false); } }; const onCancelChanges = async () => { try { + setDisabled(true); await changeState(projectId, Number(id), getCurrentState(), { state: 'Cancelled', }); setShowCancelDialog(false); - refetchChangeRequest(); + await refetchChangeRequest(); refetchChangeRequestOpen(); setToastData({ type: 'success', @@ -189,34 +200,41 @@ export const ChangeRequestOverview: FC = () => { }); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); + } finally { + setDisabled(false); } }; const onReject = async (comment?: string) => { try { + setDisabled(true); await changeState(projectId, Number(id), getCurrentState(), { state: 'Rejected', comment, }); setShowRejectDialog(false); + await refetchChangeRequest(); + setToastData({ type: 'success', title: 'Success', text: 'Changes rejected', }); - refetchChangeRequest(); refetchChangeRequestOpen(); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); + } finally { + setDisabled(false); } }; const onApprove = async () => { try { + setDisabled(true); await changeState(projectId, Number(id), getCurrentState(), { state: 'Approved', }); - refetchChangeRequest(); + await refetchChangeRequest(); refetchChangeRequestOpen(); setToastData({ type: 'success', @@ -225,6 +243,8 @@ export const ChangeRequestOverview: FC = () => { }); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); + } finally { + setDisabled(false); } }; @@ -313,7 +333,8 @@ export const ChangeRequestOverview: FC = () => { disabled={ !allowChangeRequestActions || commentText.trim().length === 0 || - commentText.trim().length > 1000 + commentText.trim().length > 1000 || + disabled } > Comment @@ -350,7 +371,10 @@ export const ChangeRequestOverview: FC = () => { setShowRejectDialog(true) } onApprove={onApprove} - disabled={!allowChangeRequestActions} + disabled={ + !allowChangeRequestActions || + disabled + } > Review changes ({countOfChanges}) @@ -366,7 +390,7 @@ export const ChangeRequestOverview: FC = () => { onApply={onApplyChanges} disabled={ !allowChangeRequestActions || - loading + disabled } onSchedule={() => setShowScheduleChangeDialog( @@ -390,7 +414,7 @@ export const ChangeRequestOverview: FC = () => { } disabled={ !allowChangeRequestActions || - loading + disabled } > Apply changes @@ -411,7 +435,7 @@ export const ChangeRequestOverview: FC = () => { } disabled={ !allowChangeRequestActions || - loading + disabled } onSchedule={() => setShowScheduleChangeDialog(true) @@ -445,6 +469,7 @@ export const ChangeRequestOverview: FC = () => { true, ) } + disabled={disabled} > Reject changes @@ -453,6 +478,7 @@ export const ChangeRequestOverview: FC = () => { Cancel changes @@ -485,6 +511,7 @@ export const ChangeRequestOverview: FC = () => { open={showRejectDialog} onConfirm={onReject} onClose={onCancelReject} + disabled={disabled} /> { open={showScheduleChangesDialog} onConfirm={onScheduleChangeRequest} onClose={onScheduleChangeAbort} - disabled={!allowChangeRequestActions || loading} + disabled={ + !allowChangeRequestActions || disabled + } projectId={projectId} environment={changeRequest.environment} primaryButtonText={ @@ -514,7 +543,9 @@ export const ChangeRequestOverview: FC = () => { onConfirm={onApplyChanges} onClose={onApplyScheduledAbort} scheduledTime={scheduledAt} - disabled={!allowChangeRequestActions || loading} + disabled={ + !allowChangeRequestActions || disabled + } projectId={projectId} environment={changeRequest.environment} /> @@ -523,6 +554,7 @@ export const ChangeRequestOverview: FC = () => { onConfirm={onReject} onClose={onRejectScheduledAbort} scheduledTime={scheduledAt} + disabled={disabled} /> } diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestRejectDialog/ChangeRequestRejectDialog.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestRejectDialog/ChangeRequestRejectDialog.tsx index a407bf00ec..39678eb893 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestRejectDialog/ChangeRequestRejectDialog.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestRejectDialog/ChangeRequestRejectDialog.tsx @@ -6,12 +6,14 @@ interface IChangeRequestDialogueProps { open: boolean; onConfirm: (comment?: string) => void; onClose: () => void; + disabled?: boolean; } export const ChangeRequestRejectDialogue: FC = ({ open, onConfirm, onClose, + disabled = false, }) => { const [commentText, setCommentText] = useState(''); @@ -21,6 +23,7 @@ export const ChangeRequestRejectDialogue: FC = ({ primaryButtonText='Reject changes' secondaryButtonText='Cancel' onClick={() => onConfirm(commentText)} + disabledPrimaryButton={disabled} onClose={onClose} title='Reject changes' fullWidth diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/ChangeRequestScheduledDialog.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/ChangeRequestScheduledDialog.tsx index 0f8a1738ad..e2483a976c 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/ChangeRequestScheduledDialog.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestScheduledDialogs/ChangeRequestScheduledDialog.tsx @@ -29,6 +29,7 @@ export const ChangeRequestScheduledDialog: FC< onClose, title, primaryButtonText, + disabled, message, scheduledTime, permissionButton, @@ -39,6 +40,7 @@ export const ChangeRequestScheduledDialog: FC< = onClose()} diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx index 22bdacdc83..cc0c663e6b 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/MultiActionButton/MultiActionButton.tsx @@ -102,6 +102,7 @@ export const MultiActionButton: FC<{ {actions.map( ({ label, onSelect, icon }) => ( diff --git a/frontend/src/component/changeRequest/ChangeRequestSidebar/EnvironmentChangeRequest/EnvironmentChangeRequest.tsx b/frontend/src/component/changeRequest/ChangeRequestSidebar/EnvironmentChangeRequest/EnvironmentChangeRequest.tsx index 45a6c7973f..14ea774daa 100644 --- a/frontend/src/component/changeRequest/ChangeRequestSidebar/EnvironmentChangeRequest/EnvironmentChangeRequest.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestSidebar/EnvironmentChangeRequest/EnvironmentChangeRequest.tsx @@ -25,11 +25,17 @@ import { ChangeRequestTitle } from './ChangeRequestTitle'; import { UpdateCount } from 'component/changeRequest/UpdateCount'; import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; -const SubmitChangeRequestButton: FC<{ onClick: () => void; count: number }> = ({ - onClick, - count, -}) => ( - ); @@ -66,11 +72,18 @@ export const EnvironmentChangeRequest: FC<{ const { user } = useAuthUser(); const [title, setTitle] = useState(environmentChangeRequest.title); const { changeState } = useChangeRequestApi(); - const sendToReview = async (project: string) => - changeState(project, environmentChangeRequest.id, 'Draft', { - state: 'In review', - comment: commentText, - }); + const [disabled, setDisabled] = useState(false); + const sendToReview = async (project: string) => { + setDisabled(true); + try { + await changeState(project, environmentChangeRequest.id, 'Draft', { + state: 'In review', + comment: commentText, + }); + } catch (e) { + setDisabled(false); + } + }; return ( @@ -152,14 +165,17 @@ export const EnvironmentChangeRequest: FC<{ count={changesCount( environmentChangeRequest, )} + disabled={disabled} /> diff --git a/frontend/src/component/project/Project/ProjectFeatureToggles/FeatureToggleSwitch/useFeatureToggleSwitch.tsx b/frontend/src/component/project/Project/ProjectFeatureToggles/FeatureToggleSwitch/useFeatureToggleSwitch.tsx index 51c51bbe0e..6089e547fe 100644 --- a/frontend/src/component/project/Project/ProjectFeatureToggles/FeatureToggleSwitch/useFeatureToggleSwitch.tsx +++ b/frontend/src/component/project/Project/ProjectFeatureToggles/FeatureToggleSwitch/useFeatureToggleSwitch.tsx @@ -53,6 +53,7 @@ export const useFeatureToggleSwitch: UseFeatureToggleSwitchType = ( onAddDefaultStrategy: () => {}, }); const { + pending, onChangeRequestToggle, onChangeRequestToggleClose, onChangeRequestToggleConfirm, @@ -233,6 +234,7 @@ export const useFeatureToggleSwitch: UseFeatureToggleSwitchType = ( onChangeRequestToggleClose(); }} environment={changeRequestDialogDetails?.environment} + disabled={pending} onConfirm={() => { changeRequestDialogCallback?.(); onChangeRequestToggleConfirm(); diff --git a/frontend/src/hooks/useChangeRequestToggle.ts b/frontend/src/hooks/useChangeRequestToggle.ts index 00051c1a3e..1e306d71bf 100644 --- a/frontend/src/hooks/useChangeRequestToggle.ts +++ b/frontend/src/hooks/useChangeRequestToggle.ts @@ -9,6 +9,7 @@ export const useChangeRequestToggle = (project: string) => { const { addChange } = useChangeRequestApi(); const { refetch: refetchChangeRequests } = usePendingChangeRequests(project); + const [pending, setPending] = useState(false); const [changeRequestDialogDetails, setChangeRequestDialogDetails] = useState<{ @@ -43,6 +44,7 @@ export const useChangeRequestToggle = (project: string) => { const onChangeRequestToggleConfirm = useCallback(async () => { try { + setPending(true); await addChange(project, changeRequestDialogDetails.environment!, { feature: changeRequestDialogDetails.featureName!, action: 'updateEnabled', @@ -68,10 +70,13 @@ export const useChangeRequestToggle = (project: string) => { ...prev, isOpen: false, })); + } finally { + setPending(false); } }, [addChange]); return { + pending, onChangeRequestToggle, onChangeRequestToggleClose, onChangeRequestToggleConfirm,