fix confirm close dialog + add restart confirm dialog (#12761)

Test: 
- On upload > No dialog at modal closing
- On match > Confirm cancel dialog at closing (escape, click outside,
cancel cross)
- On match > Restart dialog at Restart Import
- On validation > Confirm cancel dialog at closing (escape, click
outside, cancel cross)
- On import > Confirm cancel dialog at closing (escape, click outside,
cancel cross)
- On import > No confirm at import end

closes : https://github.com/twentyhq/core-team-issues/issues/1071
This commit is contained in:
Etienne
2025-06-20 15:20:17 +02:00
committed by GitHub
parent 0e669e621d
commit 19fe508ec3
6 changed files with 75 additions and 49 deletions

View File

@ -1,10 +1,5 @@
import styled from '@emotion/styled';
import { useSpreadsheetImportInitialStep } from '@/spreadsheet-import/hooks/useSpreadsheetImportInitialStep';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
import { useDialogManager } from '@/ui/feedback/dialog-manager/hooks/useDialogManager';
import { useStepBar } from '@/ui/navigation/step-bar/hooks/useStepBar';
import { useLingui } from '@lingui/react/macro';
import { IconX } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
@ -20,49 +15,15 @@ const StyledCloseButtonContainer = styled.div`
`;
type SpreadSheetImportModalCloseButtonProps = {
onClose?: () => void;
onClose: () => void;
};
export const SpreadSheetImportModalCloseButton = ({
onClose,
}: SpreadSheetImportModalCloseButtonProps) => {
const { initialStepState } = useSpreadsheetImportInternal();
const { initialStep } = useSpreadsheetImportInitialStep(
initialStepState?.type,
);
const { activeStep } = useStepBar({
initialStep,
});
const { enqueueDialog } = useDialogManager();
const { t } = useLingui();
const handleClose = () => {
if (activeStep === -1) {
onClose?.();
return;
}
enqueueDialog({
title: t`Exit import flow`,
message: t`Are you sure? Your current information will not be saved.`,
buttons: [
{ title: t`Cancel` },
{
title: t`Exit`,
onClick: onClose,
accent: 'danger',
role: 'confirm',
},
],
});
};
return (
<StyledCloseButtonContainer>
<IconButton Icon={IconX} onClick={handleClose} />
<IconButton Icon={IconX} onClick={onClose} />
</StyledCloseButtonContainer>
);
};

View File

@ -28,7 +28,7 @@ const StyledRtlLtr = styled.div`
type SpreadSheetImportModalWrapperProps = {
children: React.ReactNode;
modalId: string;
onClose?: () => void;
onClose: () => void;
};
export const SpreadSheetImportModalWrapper = ({
@ -44,6 +44,7 @@ export const SpreadSheetImportModalWrapper = ({
modalId={modalId}
isClosable={true}
onClose={onClose}
shouldCloseModalOnClickOutsideOrEscape={false}
>
<StyledRtlLtr dir={rtl ? 'rtl' : 'ltr'}>
<SpreadSheetImportModalCloseButton onClose={onClose} />

View File

@ -2,8 +2,13 @@ import { ReactSpreadsheetImportContextProvider } from '@/spreadsheet-import/comp
import { SpreadSheetImportModalWrapper } from '@/spreadsheet-import/components/SpreadSheetImportModalWrapper';
import { SPREADSHEET_IMPORT_MODAL_ID } from '@/spreadsheet-import/constants/SpreadsheetImportModalId';
import { SpreadsheetMaxRecordImportCapacity } from '@/spreadsheet-import/constants/SpreadsheetMaxRecordImportCapacity';
import { useSpreadsheetImportInitialStep } from '@/spreadsheet-import/hooks/useSpreadsheetImportInitialStep';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
import { SpreadsheetImportStepperContainer } from '@/spreadsheet-import/steps/components/SpreadsheetImportStepperContainer';
import { SpreadsheetImportDialogOptions as SpreadsheetImportProps } from '@/spreadsheet-import/types';
import { useDialogManager } from '@/ui/feedback/dialog-manager/hooks/useDialogManager';
import { useStepBar } from '@/ui/navigation/step-bar/hooks/useStepBar';
import { useLingui } from '@lingui/react/macro';
export const defaultSpreadsheetImportProps: Partial<
SpreadsheetImportProps<any>
@ -31,11 +36,46 @@ export const SpreadsheetImport = <T extends string>(
...props,
} as SpreadsheetImportProps<T>;
const { enqueueDialog } = useDialogManager();
const { initialStepState } = useSpreadsheetImportInternal();
const { initialStep } = useSpreadsheetImportInitialStep(
initialStepState?.type,
);
const { activeStep } = useStepBar({
initialStep,
});
const { t } = useLingui();
const confirmOnClose = () => {
if (activeStep < 1) {
mergedProps.onClose();
return;
}
enqueueDialog({
title: t`Exit import flow`,
message: t`Are you sure? Your current information will not be saved.`,
buttons: [
{ title: t`Cancel` },
{
title: t`Exit`,
onClick: mergedProps.onClose,
accent: 'danger',
role: 'confirm',
},
],
});
};
return (
<ReactSpreadsheetImportContextProvider values={mergedProps}>
<SpreadSheetImportModalWrapper
modalId={SPREADSHEET_IMPORT_MODAL_ID}
onClose={mergedProps.onClose}
onClose={confirmOnClose}
>
<SpreadsheetImportStepperContainer />
</SpreadSheetImportModalWrapper>

View File

@ -247,6 +247,27 @@ export const MatchColumnsStep = <T extends string>({
),
);
const onBackConfirmation = () => {
onBack?.();
setColumns([]);
};
const openRestartDialog = () => {
enqueueDialog({
title: t`Restart Import`,
message: t`You will lose all your mappings.`,
buttons: [
{ title: t`Cancel` },
{
title: t`Restart`,
onClick: onBackConfirmation,
accent: 'danger',
role: 'confirm',
},
],
});
};
return (
<>
<StyledContent>
@ -282,10 +303,8 @@ export const MatchColumnsStep = <T extends string>({
onContinue={handleOnContinue}
isLoading={isLoading}
continueTitle={t`Next Step`}
onBack={() => {
onBack?.();
setColumns([]);
}}
backTitle={t`Restart Import`}
onBack={openRestartDialog}
isContinueDisabled={!hasMatchedColumns}
/>
</>

View File

@ -42,7 +42,10 @@ export default meta;
export const Default = () => (
<DialogManagerScope dialogManagerScopeId="dialog-manager">
<ReactSpreadsheetImportContextProvider values={mockRsiValues}>
<SpreadSheetImportModalWrapper modalId="select-header-step">
<SpreadSheetImportModalWrapper
modalId="select-header-step"
onClose={() => null}
>
<SelectHeaderStep
importedRows={headerSelectionTableFields}
setCurrentStepState={() => null}

View File

@ -195,6 +195,7 @@ export type ModalProps = React.PropsWithChildren & {
onEnter?: () => void;
modalVariant?: ModalVariants;
dataGloballyPreventClickOutside?: boolean;
shouldCloseModalOnClickOutsideOrEscape?: boolean;
} & (
| { isClosable: true; onClose?: () => void }
| { isClosable?: false; onClose?: never }
@ -217,6 +218,7 @@ export const Modal = ({
onClose,
modalVariant = 'primary',
dataGloballyPreventClickOutside = false,
shouldCloseModalOnClickOutsideOrEscape = true,
}: ModalProps) => {
const isMobile = useIsMobile();
const modalRef = useRef<HTMLDivElement>(null);
@ -236,7 +238,7 @@ export const Modal = ({
const handleClose = () => {
onClose?.();
closeModal(modalId);
if (shouldCloseModalOnClickOutsideOrEscape) closeModal(modalId);
};
return (