Skip to content

Commit

Permalink
feat sheet select modal (#512)
Browse files Browse the repository at this point in the history
closes #506
  • Loading branch information
chavda-bhavik authored Feb 27, 2024
2 parents 72f36a0 + 45c9fb0 commit 296d1f7
Show file tree
Hide file tree
Showing 29 changed files with 286 additions and 72 deletions.
30 changes: 27 additions & 3 deletions apps/api/src/app/common/common.controller.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import { Body, Controller, Post, Get, UseGuards, Query, BadRequestException } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiSecurity, ApiExcludeEndpoint } from '@nestjs/swagger';
import { FileInterceptor } from '@nestjs/platform-express';
import { ApiTags, ApiOperation, ApiSecurity, ApiExcludeEndpoint, ApiConsumes } from '@nestjs/swagger';
import {
Body,
Controller,
Post,
Get,
UseGuards,
Query,
BadRequestException,
UseInterceptors,
UploadedFile,
} from '@nestjs/common';

import { ACCESS_KEY_NAME } from '@impler/shared';
import { JwtAuthGuard } from '@shared/framework/auth.gaurd';
import { ValidRequestDto, SignedUrlDto, ImportConfigResponseDto } from './dtos';
import { ValidRequestCommand, GetSignedUrl, ValidRequest, GetImportConfig } from './usecases';
import { ValidImportFile } from '@shared/validations/valid-import-file.validation';
import { ValidRequestCommand, GetSignedUrl, ValidRequest, GetImportConfig, GetSheetNames } from './usecases';

@ApiTags('Common')
@Controller('/common')
Expand All @@ -13,6 +26,7 @@ export class CommonController {
constructor(
private validRequest: ValidRequest,
private getSignedUrl: GetSignedUrl,
private getSheetNames: GetSheetNames,
private getImportConfig: GetImportConfig
) {}

Expand Down Expand Up @@ -50,4 +64,14 @@ export class CommonController {

return this.getImportConfig.execute(projectId);
}

@Post('/sheet-names')
@ApiOperation({
summary: 'Get sheet names for user selected file',
})
@ApiConsumes('multipart/form-data')
@UseInterceptors(FileInterceptor('file'))
async getSheetNamesRoute(@UploadedFile('file', ValidImportFile) file: Express.Multer.File): Promise<string[]> {
return this.getSheetNames.execute({ file });
}
}
1 change: 1 addition & 0 deletions apps/api/src/app/common/dtos/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { SignedUrlDto } from './signed-url.dto';
export { ValidRequestDto } from './valid.dto';
export { SheetNamesDto } from './sheet-names.dto';
export { ImportConfigResponseDto } from './import-config-response.dto';
9 changes: 9 additions & 0 deletions apps/api/src/app/common/dtos/sheet-names.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ApiProperty } from '@nestjs/swagger';

export class SheetNamesDto {
@ApiProperty({
type: 'file',
required: true,
})
file: Express.Multer.File;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IsDefined } from 'class-validator';
import { BaseCommand } from '@shared/commands/base.command';

export class GetSheetNamesCommand extends BaseCommand {
@IsDefined()
file: Express.Multer.File;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Injectable } from '@nestjs/common';
import { FileMimeTypesEnum } from '@impler/shared';

import { CONSTANTS } from '@shared/constants';
import { ExcelFileService } from '@shared/services/file';
import { GetSheetNamesCommand } from './get-sheet-names.command';
import { FileParseException } from '@shared/exceptions/file-parse-issue.exception';

@Injectable()
export class GetSheetNames {
async execute({ file }: GetSheetNamesCommand): Promise<string[]> {
if (file.mimetype === FileMimeTypesEnum.EXCEL || file.mimetype === FileMimeTypesEnum.EXCELX) {
try {
const fileService = new ExcelFileService();
const sheetNames = await fileService.getExcelSheets(file);

return sheetNames.filter((sheetName) => !sheetName.startsWith(CONSTANTS.EXCEL_DATA_SHEET_STARTER));
} catch (error) {
throw new FileParseException();
}
} else {
throw new Error('Invalid file type');
}
}
}
10 changes: 7 additions & 3 deletions apps/api/src/app/common/usecases/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { GetImportConfig } from './get-import-config/get-import-config.usecase';
import { ValidRequest } from './valid-request/valid-request.usecase';
import { GetSignedUrl } from './get-signed-url/get-signed-url.usecase';
import { GetSheetNames } from './get-sheet-names/get-sheet-names.usecase';
import { GetImportConfig } from './get-import-config/get-import-config.usecase';

