From 7bf5c3d3b1d33a1f6f7394d2b829680e72d99a1d Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 17:40:42 +0530 Subject: [PATCH 01/12] feat: Provided file in the showWidget --- packages/react/src/hooks/useImpler.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react/src/hooks/useImpler.ts b/packages/react/src/hooks/useImpler.ts index b36133e0..3d7d2ce0 100644 --- a/packages/react/src/hooks/useImpler.ts +++ b/packages/react/src/hooks/useImpler.ts @@ -79,9 +79,10 @@ export function useImpler({ const showWidget = async ({ colorScheme, data, + file, schema, output, - }: Pick = {}) => { + }: Pick = {}) => { if (window.impler && isImplerInitiated) { const payload: IShowWidgetProps & { uuid: string; host: string } = { uuid, @@ -91,6 +92,7 @@ export function useImpler({ accessToken, schema, data, + file, output, title, extra, From c99af85d4167b1b3fc40a277b041812ea846b5ff Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 17:41:44 +0530 Subject: [PATCH 02/12] feat: Provided file in the secondaryPayload --- apps/widget/src/components/Common/Container/Container.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/widget/src/components/Common/Container/Container.tsx b/apps/widget/src/components/Common/Container/Container.tsx index 0e9e4651..f89d9673 100644 --- a/apps/widget/src/components/Common/Container/Container.tsx +++ b/apps/widget/src/components/Common/Container/Container.tsx @@ -51,6 +51,7 @@ export function Container({ children }: PropsWithChildren<{}>) { setSecondaryPayload({ accessToken: data.value.accessToken, host: data.value.host, + file: data.value.file, projectId: data.value.projectId, uuid: data.value.uuid, extra: isObject(data.value.extra) ? JSON.stringify(data.value.extra) : data.value.extra, @@ -339,6 +340,7 @@ export function Container({ children }: PropsWithChildren<{}>) { authHeaderValue={secondaryPayload?.authHeaderValue} // eslint-disable-next-line @typescript-eslint/no-non-null-assertion primaryColor={secondaryPayload.primaryColor!} + file={secondaryPayload?.file} > {children} From 1735fbbd28b19d3e0d3c51ac934a4b94ed9af67e Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 17:43:37 +0530 Subject: [PATCH 03/12] feat: Provided file type in the IProviderProps --- apps/widget/src/components/Common/Provider/Provider.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/widget/src/components/Common/Provider/Provider.tsx b/apps/widget/src/components/Common/Provider/Provider.tsx index cf1bfa55..930a6f81 100644 --- a/apps/widget/src/components/Common/Provider/Provider.tsx +++ b/apps/widget/src/components/Common/Provider/Provider.tsx @@ -12,6 +12,7 @@ interface IProviderProps { texts: typeof WIDGET_TEXTS; primaryColor: string; output?: string; + file?: File | Blob; schema?: string; data?: string; host: string; @@ -31,6 +32,7 @@ export function Provider(props: PropsWithChildren) { api, data, title, + file, texts, output, projectId, @@ -58,6 +60,7 @@ export function Provider(props: PropsWithChildren) { host={host} data={data} title={title} + file={file} texts={texts} output={output} schema={schema} From 5bb8f5b1e78b84612998905fefa599f5c46c8555 Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 17:56:43 +0530 Subject: [PATCH 04/12] feat: Added file and its type as File or Blob --- packages/client/src/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/client/src/types.ts b/packages/client/src/types.ts index 6d9e5346..6af5e5b4 100644 --- a/packages/client/src/types.ts +++ b/packages/client/src/types.ts @@ -163,6 +163,7 @@ export interface IShowWidgetProps { projectId: string; templateId: string; accessToken: string; + file?: File | Blob; texts?: CustomTexts; title?: string; primaryColor?: string; From 650baef09d0ba5e6adf8e1ac01adb9e6bc20b6a1 Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 17:57:26 +0530 Subject: [PATCH 05/12] feat: HAndled error and the fileError --- apps/widget/src/components/widget/Phases/Phase0/Phase0.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/widget/src/components/widget/Phases/Phase0/Phase0.tsx b/apps/widget/src/components/widget/Phases/Phase0/Phase0.tsx index 96fdfa09..f75893c3 100644 --- a/apps/widget/src/components/widget/Phases/Phase0/Phase0.tsx +++ b/apps/widget/src/components/widget/Phases/Phase0/Phase0.tsx @@ -8,7 +8,7 @@ interface IPhase0Props { } export function Phase0({ onValidationSuccess: goNext }: IPhase0Props) { - const { isLoading, error, isWidgetOpened, handleValidate } = usePhase0({ + const { isLoading, error, fileError, isWidgetOpened, handleValidate } = usePhase0({ goNext, }); @@ -20,7 +20,7 @@ export function Phase0({ onValidationSuccess: goNext }: IPhase0Props) { if (isLoading) return ; - if (error) + if (error || fileError) { return ( - {error.message} + {error?.message || fileError} ); + } return null; } From 82314d9afe35be350eacc8ed68654bb857b81cad Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 17:58:06 +0530 Subject: [PATCH 06/12] feat: Added allowedTypes for handling invaid file --- apps/widget/src/hooks/Phase0/usePhase0.ts | 25 ++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/apps/widget/src/hooks/Phase0/usePhase0.ts b/apps/widget/src/hooks/Phase0/usePhase0.ts index f3197911..1008cafd 100644 --- a/apps/widget/src/hooks/Phase0/usePhase0.ts +++ b/apps/widget/src/hooks/Phase0/usePhase0.ts @@ -1,11 +1,12 @@ import { useMutation } from '@tanstack/react-query'; - +import { useState } from 'react'; import { FlowsEnum } from '@types'; import { useAPIState } from '@store/api.context'; import { useAppState } from '@store/app.context'; import { identifyImportIntent } from '@amplitude'; import { useImplerState } from '@store/impler.context'; -import { IErrorObject, IImportConfig, TemplateModeEnum } from '@impler/shared'; +import { FileMimeTypesEnum, IErrorObject, IImportConfig, TemplateModeEnum } from '@impler/shared'; +import { isValidFileType } from 'util/helpers/common.helpers'; interface IUsePhase0Props { goNext: () => void; @@ -14,7 +15,8 @@ interface IUsePhase0Props { export function usePhase0({ goNext }: IUsePhase0Props) { const { api } = useAPIState(); const { projectId, templateId } = useImplerState(); - const { schema, setImportConfig, showWidget, setFlow } = useAppState(); + const [fileError, setFileError] = useState(null); + const { schema, setImportConfig, showWidget, setFlow, file } = useAppState(); const { mutate: fetchImportConfig } = useMutation( ['importConfig', projectId, templateId], @@ -25,10 +27,22 @@ export function usePhase0({ goNext }: IUsePhase0Props) { importConfigData.mode === TemplateModeEnum.AUTOMATIC ? FlowsEnum.AUTO_IMPORT : FlowsEnum.STRAIGHT_IMPORT ); setImportConfig(importConfigData); - goNext(); + const allowedTypes = [ + FileMimeTypesEnum.CSV, + FileMimeTypesEnum.EXCEL, + FileMimeTypesEnum.EXCELM, + FileMimeTypesEnum.EXCELX, + ]; + + if (file && !isValidFileType(file as Blob)) { + setFileError(`Only ${allowedTypes.join(',')} are supported`); + } else { + goNext(); + } }, } ); + const { error, isLoading, @@ -47,11 +61,12 @@ export function usePhase0({ goNext }: IUsePhase0Props) { ); const handleValidate = async () => { - return checkIsRequestvalid({ projectId, templateId, schema: schema }); + return checkIsRequestvalid({ projectId, templateId, schema }); }; return { error, + fileError, isLoading, handleValidate, isWidgetOpened: showWidget, From d6f89b21c7a0d2a9170e0390f2fa7b393bfb0050 Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 17:59:43 +0530 Subject: [PATCH 07/12] feat: Added download of user provided sample file --- apps/widget/src/hooks/Phase1/usePhase1.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/widget/src/hooks/Phase1/usePhase1.ts b/apps/widget/src/hooks/Phase1/usePhase1.ts index 004cea06..99535ab1 100644 --- a/apps/widget/src/hooks/Phase1/usePhase1.ts +++ b/apps/widget/src/hooks/Phase1/usePhase1.ts @@ -8,7 +8,7 @@ import { useAPIState } from '@store/api.context'; import { useAppState } from '@store/app.context'; import { useImplerState } from '@store/impler.context'; import { IUpload, WIDGET_TEXTS } from '@impler/client'; -import { IErrorObject, ITemplate, FileMimeTypesEnum, IColumn } from '@impler/shared'; +import { IErrorObject, ITemplate, FileMimeTypesEnum, IColumn, downloadFile } from '@impler/shared'; import { variables } from '@config'; import { useSample } from '@hooks/useSample'; @@ -40,7 +40,7 @@ export function usePhase1({ goNext, texts, onManuallyEnterData }: IUsePhase1Prop const { templateId, authHeaderValue, extra } = useImplerState(); const [excelSheetNames, setExcelSheetNames] = useState([]); const [isDownloadInProgress, setIsDownloadInProgress] = useState(false); - const { setUploadInfo, setTemplateInfo, output, schema, data, importId, imageSchema } = useAppState(); + const { setUploadInfo, setTemplateInfo, output, schema, data, importId, imageSchema, file } = useAppState(); const selectedTemplateId = watch('templateId'); @@ -73,7 +73,8 @@ export function usePhase1({ goNext, texts, onManuallyEnterData }: IUsePhase1Prop string[], IErrorObject, { file: File } - >(['getExcelSheetNames'], (file) => api.getExcelSheetNames(file), { + // eslint-disable-next-line prettier/prettier + >(['getExcelSheetNames'], (excelSheetFile) => api.getExcelSheetNames(excelSheetFile), { onSuccess(sheetNames) { if (sheetNames.length <= 1) { setValue('selectedSheetName', sheetNames[0]); @@ -107,6 +108,12 @@ export function usePhase1({ goNext, texts, onManuallyEnterData }: IUsePhase1Prop } }; const onDownloadClick = async () => { + if (file) { + const fileName = (file as File).name; + const fileBaseName = fileName.split('.')[0]; + downloadFile(file as Blob, fileBaseName); + } + setIsDownloadInProgress(true); const isTemplateValid = await trigger('templateId'); if (!isTemplateValid) { @@ -148,9 +155,12 @@ export function usePhase1({ goNext, texts, onManuallyEnterData }: IUsePhase1Prop }); } }; - const onSubmit = async (file?: File) => { - if (file && [FileMimeTypesEnum.EXCEL, FileMimeTypesEnum.EXCELX].includes(file.type as FileMimeTypesEnum)) { - getExcelSheetNames({ file: file }); + const onSubmit = async (uploadedFile?: File) => { + if ( + uploadedFile && + [FileMimeTypesEnum.EXCEL, FileMimeTypesEnum.EXCELX].includes(uploadedFile.type as FileMimeTypesEnum) + ) { + getExcelSheetNames({ file: uploadedFile }); } else { handleSubmit(uploadFile)(); } From dbf57be1bf248f5be05ee1591d67806ec0b0cd6e Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 18:01:11 +0530 Subject: [PATCH 08/12] feat: Added file and type to it in the ICommonShowPayload interface --- libs/shared/src/types/widget/widget.types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/shared/src/types/widget/widget.types.ts b/libs/shared/src/types/widget/widget.types.ts index 5fc9f3ab..eddcfbfc 100644 --- a/libs/shared/src/types/widget/widget.types.ts +++ b/libs/shared/src/types/widget/widget.types.ts @@ -9,6 +9,7 @@ export interface ICommonShowPayload { primaryColor?: string; colorScheme?: string; title?: string; + file?: File | Blob; projectId: string; accessToken: string; uuid: string; From 46c5416cadc513143c14c82e6171b48e46b05fdd Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 18:01:36 +0530 Subject: [PATCH 09/12] feat: Added file in the context --- apps/widget/src/store/app.context.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/widget/src/store/app.context.tsx b/apps/widget/src/store/app.context.tsx index 2d1fe4f7..8847d118 100644 --- a/apps/widget/src/store/app.context.tsx +++ b/apps/widget/src/store/app.context.tsx @@ -30,6 +30,7 @@ const AppContextProvider = ({ title, texts, data, + file, schema, output, host, @@ -66,6 +67,7 @@ const AppContextProvider = ({ uploadInfo, setImportId, imageSchema, + file, templateInfo, importConfig, primaryColor, From bc4eefa0c3363514aedb68ccb3ecadd3cbcb98f6 Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 18:02:23 +0530 Subject: [PATCH 10/12] feat: Added file and its type as File or Blob in the IAppStore interface --- apps/widget/src/types/store.types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/widget/src/types/store.types.ts b/apps/widget/src/types/store.types.ts index c3acf6a5..e9cf42ad 100644 --- a/apps/widget/src/types/store.types.ts +++ b/apps/widget/src/types/store.types.ts @@ -40,6 +40,7 @@ export interface IAppStore { schema?: string; output?: string; flow: FlowsEnum; + file?: File | Blob; showWidget: boolean; importConfig: IImportConfig; setFlow: (flow: FlowsEnum) => void; From a9fc92c6af4c17ff3f275a44f5f18742152e4406 Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 15 Nov 2024 18:02:53 +0530 Subject: [PATCH 11/12] feat: Added function to validate the provided file --- apps/widget/src/util/helpers/common.helpers.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/widget/src/util/helpers/common.helpers.ts b/apps/widget/src/util/helpers/common.helpers.ts index 8ccc924a..0c4c0fe9 100644 --- a/apps/widget/src/util/helpers/common.helpers.ts +++ b/apps/widget/src/util/helpers/common.helpers.ts @@ -5,7 +5,7 @@ import 'tippy.js/dist/tippy.css'; import 'tippy.js/animations/shift-away.css'; import { variables } from '@config'; import { WIDGET_TEXTS, isObject } from '@impler/client'; -import { convertStringToJson, downloadFile } from '@impler/shared'; +import { convertStringToJson, downloadFile, FileMimeTypesEnum } from '@impler/shared'; // eslint-disable-next-line no-magic-numbers export function formatBytes(bytes, decimals = 2) { @@ -151,3 +151,15 @@ export function memoize any>(fn: T): T { return result; } as T; } + +export const isValidFileType = (sampleFile: Blob): boolean => { + if ( + sampleFile instanceof Blob && + [FileMimeTypesEnum.CSV, FileMimeTypesEnum.EXCEL, FileMimeTypesEnum.EXCELM, FileMimeTypesEnum.EXCELX].includes( + sampleFile.type as FileMimeTypesEnum + ) + ) + return true; + + return false; +}; From 72976bd290d1e3d1d4d6b1c8eca477627161c184 Mon Sep 17 00:00:00 2001 From: Mayur Date: Sat, 16 Nov 2024 14:30:40 +0530 Subject: [PATCH 12/12] feat: Renamed file to sampleFile --- apps/widget/src/components/Common/Container/Container.tsx | 4 ++-- apps/widget/src/components/Common/Provider/Provider.tsx | 6 +++--- apps/widget/src/hooks/Phase0/usePhase0.ts | 4 ++-- apps/widget/src/hooks/Phase1/usePhase1.ts | 8 ++++---- apps/widget/src/store/app.context.tsx | 4 ++-- apps/widget/src/types/store.types.ts | 2 +- libs/shared/src/types/widget/widget.types.ts | 2 +- packages/client/src/types.ts | 2 +- packages/react/src/hooks/useImpler.ts | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apps/widget/src/components/Common/Container/Container.tsx b/apps/widget/src/components/Common/Container/Container.tsx index f89d9673..fb602ae0 100644 --- a/apps/widget/src/components/Common/Container/Container.tsx +++ b/apps/widget/src/components/Common/Container/Container.tsx @@ -51,7 +51,7 @@ export function Container({ children }: PropsWithChildren<{}>) { setSecondaryPayload({ accessToken: data.value.accessToken, host: data.value.host, - file: data.value.file, + sampleFile: data.value.sampleFile, projectId: data.value.projectId, uuid: data.value.uuid, extra: isObject(data.value.extra) ? JSON.stringify(data.value.extra) : data.value.extra, @@ -340,7 +340,7 @@ export function Container({ children }: PropsWithChildren<{}>) { authHeaderValue={secondaryPayload?.authHeaderValue} // eslint-disable-next-line @typescript-eslint/no-non-null-assertion primaryColor={secondaryPayload.primaryColor!} - file={secondaryPayload?.file} + sampleFile={secondaryPayload?.sampleFile} > {children} diff --git a/apps/widget/src/components/Common/Provider/Provider.tsx b/apps/widget/src/components/Common/Provider/Provider.tsx index 930a6f81..8c606220 100644 --- a/apps/widget/src/components/Common/Provider/Provider.tsx +++ b/apps/widget/src/components/Common/Provider/Provider.tsx @@ -12,7 +12,7 @@ interface IProviderProps { texts: typeof WIDGET_TEXTS; primaryColor: string; output?: string; - file?: File | Blob; + sampleFile?: File | Blob; schema?: string; data?: string; host: string; @@ -32,7 +32,7 @@ export function Provider(props: PropsWithChildren) { api, data, title, - file, + sampleFile, texts, output, projectId, @@ -60,7 +60,7 @@ export function Provider(props: PropsWithChildren) { host={host} data={data} title={title} - file={file} + sampleFile={sampleFile} texts={texts} output={output} schema={schema} diff --git a/apps/widget/src/hooks/Phase0/usePhase0.ts b/apps/widget/src/hooks/Phase0/usePhase0.ts index 1008cafd..0e8fd5ca 100644 --- a/apps/widget/src/hooks/Phase0/usePhase0.ts +++ b/apps/widget/src/hooks/Phase0/usePhase0.ts @@ -16,7 +16,7 @@ export function usePhase0({ goNext }: IUsePhase0Props) { const { api } = useAPIState(); const { projectId, templateId } = useImplerState(); const [fileError, setFileError] = useState(null); - const { schema, setImportConfig, showWidget, setFlow, file } = useAppState(); + const { schema, setImportConfig, showWidget, setFlow, sampleFile } = useAppState(); const { mutate: fetchImportConfig } = useMutation( ['importConfig', projectId, templateId], @@ -34,7 +34,7 @@ export function usePhase0({ goNext }: IUsePhase0Props) { FileMimeTypesEnum.EXCELX, ]; - if (file && !isValidFileType(file as Blob)) { + if (sampleFile && !isValidFileType(sampleFile as Blob)) { setFileError(`Only ${allowedTypes.join(',')} are supported`); } else { goNext(); diff --git a/apps/widget/src/hooks/Phase1/usePhase1.ts b/apps/widget/src/hooks/Phase1/usePhase1.ts index 99535ab1..6f4a906f 100644 --- a/apps/widget/src/hooks/Phase1/usePhase1.ts +++ b/apps/widget/src/hooks/Phase1/usePhase1.ts @@ -40,7 +40,7 @@ export function usePhase1({ goNext, texts, onManuallyEnterData }: IUsePhase1Prop const { templateId, authHeaderValue, extra } = useImplerState(); const [excelSheetNames, setExcelSheetNames] = useState([]); const [isDownloadInProgress, setIsDownloadInProgress] = useState(false); - const { setUploadInfo, setTemplateInfo, output, schema, data, importId, imageSchema, file } = useAppState(); + const { setUploadInfo, setTemplateInfo, output, schema, data, importId, imageSchema, sampleFile } = useAppState(); const selectedTemplateId = watch('templateId'); @@ -108,10 +108,10 @@ export function usePhase1({ goNext, texts, onManuallyEnterData }: IUsePhase1Prop } }; const onDownloadClick = async () => { - if (file) { - const fileName = (file as File).name; + if (sampleFile) { + const fileName = (sampleFile as File).name; const fileBaseName = fileName.split('.')[0]; - downloadFile(file as Blob, fileBaseName); + downloadFile(sampleFile as Blob, fileBaseName); } setIsDownloadInProgress(true); diff --git a/apps/widget/src/store/app.context.tsx b/apps/widget/src/store/app.context.tsx index 8847d118..f1ded18c 100644 --- a/apps/widget/src/store/app.context.tsx +++ b/apps/widget/src/store/app.context.tsx @@ -30,7 +30,7 @@ const AppContextProvider = ({ title, texts, data, - file, + sampleFile, schema, output, host, @@ -67,7 +67,7 @@ const AppContextProvider = ({ uploadInfo, setImportId, imageSchema, - file, + sampleFile, templateInfo, importConfig, primaryColor, diff --git a/apps/widget/src/types/store.types.ts b/apps/widget/src/types/store.types.ts index e9cf42ad..5c22bc57 100644 --- a/apps/widget/src/types/store.types.ts +++ b/apps/widget/src/types/store.types.ts @@ -40,7 +40,7 @@ export interface IAppStore { schema?: string; output?: string; flow: FlowsEnum; - file?: File | Blob; + sampleFile?: File | Blob; showWidget: boolean; importConfig: IImportConfig; setFlow: (flow: FlowsEnum) => void; diff --git a/libs/shared/src/types/widget/widget.types.ts b/libs/shared/src/types/widget/widget.types.ts index eddcfbfc..a3f07516 100644 --- a/libs/shared/src/types/widget/widget.types.ts +++ b/libs/shared/src/types/widget/widget.types.ts @@ -9,7 +9,7 @@ export interface ICommonShowPayload { primaryColor?: string; colorScheme?: string; title?: string; - file?: File | Blob; + sampleFile?: File | Blob; projectId: string; accessToken: string; uuid: string; diff --git a/packages/client/src/types.ts b/packages/client/src/types.ts index 6af5e5b4..94da3779 100644 --- a/packages/client/src/types.ts +++ b/packages/client/src/types.ts @@ -163,7 +163,7 @@ export interface IShowWidgetProps { projectId: string; templateId: string; accessToken: string; - file?: File | Blob; + sampleFile?: File | Blob; texts?: CustomTexts; title?: string; primaryColor?: string; diff --git a/packages/react/src/hooks/useImpler.ts b/packages/react/src/hooks/useImpler.ts index 3d7d2ce0..24a73825 100644 --- a/packages/react/src/hooks/useImpler.ts +++ b/packages/react/src/hooks/useImpler.ts @@ -79,10 +79,10 @@ export function useImpler({ const showWidget = async ({ colorScheme, data, - file, + sampleFile, schema, output, - }: Pick = {}) => { + }: Pick = {}) => { if (window.impler && isImplerInitiated) { const payload: IShowWidgetProps & { uuid: string; host: string } = { uuid, @@ -92,7 +92,7 @@ export function useImpler({ accessToken, schema, data, - file, + sampleFile, output, title, extra,