mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-01 01:18:10 +02:00
feat: add disabled state handling on slow network (#6165)
This commit is contained in:
parent
bc7d4b8edb
commit
7e66a79f9f
@ -13,10 +13,12 @@ interface IChangeRequestDialogueProps {
|
||||
environment?: string;
|
||||
showBanner?: boolean;
|
||||
messageComponent: JSX.Element;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const ChangeRequestDialogue: FC<IChangeRequestDialogueProps> = ({
|
||||
isOpen,
|
||||
disabled = false,
|
||||
onConfirm,
|
||||
onClose,
|
||||
showBanner,
|
||||
@ -40,6 +42,7 @@ export const ChangeRequestDialogue: FC<IChangeRequestDialogueProps> = ({
|
||||
open={isOpen}
|
||||
primaryButtonText={primaryButtonText}
|
||||
secondaryButtonText='Cancel'
|
||||
disabledPrimaryButton={disabled}
|
||||
onClick={onConfirm}
|
||||
onClose={onClose}
|
||||
title='Request changes'
|
||||
|
@ -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})
|
||||
</ReviewButton>
|
||||
@ -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
|
||||
</StyledButton>
|
||||
@ -453,6 +478,7 @@ export const ChangeRequestOverview: FC = () => {
|
||||
<StyledButton
|
||||
variant='outlined'
|
||||
onClick={onCancel}
|
||||
disabled={disabled}
|
||||
>
|
||||
Cancel changes
|
||||
</StyledButton>
|
||||
@ -485,6 +511,7 @@ export const ChangeRequestOverview: FC = () => {
|
||||
open={showRejectDialog}
|
||||
onConfirm={onReject}
|
||||
onClose={onCancelReject}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={scheduleChangeRequests}
|
||||
@ -494,7 +521,9 @@ export const ChangeRequestOverview: FC = () => {
|
||||
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}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
@ -6,12 +6,14 @@ interface IChangeRequestDialogueProps {
|
||||
open: boolean;
|
||||
onConfirm: (comment?: string) => void;
|
||||
onClose: () => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const ChangeRequestRejectDialogue: FC<IChangeRequestDialogueProps> = ({
|
||||
open,
|
||||
onConfirm,
|
||||
onClose,
|
||||
disabled = false,
|
||||
}) => {
|
||||
const [commentText, setCommentText] = useState('');
|
||||
|
||||
@ -21,6 +23,7 @@ export const ChangeRequestRejectDialogue: FC<IChangeRequestDialogueProps> = ({
|
||||
primaryButtonText='Reject changes'
|
||||
secondaryButtonText='Cancel'
|
||||
onClick={() => onConfirm(commentText)}
|
||||
disabledPrimaryButton={disabled}
|
||||
onClose={onClose}
|
||||
title='Reject changes'
|
||||
fullWidth
|
||||
|
@ -29,6 +29,7 @@ export const ChangeRequestScheduledDialog: FC<
|
||||
onClose,
|
||||
title,
|
||||
primaryButtonText,
|
||||
disabled,
|
||||
message,
|
||||
scheduledTime,
|
||||
permissionButton,
|
||||
@ -39,6 +40,7 @@ export const ChangeRequestScheduledDialog: FC<
|
||||
<Dialogue
|
||||
title={title}
|
||||
primaryButtonText={primaryButtonText}
|
||||
disabledPrimaryButton={disabled}
|
||||
secondaryButtonText='Cancel'
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
|
@ -48,6 +48,7 @@ export const ScheduleChangeRequestDialog: FC<ScheduleChangeRequestDialogProps> =
|
||||
<Dialogue
|
||||
title={title}
|
||||
primaryButtonText={primaryButtonText}
|
||||
disabledPrimaryButton={disabled}
|
||||
secondaryButtonText='Cancel'
|
||||
open={open}
|
||||
onClose={() => onClose()}
|
||||
|
@ -102,6 +102,7 @@ export const MultiActionButton: FC<{
|
||||
{actions.map(
|
||||
({ label, onSelect, icon }) => (
|
||||
<MenuItem
|
||||
disabled={disabled}
|
||||
onClick={onSelect}
|
||||
key={`MenuItem-${label}`}
|
||||
>
|
||||
|
@ -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,
|
||||
}) => (
|
||||
<Button sx={{ ml: 'auto' }} variant='contained' onClick={onClick}>
|
||||
const SubmitChangeRequestButton: FC<{
|
||||
onClick: () => void;
|
||||
count: number;
|
||||
disabled?: boolean;
|
||||
}> = ({ onClick, count, disabled = false }) => (
|
||||
<Button
|
||||
sx={{ ml: 'auto' }}
|
||||
variant='contained'
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
Submit change request ({count})
|
||||
</Button>
|
||||
);
|
||||
@ -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 (
|
||||
<Box key={environmentChangeRequest.id}>
|
||||
@ -152,14 +165,17 @@ export const EnvironmentChangeRequest: FC<{
|
||||
count={changesCount(
|
||||
environmentChangeRequest,
|
||||
)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
<Button
|
||||
sx={{ ml: 2 }}
|
||||
variant='outlined'
|
||||
onClick={() =>
|
||||
onDiscard(environmentChangeRequest.id)
|
||||
}
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
setDisabled(true);
|
||||
onDiscard(environmentChangeRequest.id);
|
||||
}}
|
||||
>
|
||||
Discard changes
|
||||
</Button>
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user