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:
Shashank Vishwakarma
2024-05-30 22:13:56 +05:30
committed by GitHub
parent a12c1aad5e
commit c7f2150ac7
11 changed files with 60 additions and 21 deletions

View File

@ -16,22 +16,22 @@ const StyledButton = styled(MainButton)`
width: 200px;
`;
type ContinueButtonProps = {
onContinue: (val: any) => void;
type StepNavigationButtonProps = {
onClick: () => void;
title: string;
isLoading?: boolean;
};
export const ContinueButton = ({
onContinue,
export const StepNavigationButton = ({
onClick,
title,
isLoading,
}: ContinueButtonProps) => (
}: StepNavigationButtonProps) => (
<StyledFooter>
<StyledButton
Icon={isLoading ? CircularProgressBar : undefined}
title={title}
onClick={!isLoading ? onContinue : undefined}
onClick={!isLoading ? onClick : undefined}
/>
</StyledFooter>
);

View File

@ -1,8 +1,8 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import styled from '@emotion/styled';
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
import { Heading } from '@/spreadsheet-import/components/Heading';
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
import { Field, RawData } from '@/spreadsheet-import/types';
import { findUnmatchedRequiredFields } from '@/spreadsheet-import/utils/findUnmatchedRequiredFields';
@ -49,6 +49,7 @@ export type MatchColumnsStepProps<T extends string> = {
data: RawData[];
headerValues: RawData;
onContinue: (data: any[], rawData: RawData[], columns: Columns<T>) => void;
onBack: () => void;
};
export enum ColumnType {
@ -112,6 +113,7 @@ export const MatchColumnsStep = <T extends string>({
data,
headerValues,
onContinue,
onBack,
}: MatchColumnsStepProps<T>) => {
const { enqueueDialog } = useDialogManager();
const { enqueueSnackBar } = useSnackBar();
@ -284,11 +286,12 @@ export const MatchColumnsStep = <T extends string>({
)}
/>
</StyledContent>
<ContinueButton
<StepNavigationButton
onClick={handleOnContinue}
isLoading={isLoading}
onContinue={handleOnContinue}
title="Next"
/>
<StepNavigationButton onClick={onBack} title="Back" />
</>
);
};

View File

@ -1,8 +1,8 @@
import { useCallback, useState } from 'react';
import styled from '@emotion/styled';
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
import { Heading } from '@/spreadsheet-import/components/Heading';
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
import { RawData } from '@/spreadsheet-import/types';
import { Modal } from '@/ui/layout/modal/components/Modal';
@ -21,11 +21,13 @@ const StyledTableContainer = styled.div`
type SelectHeaderStepProps = {
data: RawData[];
onContinue: (headerValues: RawData, data: RawData[]) => Promise<void>;
onBack: () => void;
};
export const SelectHeaderStep = ({
data,
onContinue,
onBack,
}: SelectHeaderStepProps) => {
const [selectedRows, setSelectedRows] = useState<ReadonlySet<number>>(
new Set([0]),
@ -53,11 +55,12 @@ export const SelectHeaderStep = ({
/>
</StyledTableContainer>
</Modal.Content>
<ContinueButton
onContinue={handleContinue}
<StepNavigationButton
onClick={handleContinue}
title="Next"
isLoading={isLoading}
/>
<StepNavigationButton onClick={onBack} title="Back" />
</>
);
};

View File

@ -1,8 +1,8 @@
import { useCallback, useState } from 'react';
import styled from '@emotion/styled';
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
import { Heading } from '@/spreadsheet-import/components/Heading';
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
import { Radio } from '@/ui/input/components/Radio';
import { RadioGroup } from '@/ui/input/components/RadioGroup';
import { Modal } from '@/ui/layout/modal/components/Modal';
@ -27,11 +27,13 @@ const StyledRadioContainer = styled.div`
type SelectSheetStepProps = {
sheetNames: string[];
onContinue: (sheetName: string) => Promise<void>;
onBack: () => void;
};
export const SelectSheetStep = ({
sheetNames,
onContinue,
onBack,
}: SelectSheetStepProps) => {
const [isLoading, setIsLoading] = useState(false);
@ -58,11 +60,12 @@ export const SelectSheetStep = ({
</RadioGroup>
</StyledRadioContainer>
</StyledContent>
<ContinueButton
<StepNavigationButton
onClick={() => handleOnContinue(value)}
isLoading={isLoading}
onContinue={() => handleOnContinue(value)}
title="Next"
/>
<StepNavigationButton onClick={onBack} title="Back" />
</>
);
};

View File

@ -35,7 +35,7 @@ export const Steps = () => {
initialStepState?.type,
);
const { nextStep, activeStep } = useStepBar({
const { nextStep, prevStep, activeStep } = useStepBar({
initialStep,
});
@ -48,7 +48,7 @@ export const Steps = () => {
))}
</StepBar>
</StyledHeader>
<UploadFlow nextStep={nextStep} />
<UploadFlow nextStep={nextStep} prevStep={prevStep} />
</>
);
};

View File

@ -59,14 +59,18 @@ export type StepState =
interface UploadFlowProps {
nextStep: () => void;
prevStep: () => void;
}
export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
export const UploadFlow = ({ nextStep, prevStep }: UploadFlowProps) => {
const theme = useTheme();
const { initialStepState } = useSpreadsheetImportInternal();
const [state, setState] = useState<StepState>(
initialStepState || { type: StepType.upload },
);
const [previousState, setPreviousState] = useState<StepState>(
initialStepState || { type: StepType.upload },
);
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
const {
maxRecords,
@ -87,6 +91,11 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
[enqueueSnackBar],
);
const onBack = useCallback(() => {
setState(previousState);
prevStep();
}, [prevStep, previousState]);
switch (state.type) {
case StepType.upload:
return (
@ -138,6 +147,7 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
} else {
setState({ type: StepType.selectSheet, workbook });
}
setPreviousState(state);
nextStep();
}}
/>
@ -164,10 +174,12 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
type: StepType.selectHeader,
data: mappedWorkbook,
});
setPreviousState(state);
} catch (e) {
errorToast((e as Error).message);
}
}}
onBack={onBack}
/>
);
case StepType.selectHeader:
@ -184,11 +196,13 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
data,
headerValues,
});
setPreviousState(state);
nextStep();
} catch (e) {
errorToast((e as Error).message);
}
}}
onBack={onBack}
/>
);
case StepType.matchColumns:
@ -203,11 +217,13 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
type: StepType.validateData,
data,
});
setPreviousState(state);
nextStep();
} catch (e) {
errorToast((e as Error).message);
}
}}
onBack={onBack}
/>
);
case StepType.validateData:
@ -223,6 +239,10 @@ export const UploadFlow = ({ nextStep }: UploadFlowProps) => {
type: StepType.loading,
})
}
onBack={() => {
onBack();
setPreviousState(initialStepState || { type: StepType.upload });
}}
/>
);
case StepType.loading:

View File

@ -4,8 +4,8 @@ import { RowsChangeData } from 'react-data-grid';
import styled from '@emotion/styled';
import { IconTrash } from 'twenty-ui';
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
import { Heading } from '@/spreadsheet-import/components/Heading';
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
import { Table } from '@/spreadsheet-import/components/Table';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
import { Data } from '@/spreadsheet-import/types';
@ -64,12 +64,14 @@ type ValidationStepProps<T extends string> = {
initialData: Data<T>[];
file: File;
onSubmitStart?: () => void;
onBack: () => void;
};
export const ValidationStep = <T extends string>({
initialData,
file,
onSubmitStart,
onBack,
}: ValidationStepProps<T>) => {
const { enqueueDialog } = useDialogManager();
const { fields, onClose, onSubmit, rowHook, tableHook } =
@ -238,7 +240,8 @@ export const ValidationStep = <T extends string>({
/>
</StyledScrollContainer>
</StyledContent>
<ContinueButton onContinue={onContinue} title="Confirm" />
<StepNavigationButton onClick={onContinue} title="Confirm" />
<StepNavigationButton onClick={onBack} title="Back" />
</>
);
};

View File

@ -67,6 +67,7 @@ export const Default = () => (
headerValues={mockData[0] as string[]}
data={mockData.slice(1)}
onContinue={() => null}
onBack={() => null}
/>
</ModalWrapper>
</Providers>

View File

@ -26,6 +26,7 @@ export const Default = () => (
<SelectHeaderStep
data={headerSelectionTableFields}
onContinue={() => Promise.resolve()}
onBack={() => Promise.resolve()}
/>
</ModalWrapper>
</Providers>

View File

@ -25,6 +25,7 @@ export const Default = () => (
<SelectSheetStep
sheetNames={sheetNames}
onContinue={() => Promise.resolve()}
onBack={() => Promise.resolve()}
/>
</ModalWrapper>
</Providers>

View File

@ -25,7 +25,11 @@ export const Default = () => (
<DialogManagerScope dialogManagerScopeId="dialog-manager">
<Providers values={mockRsiValues}>
<ModalWrapper isOpen={true} onClose={() => null}>
<ValidationStep initialData={editableTableInitialData} file={file} />
<ValidationStep
initialData={editableTableInitialData}
file={file}
onBack={() => Promise.resolve()}
/>
</ModalWrapper>
</Providers>
</DialogManagerScope>