Files
twenty/packages/twenty-front/src/modules/ui/layout/modal/components/ConfirmationModal.tsx
Lý Thanh Bách 7a282b4363 Fix bug bypassing verification in confirmation modal when pressing Enter (#6889)
# Description
Fix bug bypassing verification in the confirmation modal when pressing
Enter

# Demo
Tested for webhook case (similar to other cases):
1. Press Enter when invalid verification => not delete webhook
2. Press Enter when valid verification => delete webhook


https://github.com/user-attachments/assets/81aa0aaa-7361-4584-b7ae-b29525f33664

# Ref
Fixes #6663
2024-09-04 15:15:29 +02:00

145 lines
4.3 KiB
TypeScript

import styled from '@emotion/styled';
import { AnimatePresence, LayoutGroup } from 'framer-motion';
import { ReactNode, useState } from 'react';
import { H1Title, H1TitleFontColor } from 'twenty-ui';
import { useDebouncedCallback } from 'use-debounce';
import { Button, ButtonAccent } from '@/ui/input/button/components/Button';
import { TextInput } from '@/ui/input/components/TextInput';
import { Modal } from '@/ui/layout/modal/components/Modal';
import {
Section,
SectionAlignment,
SectionFontColor,
} from '@/ui/layout/section/components/Section';
export type ConfirmationModalProps = {
isOpen: boolean;
title: string;
subtitle: ReactNode;
setIsOpen: (val: boolean) => void;
onConfirmClick: () => void;
deleteButtonText?: string;
confirmationPlaceholder?: string;
confirmationValue?: string;
confirmButtonAccent?: ButtonAccent;
};
const StyledConfirmationModal = styled(Modal)`
border-radius: ${({ theme }) => theme.spacing(1)};
width: calc(400px - ${({ theme }) => theme.spacing(32)});
height: auto;
`;
const StyledCenteredButton = styled(Button)`
justify-content: center;
margin-top: ${({ theme }) => theme.spacing(2)};
`;
const StyledCenteredTitle = styled.div`
text-align: center;
`;
const StyledSection = styled(Section)`
margin-bottom: ${({ theme }) => theme.spacing(6)};
`;
export const StyledConfirmationButton = styled(StyledCenteredButton)`
border-color: ${({ theme }) => theme.border.color.danger};
box-shadow: none;
color: ${({ theme }) => theme.color.red};
font-size: ${({ theme }) => theme.font.size.md};
line-height: ${({ theme }) => theme.text.lineHeight.lg};
:hover {
background-color: ${({ theme }) => theme.color.red10};
}
`;
export const ConfirmationModal = ({
isOpen = false,
title,
subtitle,
setIsOpen,
onConfirmClick,
deleteButtonText = 'Delete',
confirmationValue,
confirmationPlaceholder,
confirmButtonAccent = 'danger',
}: ConfirmationModalProps) => {
const [inputConfirmationValue, setInputConfirmationValue] =
useState<string>('');
const [isValidValue, setIsValidValue] = useState(!confirmationValue);
const handleInputConfimrationValueChange = (value: string) => {
setInputConfirmationValue(value);
isValueMatchingInput(confirmationValue, value);
};
const isValueMatchingInput = useDebouncedCallback(
(value?: string, inputValue?: string) => {
setIsValidValue(Boolean(value && inputValue && value === inputValue));
},
250,
);
return (
<AnimatePresence mode="wait">
<LayoutGroup>
{isOpen && (
<StyledConfirmationModal
onClose={() => {
if (isOpen) {
setIsOpen(false);
}
}}
onEnter={!isValidValue ? undefined : onConfirmClick}
isClosable={true}
padding="large"
>
<StyledCenteredTitle>
<H1Title title={title} fontColor={H1TitleFontColor.Primary} />
</StyledCenteredTitle>
<StyledSection
alignment={SectionAlignment.Center}
fontColor={SectionFontColor.Primary}
>
{subtitle}
</StyledSection>
{confirmationValue && (
<Section>
<TextInput
dataTestId="confirmation-modal-input"
value={inputConfirmationValue}
onChange={handleInputConfimrationValueChange}
placeholder={confirmationPlaceholder}
fullWidth
key={'input-' + confirmationValue}
/>
</Section>
)}
<StyledCenteredButton
onClick={() => setIsOpen(false)}
variant="secondary"
title="Cancel"
fullWidth
/>
<StyledCenteredButton
onClick={async () => {
await onConfirmClick();
setIsOpen(false);
}}
variant="secondary"
accent={confirmButtonAccent}
title={deleteButtonText}
disabled={!isValidValue}
fullWidth
dataTestId="confirmation-modal-confirm-button"
/>
</StyledConfirmationModal>
)}
</LayoutGroup>
</AnimatePresence>
);
};