Fixed: In CSV import now users are able to come back to the previous step. (#5625)
Users now can make a back transition from the current step state. - Added a `BackButton` component to `spreadsheet-import` in order to use it within the step state components. - Used the prebuilt `prevStep` from `useStepBar` and passed it as a prop to the `Uploadflow` to get the previous state as activestep. - Added a `previousState` to set the previous state with the required key data. - Added a `handleOnBack` function in `Uploadflow` to set the correct state and call the `prevStep` function to make the transition. - Added a callback function `onBack` and passed it as props to each step state component. fixes: #5564 https://github.com/twentyhq/twenty/assets/140178357/be7e1a0a-0fb8-41f2-a207-dfc3208ca6f0 --------- Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
This commit is contained in:
committed by
GitHub
parent
a12c1aad5e
commit
c7f2150ac7
@ -16,22 +16,22 @@ const StyledButton = styled(MainButton)`
|
|||||||
width: 200px;
|
width: 200px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type ContinueButtonProps = {
|
type StepNavigationButtonProps = {
|
||||||
onContinue: (val: any) => void;
|
onClick: () => void;
|
||||||
title: string;
|
title: string;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ContinueButton = ({
|
export const StepNavigationButton = ({
|
||||||
onContinue,
|
onClick,
|
||||||
title,
|
title,
|
||||||
isLoading,
|
isLoading,
|
||||||
}: ContinueButtonProps) => (
|
}: StepNavigationButtonProps) => (
|
||||||
<StyledFooter>
|
<StyledFooter>
|
||||||
<StyledButton
|
<StyledButton
|
||||||
Icon={isLoading ? CircularProgressBar : undefined}
|
Icon={isLoading ? CircularProgressBar : undefined}
|
||||||
title={title}
|
title={title}
|
||||||
onClick={!isLoading ? onContinue : undefined}
|
onClick={!isLoading ? onClick : undefined}
|
||||||
/>
|
/>
|
||||||
</StyledFooter>
|
</StyledFooter>
|
||||||
);
|
);
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
|
|
||||||
import { Heading } from '@/spreadsheet-import/components/Heading';
|
import { Heading } from '@/spreadsheet-import/components/Heading';
|
||||||
|
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
|
||||||
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import { Field, RawData } from '@/spreadsheet-import/types';
|
import { Field, RawData } from '@/spreadsheet-import/types';
|
||||||
import { findUnmatchedRequiredFields } from '@/spreadsheet-import/utils/findUnmatchedRequiredFields';
|
import { findUnmatchedRequiredFields } from '@/spreadsheet-import/utils/findUnmatchedRequiredFields';
|
||||||
@ -49,6 +49,7 @@ export type MatchColumnsStepProps<T extends string> = {
|
|||||||
data: RawData[];
|
data: RawData[];
|
||||||
headerValues: RawData;
|
headerValues: RawData;
|
||||||
onContinue: (data: any[], rawData: RawData[], columns: Columns<T>) => void;
|
onContinue: (data: any[], rawData: RawData[], columns: Columns<T>) => void;
|
||||||
|
onBack: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum ColumnType {
|
export enum ColumnType {
|
||||||
@ -112,6 +113,7 @@ export const MatchColumnsStep = <T extends string>({
|
|||||||
data,
|
data,
|
||||||
headerValues,
|
headerValues,
|
||||||
onContinue,
|
onContinue,
|
||||||
|
onBack,
|
||||||
}: MatchColumnsStepProps<T>) => {
|
}: MatchColumnsStepProps<T>) => {
|
||||||
const { enqueueDialog } = useDialogManager();
|
const { enqueueDialog } = useDialogManager();
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
@ -284,11 +286,12 @@ export const MatchColumnsStep = <T extends string>({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</StyledContent>
|
</StyledContent>
|
||||||
<ContinueButton
|
<StepNavigationButton
|
||||||
|
onClick={handleOnContinue}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onContinue={handleOnContinue}
|
|
||||||
title="Next"
|
title="Next"
|
||||||
/>
|
/>
|
||||||
|
<StepNavigationButton onClick={onBack} title="Back" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
|
|
||||||
import { Heading } from '@/spreadsheet-import/components/Heading';
|
import { Heading } from '@/spreadsheet-import/components/Heading';
|
||||||
|
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
|
||||||
import { RawData } from '@/spreadsheet-import/types';
|
import { RawData } from '@/spreadsheet-import/types';
|
||||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||||
|
|
||||||
@ -21,11 +21,13 @@ const StyledTableContainer = styled.div`
|
|||||||
type SelectHeaderStepProps = {
|
type SelectHeaderStepProps = {
|
||||||
data: RawData[];
|
data: RawData[];
|
||||||
onContinue: (headerValues: RawData, data: RawData[]) => Promise<void>;
|
onContinue: (headerValues: RawData, data: RawData[]) => Promise<void>;
|
||||||
|
onBack: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SelectHeaderStep = ({
|
export const SelectHeaderStep = ({
|
||||||
data,
|
data,
|
||||||
onContinue,
|
onContinue,
|
||||||
|
onBack,
|
||||||
}: SelectHeaderStepProps) => {
|
}: SelectHeaderStepProps) => {
|
||||||
const [selectedRows, setSelectedRows] = useState<ReadonlySet<number>>(
|
const [selectedRows, setSelectedRows] = useState<ReadonlySet<number>>(
|
||||||
new Set([0]),
|
new Set([0]),
|
||||||
@ -53,11 +55,12 @@ export const SelectHeaderStep = ({
|
|||||||
/>
|
/>
|
||||||
</StyledTableContainer>
|
</StyledTableContainer>
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
<ContinueButton
|
<StepNavigationButton
|
||||||
onContinue={handleContinue}
|
onClick={handleContinue}
|
||||||
title="Next"
|
title="Next"
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
/>
|
/>
|
||||||
|
<StepNavigationButton onClick={onBack} title="Back" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
|
|
||||||
import { Heading } from '@/spreadsheet-import/components/Heading';
|
import { Heading } from '@/spreadsheet-import/components/Heading';
|
||||||
|
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
|
||||||
import { Radio } from '@/ui/input/components/Radio';
|
import { Radio } from '@/ui/input/components/Radio';
|
||||||
import { RadioGroup } from '@/ui/input/components/RadioGroup';
|
import { RadioGroup } from '@/ui/input/components/RadioGroup';
|
||||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||||
@ -27,11 +27,13 @@ const StyledRadioContainer = styled.div`
|
|||||||
type SelectSheetStepProps = {
|
type SelectSheetStepProps = {
|
||||||
sheetNames: string[];
|
sheetNames: string[];
|
||||||
onContinue: (sheetName: string) => Promise<void>;
|
onContinue: (sheetName: string) => Promise<void>;
|
||||||
|
onBack: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SelectSheetStep = ({
|
export const SelectSheetStep = ({
|
||||||
sheetNames,
|
sheetNames,
|
||||||
onContinue,
|
onContinue,
|
||||||
|
onBack,
|
||||||
}: SelectSheetStepProps) => {
|
}: SelectSheetStepProps) => {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@ -58,11 +60,12 @@ export const SelectSheetStep = ({
|
|||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</StyledRadioContainer>
|
</StyledRadioContainer>
|
||||||
</StyledContent>
|
</StyledContent>
|
||||||
<ContinueButton
|
<StepNavigationButton
|
||||||
|
onClick={() => handleOnContinue(value)}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onContinue={() => handleOnContinue(value)}
|
|
||||||
title="Next"
|
title="Next"
|
||||||
/>
|
/>
|
||||||
|
<StepNavigationButton onClick={onBack} title="Back" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export const Steps = () => {
|
|||||||
initialStepState?.type,
|
initialStepState?.type,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { nextStep, activeStep } = useStepBar({
|
const { nextStep, prevStep, activeStep } = useStepBar({
|
||||||
initialStep,
|
initialStep,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ export const Steps = () => {
|
|||||||
))}
|
))}
|
||||||
</StepBar>
|
</StepBar>
|
||||||
</StyledHeader>
|
</StyledHeader>
|
||||||
<UploadFlow nextStep={nextStep} />
|
<UploadFlow nextStep={nextStep} prevStep={prevStep} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -59,14 +59,18 @@ export type StepState =
|
|||||||
|
|
||||||
interface UploadFlowProps {
|
interface UploadFlowProps {
|
||||||
nextStep: () => void;
|
nextStep: () => void;
|
||||||
|
prevStep: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
|
export const UploadFlow = ({ nextStep, prevStep }: UploadFlowProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { initialStepState } = useSpreadsheetImportInternal();
|
const { initialStepState } = useSpreadsheetImportInternal();
|
||||||
const [state, setState] = useState<StepState>(
|
const [state, setState] = useState<StepState>(
|
||||||
initialStepState || { type: StepType.upload },
|
initialStepState || { type: StepType.upload },
|
||||||
);
|
);
|
||||||
|
const [previousState, setPreviousState] = useState<StepState>(
|
||||||
|
initialStepState || { type: StepType.upload },
|
||||||
|
);
|
||||||
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
||||||
const {
|
const {
|
||||||
maxRecords,
|
maxRecords,
|
||||||
@ -87,6 +91,11 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
|
|||||||
[enqueueSnackBar],
|
[enqueueSnackBar],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onBack = useCallback(() => {
|
||||||
|
setState(previousState);
|
||||||
|
prevStep();
|
||||||
|
}, [prevStep, previousState]);
|
||||||
|
|
||||||
switch (state.type) {
|
switch (state.type) {
|
||||||
case StepType.upload:
|
case StepType.upload:
|
||||||
return (
|
return (
|
||||||
@ -138,6 +147,7 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
|
|||||||
} else {
|
} else {
|
||||||
setState({ type: StepType.selectSheet, workbook });
|
setState({ type: StepType.selectSheet, workbook });
|
||||||
}
|
}
|
||||||
|
setPreviousState(state);
|
||||||
nextStep();
|
nextStep();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -164,10 +174,12 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
|
|||||||
type: StepType.selectHeader,
|
type: StepType.selectHeader,
|
||||||
data: mappedWorkbook,
|
data: mappedWorkbook,
|
||||||
});
|
});
|
||||||
|
setPreviousState(state);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorToast((e as Error).message);
|
errorToast((e as Error).message);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onBack={onBack}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case StepType.selectHeader:
|
case StepType.selectHeader:
|
||||||
@ -184,11 +196,13 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
|
|||||||
data,
|
data,
|
||||||
headerValues,
|
headerValues,
|
||||||
});
|
});
|
||||||
|
setPreviousState(state);
|
||||||
nextStep();
|
nextStep();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorToast((e as Error).message);
|
errorToast((e as Error).message);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onBack={onBack}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case StepType.matchColumns:
|
case StepType.matchColumns:
|
||||||
@ -203,11 +217,13 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
|
|||||||
type: StepType.validateData,
|
type: StepType.validateData,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
setPreviousState(state);
|
||||||
nextStep();
|
nextStep();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorToast((e as Error).message);
|
errorToast((e as Error).message);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onBack={onBack}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case StepType.validateData:
|
case StepType.validateData:
|
||||||
@ -223,6 +239,10 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
|
|||||||
type: StepType.loading,
|
type: StepType.loading,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
onBack={() => {
|
||||||
|
onBack();
|
||||||
|
setPreviousState(initialStepState || { type: StepType.upload });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case StepType.loading:
|
case StepType.loading:
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import { RowsChangeData } from 'react-data-grid';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { IconTrash } from 'twenty-ui';
|
import { IconTrash } from 'twenty-ui';
|
||||||
|
|
||||||
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
|
|
||||||
import { Heading } from '@/spreadsheet-import/components/Heading';
|
import { Heading } from '@/spreadsheet-import/components/Heading';
|
||||||
|
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
|
||||||
import { Table } from '@/spreadsheet-import/components/Table';
|
import { Table } from '@/spreadsheet-import/components/Table';
|
||||||
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||||
import { Data } from '@/spreadsheet-import/types';
|
import { Data } from '@/spreadsheet-import/types';
|
||||||
@ -64,12 +64,14 @@ type ValidationStepProps<T extends string> = {
|
|||||||
initialData: Data<T>[];
|
initialData: Data<T>[];
|
||||||
file: File;
|
file: File;
|
||||||
onSubmitStart?: () => void;
|
onSubmitStart?: () => void;
|
||||||
|
onBack: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ValidationStep = <T extends string>({
|
export const ValidationStep = <T extends string>({
|
||||||
initialData,
|
initialData,
|
||||||
file,
|
file,
|
||||||
onSubmitStart,
|
onSubmitStart,
|
||||||
|
onBack,
|
||||||
}: ValidationStepProps<T>) => {
|
}: ValidationStepProps<T>) => {
|
||||||
const { enqueueDialog } = useDialogManager();
|
const { enqueueDialog } = useDialogManager();
|
||||||
const { fields, onClose, onSubmit, rowHook, tableHook } =
|
const { fields, onClose, onSubmit, rowHook, tableHook } =
|
||||||
@ -238,7 +240,8 @@ export const ValidationStep = <T extends string>({
|
|||||||
/>
|
/>
|
||||||
</StyledScrollContainer>
|
</StyledScrollContainer>
|
||||||
</StyledContent>
|
</StyledContent>
|
||||||
<ContinueButton onContinue={onContinue} title="Confirm" />
|
<StepNavigationButton onClick={onContinue} title="Confirm" />
|
||||||
|
<StepNavigationButton onClick={onBack} title="Back" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -67,6 +67,7 @@ export const Default = () => (
|
|||||||
headerValues={mockData[0] as string[]}
|
headerValues={mockData[0] as string[]}
|
||||||
data={mockData.slice(1)}
|
data={mockData.slice(1)}
|
||||||
onContinue={() => null}
|
onContinue={() => null}
|
||||||
|
onBack={() => null}
|
||||||
/>
|
/>
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
</Providers>
|
</Providers>
|
||||||
|
|||||||
@ -26,6 +26,7 @@ export const Default = () => (
|
|||||||
<SelectHeaderStep
|
<SelectHeaderStep
|
||||||
data={headerSelectionTableFields}
|
data={headerSelectionTableFields}
|
||||||
onContinue={() => Promise.resolve()}
|
onContinue={() => Promise.resolve()}
|
||||||
|
onBack={() => Promise.resolve()}
|
||||||
/>
|
/>
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
</Providers>
|
</Providers>
|
||||||
|
|||||||
@ -25,6 +25,7 @@ export const Default = () => (
|
|||||||
<SelectSheetStep
|
<SelectSheetStep
|
||||||
sheetNames={sheetNames}
|
sheetNames={sheetNames}
|
||||||
onContinue={() => Promise.resolve()}
|
onContinue={() => Promise.resolve()}
|
||||||
|
onBack={() => Promise.resolve()}
|
||||||
/>
|
/>
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
</Providers>
|
</Providers>
|
||||||
|
|||||||
@ -25,7 +25,11 @@ export const Default = () => (
|
|||||||
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
||||||
<Providers values={mockRsiValues}>
|
<Providers values={mockRsiValues}>
|
||||||
<ModalWrapper isOpen={true} onClose={() => null}>
|
<ModalWrapper isOpen={true} onClose={() => null}>
|
||||||
<ValidationStep initialData={editableTableInitialData} file={file} />
|
<ValidationStep
|
||||||
|
initialData={editableTableInitialData}
|
||||||
|
file={file}
|
||||||
|
onBack={() => Promise.resolve()}
|
||||||
|
/>
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
</Providers>
|
</Providers>
|
||||||
</DialogManagerScope>
|
</DialogManagerScope>
|
||||||
|
|||||||
Reference in New Issue
Block a user