Import - Increase record import limit (#12627)

<img width="700" alt="Screenshot 2025-06-16 at 15 05 09"
src="https://github.com/user-attachments/assets/a09c3fae-c0ae-4a63-8bda-9d29c97a6a66"
/>


closes https://github.com/twentyhq/twenty/issues/11980
This commit is contained in:
Etienne
2025-06-18 12:13:24 +02:00
committed by GitHub
parent 83f28f113a
commit 78d39294ef
26 changed files with 331 additions and 54 deletions
@@ -0,0 +1,64 @@
import { useRecoilValue } from 'recoil';
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
import { useHideStepBar } from '@/spreadsheet-import/hooks/useHideStepBar';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
import { spreadsheetImportCreatedRecordsProgressState } from '@/spreadsheet-import/states/spreadsheetImportCreatedRecordsProgressState';
import { Modal } from '@/ui/layout/modal/components/Modal';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { Loader } from 'twenty-ui/feedback';
import { formatNumber } from '~/utils/format/number';
const StyledContent = styled(Modal.Content)`
align-items: center;
display: flex;
justify-content: center;
padding: 0px;
`;
const StyledHeader = styled.span`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-bottom: ${({ theme }) => theme.spacing(2)};
`;
const StyledDescription = styled.span`
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.regular};
margin-bottom: ${({ theme }) => theme.spacing(5)};
`;
type ImportDataStepProps = {
recordsToImportCount: number;
};
export const ImportDataStep = ({
recordsToImportCount,
}: ImportDataStepProps) => {
const hideStepBar = useHideStepBar();
hideStepBar();
const { onClose } = useSpreadsheetImportInternal();
const spreadsheetImportCreatedRecordsProgress = useRecoilValue(
spreadsheetImportCreatedRecordsProgressState,
);
const formattedCreatedRecordsProgress = formatNumber(
spreadsheetImportCreatedRecordsProgress,
);
const formattedRecordsToImportCount = formatNumber(recordsToImportCount);
return (
<>
<StyledContent>
<StyledHeader>{t`Importing Data ...`}</StyledHeader>
<StyledDescription>{t`${formattedCreatedRecordsProgress} out of ${formattedRecordsToImportCount} records imported.`}</StyledDescription>
<Loader />
</StyledContent>
<StepNavigationButton onBack={onClose} backTitle={t`Cancel`} />
</>
);
};
@@ -279,14 +279,14 @@ export const MatchColumnsStep = <T extends string>({
</ScrollWrapper>
</StyledContent>
<StepNavigationButton
onClick={handleOnContinue}
onContinue={handleOnContinue}
isLoading={isLoading}
title={t`Next Step`}
continueTitle={t`Next Step`}
onBack={() => {
onBack?.();
setColumns([]);
}}
isNextDisabled={!hasMatchedColumns}
isContinueDisabled={!hasMatchedColumns}
/>
</>
);
@@ -114,9 +114,9 @@ export const SelectHeaderStep = ({
</StyledTableContainer>
</Modal.Content>
<StepNavigationButton
onClick={handleOnContinue}
onContinue={handleOnContinue}
onBack={onBack}
title={t`Continue`}
continueTitle={t`Continue`}
isLoading={isLoading}
/>
</>
@@ -11,8 +11,8 @@ import { mapWorkbook } from '@/spreadsheet-import/utils/mapWorkbook';
import { Modal } from '@/ui/layout/modal/components/Modal';
import { useLingui } from '@lingui/react/macro';
import { WorkBook } from 'xlsx-ugnis';
import { Radio, RadioGroup } from 'twenty-ui/input';
import { WorkBook } from 'xlsx-ugnis';
const StyledContent = styled(Modal.Content)`
align-items: center;
@@ -116,10 +116,10 @@ export const SelectSheetStep = ({
</StyledRadioContainer>
</StyledContent>
<StepNavigationButton
onClick={() => handleOnContinue(value)}
onContinue={() => handleOnContinue(value)}
onBack={onBack}
isLoading={isLoading}
title={t`Next Step`}
continueTitle={t`Next Step`}
/>
</>
);
@@ -7,14 +7,15 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { Modal } from '@/ui/layout/modal/components/Modal';
import { ImportDataStep } from '@/spreadsheet-import/steps/components/ImportDataStep';
import { SpreadsheetImportStep } from '@/spreadsheet-import/steps/types/SpreadsheetImportStep';
import { SpreadsheetImportStepType } from '@/spreadsheet-import/steps/types/SpreadsheetImportStepType';
import { CircularProgressBar } from 'twenty-ui/feedback';
import { MatchColumnsStep } from './MatchColumnsStep/MatchColumnsStep';
import { SelectHeaderStep } from './SelectHeaderStep/SelectHeaderStep';
import { SelectSheetStep } from './SelectSheetStep/SelectSheetStep';
import { UploadStep } from './UploadStep/UploadStep';
import { ValidationStep } from './ValidationStep/ValidationStep';
import { CircularProgressBar } from 'twenty-ui/feedback';
const StyledProgressBarContainer = styled(Modal.Content)`
align-items: center;
@@ -128,6 +129,12 @@ export const SpreadsheetImportStepper = ({
}}
/>
);
case SpreadsheetImportStepType.importData:
return (
<ImportDataStep
recordsToImportCount={currentStepState.recordsToImportCount}
/>
);
case SpreadsheetImportStepType.loading:
default:
return (
@@ -6,8 +6,10 @@ import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpre
import { StepBar } from '@/ui/navigation/step-bar/components/StepBar';
import { useStepBar } from '@/ui/navigation/step-bar/hooks/useStepBar';
import { spreadsheetImportDialogState } from '@/spreadsheet-import/states/spreadsheetImportDialogState';
import { Modal } from '@/ui/layout/modal/components/Modal';
import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { SpreadsheetImportStepper } from './SpreadsheetImportStepper';
@@ -26,6 +28,8 @@ const StyledHeader = styled(Modal.Header)`
export const SpreadsheetImportStepperContainer = () => {
const { t } = useLingui();
const spreadsheetImportDialog = useRecoilValue(spreadsheetImportDialogState);
const stepTitles = {
uploadStep: t`Upload File`,
matchColumnsStep: t`Match Columns`,
@@ -45,15 +49,17 @@ export const SpreadsheetImportStepperContainer = () => {
return (
<>
<StyledHeader>
<StepBar activeStep={activeStep}>
{steps.map((key) => (
<StepBar.Step
activeStep={activeStep}
label={stepTitles[key]}
key={key}
/>
))}
</StepBar>
{spreadsheetImportDialog.isStepBarVisible && (
<StepBar activeStep={activeStep}>
{steps.map((key) => (
<StepBar.Step
activeStep={activeStep}
label={stepTitles[key]}
key={key}
/>
))}
</StepBar>
)}
</StyledHeader>
<SpreadsheetImportStepper nextStep={nextStep} prevStep={prevStep} />
</>
@@ -11,6 +11,7 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { Trans, useLingui } from '@lingui/react/macro';
import { MainButton } from 'twenty-ui/input';
import { formatNumber } from '~/utils/format/number';
const StyledContainer = styled.div`
align-items: center;
@@ -154,6 +155,10 @@ export const DropZone = ({ onContinue, isLoading }: DropZoneProps) => {
const { t } = useLingui();
const formatSpreadsheetMaxRecordImportCapacity = formatNumber(
SpreadsheetMaxRecordImportCapacity,
);
return (
<StyledContainer
// eslint-disable-next-line react/jsx-props-no-spreading
@@ -179,7 +184,7 @@ export const DropZone = ({ onContinue, isLoading }: DropZoneProps) => {
</StyledText>
<MainButton onClick={open} title={t`Select file`} />
<StyledFooterText>
{t`Max import capacity: ${SpreadsheetMaxRecordImportCapacity} records. Otherwise, consider splitting your file or using the API.`}{' '}
{t`Max import capacity: ${formatSpreadsheetMaxRecordImportCapacity} records. Otherwise, consider splitting your file or using the API.`}{' '}
<StyledTextAction onClick={downloadSample}>
{t`Download sample file.`}
</StyledTextAction>
@@ -237,7 +237,8 @@ export const ValidationStep = <T extends string>({
);
setCurrentStepState({
type: SpreadsheetImportStepType.loading,
type: SpreadsheetImportStepType.importData,
recordsToImportCount: calculatedData.validStructuredRows.length,
});
await onSubmit(calculatedData, file);
@@ -321,9 +322,9 @@ export const ValidationStep = <T extends string>({
</StyledToolbar>
</StyledContent>
<StepNavigationButton
onClick={onContinue}
onContinue={onContinue}
onBack={onBack}
title={t`Confirm`}
continueTitle={t`Confirm`}
/>
</>
);
@@ -27,4 +27,8 @@ export type SpreadsheetImportStep =
}
| {
type: SpreadsheetImportStepType.loading;
}
| {
type: SpreadsheetImportStepType.importData;
recordsToImportCount: number;
};
@@ -4,5 +4,6 @@ export enum SpreadsheetImportStepType {
selectHeader = 'selectHeader',
matchColumns = 'matchColumns',
validateData = 'validateData',
importData = 'importData',
loading = 'loading',
}