import { ValidRequestCommand } from './valid-request/valid-request.command';
import { GetSheetNamesCommand } from './get-sheet-names/get-sheet-names.command';

export const USE_CASES = [
ValidRequest,
GetSignedUrl,
GetSheetNames,
GetImportConfig,
//
];

export { GetSignedUrl, ValidRequest, GetImportConfig };
export { ValidRequestCommand };
export { GetSignedUrl, ValidRequest, GetImportConfig, GetSheetNames };
export { ValidRequestCommand, GetSheetNamesCommand };
1 change: 1 addition & 0 deletions apps/api/src/app/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const APIMessages = {

export const CONSTANTS = {
PASSWORD_SALT: 10,
EXCEL_DATA_SHEET_STARTER: 'imp_',
AUTH_COOKIE_NAME: 'authentication',
// eslint-disable-next-line no-magic-numbers
maxAge: 1000 * 60 * 60 * 24 * 1, // 1 day
Expand Down
19 changes: 15 additions & 4 deletions apps/api/src/app/shared/services/file/file.service.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import * as XLSX from 'xlsx';
import * as ExcelJS from 'exceljs';
import { ParseConfig, parse } from 'papaparse';
import { CONSTANTS } from '@shared/constants';
import { ColumnTypesEnum, Defaults, FileEncodingsEnum } from '@impler/shared';
import { EmptyFileException } from '@shared/exceptions/empty-file.exception';
import { InvalidFileException } from '@shared/exceptions/invalid-file.exception';
import { IExcelFileHeading } from '@shared/types/file.types';

export class ExcelFileService {
async convertToCsv(file: Express.Multer.File): Promise<string> {
async convertToCsv(file: Express.Multer.File, sheetName?: string): Promise<string> {
return new Promise(async (resolve, reject) => {
try {
const wb = XLSX.read(file.buffer);
const ws = wb.Sheets[wb.SheetNames[0]];
const ws = sheetName && wb.SheetNames.includes(sheetName) ? wb.Sheets[sheetName] : wb.Sheets[wb.SheetNames[0]];
resolve(
XLSX.utils.sheet_to_csv(ws, {
blankrows: false,
Expand All @@ -25,12 +26,12 @@ export class ExcelFileService {
});
}
formatName(name: string): string {
return name.replace(/[^a-zA-Z0-9]/g, '');
return CONSTANTS.EXCEL_DATA_SHEET_STARTER + name.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
}
addSelectSheet(wb: ExcelJS.Workbook, heading: IExcelFileHeading): string {
const name = this.formatName(heading.key);
const companies = wb.addWorksheet(name);
const companyHeadings = [name];
const companyHeadings = [heading.key];
companies.addRow(companyHeadings);
heading.selectValues.forEach((value) => companies.addRow([value]));

Expand Down Expand Up @@ -120,6 +121,16 @@ export class ExcelFileService {

return workbook.xlsx.writeBuffer() as Promise<Buffer>;
}
getExcelSheets(file: Express.Multer.File): Promise<string[]> {
return new Promise(async (resolve, reject) => {
try {
const wb = XLSX.read(file.buffer);
resolve(wb.SheetNames);
} catch (error) {
reject(error);
}
});
}
}

export class CSVFileService2 {
Expand Down
7 changes: 7 additions & 0 deletions apps/api/src/app/upload/dtos/upload-request.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,11 @@ export class UploadRequestDto {
@IsOptional()
@IsJSON()
extra: string;

@ApiProperty({
description: 'Name of the excel sheet to Import',
})
@IsOptional()
@IsString()
selectedSheetName: string;
}
9 changes: 5 additions & 4 deletions apps/api/src/app/upload/upload.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ import {
GetOriginalFileContent,
} from './usecases';

@Controller('/upload')
@ApiTags('Uploads')
@Controller('/upload')
@ApiSecurity(ACCESS_KEY_NAME)
@UseGuards(JwtAuthGuard)
export class UploadController {
Expand All @@ -68,18 +68,19 @@ export class UploadController {
@ApiConsumes('multipart/form-data')
@UseInterceptors(FileInterceptor('file'))
async uploadFile(
@UploadedFile('file', ValidImportFile) file: Express.Multer.File,
@Body() body: UploadRequestDto,
@Param('templateId', ValidateTemplate) templateId: string
@Param('templateId', ValidateTemplate) templateId: string,
@UploadedFile('file', ValidImportFile) file: Express.Multer.File
) {
return this.makeUploadEntry.execute(
MakeUploadEntryCommand.create({
file: file,
templateId,
extra: body.extra,
authHeaderValue: body.authHeaderValue,
schema: body.schema,
output: body.output,
authHeaderValue: body.authHeaderValue,
selectedSheetName: body.selectedSheetName,
})
);
}
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/app/upload/usecases/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export const USE_CASES = [
];

export {
MakeUploadEntry,
GetUpload,
MakeUploadEntry,
TerminateUpload,
GetUploadColumns,
GetOriginalFileContent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ export class MakeUploadEntryCommand extends BaseCommand {
@IsOptional()
@IsString()
authHeaderValue?: string;

@IsString()
@IsOptional()
selectedSheetName?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,21 @@ export class MakeUploadEntry {
private customizationRepository: CustomizationRepository
) {}

async execute({ file, templateId, extra, authHeaderValue, schema, output }: MakeUploadEntryCommand) {
async execute({
file,
templateId,
extra,
authHeaderValue,
schema,
output,
selectedSheetName,
}: MakeUploadEntryCommand) {
const fileOriginalName = file.originalname;
let csvFile: string | Express.Multer.File = file;
if (file.mimetype === FileMimeTypesEnum.EXCEL || file.mimetype === FileMimeTypesEnum.EXCELX) {
try {
const fileService = new ExcelFileService();
csvFile = await fileService.convertToCsv(file);
csvFile = await fileService.convertToCsv(file, selectedSheetName);
} catch (error) {
throw new FileParseException();
}
Expand Down
30 changes: 0 additions & 30 deletions apps/widget/src/components/widget/Phases/ConfirmModal/Styles.tsx

This file was deleted.

28 changes: 21 additions & 7 deletions apps/widget/src/components/widget/Phases/Phase1/Phase1.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { TEXTS, variables } from '@config';
import { Group } from '@mantine/core';
import { Controller } from 'react-hook-form';

import { Download } from '@icons';
import { PhasesEnum } from '@types';
import { Select } from '@ui/Select';
import { Button } from '@ui/Button';
import { Dropzone } from '@ui/Dropzone';
import { TEXTS, variables } from '@config';
import { LoadingOverlay } from '@ui/LoadingOverlay';
import { Group } from '@mantine/core';
import { Download } from '@icons';
import { usePhase1 } from '@hooks/Phase1/usePhase1';

import useStyles from './Styles';
import { Footer } from 'components/Common/Footer';
import { usePhase1 } from '@hooks/Phase1/usePhase1';
import { Controller } from 'react-hook-form';
import { PhasesEnum } from '@types';
import { SheetSelectModal } from './SheetSelectModal';

interface IPhase1Props {
onNextClick: () => void;
Expand All @@ -21,14 +24,17 @@ export function Phase1(props: IPhase1Props) {
const {
onSubmit,
control,
setError,
templates,
onDownload,
setError,
excelSheetNames,
isUploadLoading,
onTemplateChange,
onSelectExcelSheet,
showSelectTemplate,
isInitialDataLoaded,
isDownloadInProgress,
onSelectSheetModalReset,
} = usePhase1({
goNext,
});
Expand Down Expand Up @@ -93,6 +99,14 @@ export function Phase1(props: IPhase1Props) {
)}
/>

<SheetSelectModal
control={control}
onSubmit={onSelectExcelSheet}
excelSheetNames={excelSheetNames}
opened={!!excelSheetNames.length}
onClose={onSelectSheetModalReset}
/>

<Footer
primaryButtonLoading={isUploadLoading}
onNextClick={onSubmit}
Expand Down
Loading

0 comments on commit 296d1f7

Please sign in to comment.