mirror of
https://github.com/lingble/twenty.git
synced 2025-10-30 12:22:29 +00:00
Verification popup can be activated multiple times (#6938)
Fixes https://github.com/twentyhq/twenty/issues/6912 By clicking Enter key over and over, user can repeat action Expected: When 'yes' is typed in popup and user clicks Enter key once, popup should disappear and correlated action should be performed only once Implementation: - Added loading state for buttons onClick and onEnter to disable the button when the "Delete Api Key" and "Regenerate Api Key" function is called. - Added a new function to handle modal close and logic handling on clicking enter key. --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
|||||||
export type ConfirmationModalProps = {
|
export type ConfirmationModalProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
|
loading?: boolean;
|
||||||
subtitle: ReactNode;
|
subtitle: ReactNode;
|
||||||
setIsOpen: (val: boolean) => void;
|
setIsOpen: (val: boolean) => void;
|
||||||
onConfirmClick: () => void;
|
onConfirmClick: () => void;
|
||||||
@@ -59,6 +60,7 @@ export const StyledConfirmationButton = styled(StyledCenteredButton)`
|
|||||||
export const ConfirmationModal = ({
|
export const ConfirmationModal = ({
|
||||||
isOpen = false,
|
isOpen = false,
|
||||||
title,
|
title,
|
||||||
|
loading,
|
||||||
subtitle,
|
subtitle,
|
||||||
setIsOpen,
|
setIsOpen,
|
||||||
onConfirmClick,
|
onConfirmClick,
|
||||||
@@ -83,6 +85,18 @@ export const ConfirmationModal = ({
|
|||||||
250,
|
250,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleConfirmClick = () => {
|
||||||
|
onConfirmClick();
|
||||||
|
|
||||||
|
setIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEnter = () => {
|
||||||
|
if (isValidValue) {
|
||||||
|
handleConfirmClick();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence mode="wait">
|
<AnimatePresence mode="wait">
|
||||||
<LayoutGroup>
|
<LayoutGroup>
|
||||||
@@ -93,7 +107,7 @@ export const ConfirmationModal = ({
|
|||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onEnter={!isValidValue ? undefined : onConfirmClick}
|
onEnter={handleEnter}
|
||||||
isClosable={true}
|
isClosable={true}
|
||||||
padding="large"
|
padding="large"
|
||||||
>
|
>
|
||||||
@@ -125,14 +139,11 @@ export const ConfirmationModal = ({
|
|||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
<StyledCenteredButton
|
<StyledCenteredButton
|
||||||
onClick={async () => {
|
onClick={handleConfirmClick}
|
||||||
await onConfirmClick();
|
|
||||||
setIsOpen(false);
|
|
||||||
}}
|
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
accent={confirmButtonAccent}
|
accent={confirmButtonAccent}
|
||||||
title={deleteButtonText}
|
title={deleteButtonText}
|
||||||
disabled={!isValidValue}
|
disabled={!isValidValue || loading}
|
||||||
fullWidth
|
fullWidth
|
||||||
dataTestId="confirmation-modal-confirm-button"
|
dataTestId="confirmation-modal-confirm-button"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import { computeNewExpirationDate } from '@/settings/developers/utils/compute-ne
|
|||||||
import { formatExpiration } from '@/settings/developers/utils/format-expiration';
|
import { formatExpiration } from '@/settings/developers/utils/format-expiration';
|
||||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||||
import { SettingsPath } from '@/types/SettingsPath';
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { Button } from '@/ui/input/button/components/Button';
|
import { Button } from '@/ui/input/button/components/Button';
|
||||||
import { TextInput } from '@/ui/input/components/TextInput';
|
import { TextInput } from '@/ui/input/components/TextInput';
|
||||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||||
@@ -41,9 +43,11 @@ const StyledInputContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const SettingsDevelopersApiKeyDetail = () => {
|
export const SettingsDevelopersApiKeyDetail = () => {
|
||||||
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
const [isRegenerateKeyModalOpen, setIsRegenerateKeyModalOpen] =
|
const [isRegenerateKeyModalOpen, setIsRegenerateKeyModalOpen] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [isDeleteApiKeyModalOpen, setIsDeleteApiKeyModalOpen] = useState(false);
|
const [isDeleteApiKeyModalOpen, setIsDeleteApiKeyModalOpen] = useState(false);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { apiKeyId = '' } = useParams();
|
const { apiKeyId = '' } = useParams();
|
||||||
@@ -69,12 +73,22 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
const developerPath = getSettingsPagePath(SettingsPath.Developers);
|
const developerPath = getSettingsPagePath(SettingsPath.Developers);
|
||||||
|
|
||||||
const deleteIntegration = async (redirect = true) => {
|
const deleteIntegration = async (redirect = true) => {
|
||||||
await updateApiKey?.({
|
setIsLoading(true);
|
||||||
idToUpdate: apiKeyId,
|
|
||||||
updateOneRecordInput: { revokedAt: DateTime.now().toString() },
|
try {
|
||||||
});
|
await updateApiKey?.({
|
||||||
if (redirect) {
|
idToUpdate: apiKeyId,
|
||||||
navigate(developerPath);
|
updateOneRecordInput: { revokedAt: DateTime.now().toString() },
|
||||||
|
});
|
||||||
|
if (redirect) {
|
||||||
|
navigate(developerPath);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
enqueueSnackBar(`Error deleting api key: ${err}`, {
|
||||||
|
variant: SnackBarVariant.Error,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -102,20 +116,28 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
token: tokenData.data?.generateApiKeyToken.token,
|
token: tokenData.data?.generateApiKeyToken.token,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const regenerateApiKey = async () => {
|
const regenerateApiKey = async () => {
|
||||||
if (isNonEmptyString(apiKeyData?.name)) {
|
setIsLoading(true);
|
||||||
const newExpiresAt = computeNewExpirationDate(
|
try {
|
||||||
apiKeyData?.expiresAt,
|
if (isNonEmptyString(apiKeyData?.name)) {
|
||||||
apiKeyData?.createdAt,
|
const newExpiresAt = computeNewExpirationDate(
|
||||||
);
|
apiKeyData?.expiresAt,
|
||||||
const apiKey = await createIntegration(apiKeyData?.name, newExpiresAt);
|
apiKeyData?.createdAt,
|
||||||
await deleteIntegration(false);
|
);
|
||||||
|
const apiKey = await createIntegration(apiKeyData?.name, newExpiresAt);
|
||||||
|
await deleteIntegration(false);
|
||||||
|
|
||||||
if (isNonEmptyString(apiKey?.token)) {
|
if (isNonEmptyString(apiKey?.token)) {
|
||||||
setApiKeyToken(apiKey.token);
|
setApiKeyToken(apiKey.token);
|
||||||
navigate(`/settings/developers/api-keys/${apiKey.id}`);
|
navigate(`/settings/developers/api-keys/${apiKey.id}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
enqueueSnackBar(`Error regenerating api key: ${err}`, {
|
||||||
|
variant: SnackBarVariant.Error,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -225,6 +247,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
}
|
}
|
||||||
onConfirmClick={deleteIntegration}
|
onConfirmClick={deleteIntegration}
|
||||||
deleteButtonText="Delete"
|
deleteButtonText="Delete"
|
||||||
|
loading={isLoading}
|
||||||
/>
|
/>
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
confirmationPlaceholder="yes"
|
confirmationPlaceholder="yes"
|
||||||
@@ -241,6 +264,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
}
|
}
|
||||||
onConfirmClick={regenerateApiKey}
|
onConfirmClick={regenerateApiKey}
|
||||||
deleteButtonText="Regenerate key"
|
deleteButtonText="Regenerate key"
|
||||||
|
loading={isLoading}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user