From 599202ac98479531f688705f5c7a234e6fdd0ece Mon Sep 17 00:00:00 2001 From: Mayur Date: Mon, 15 Dec 2025 13:32:28 +0530 Subject: [PATCH 01/12] refactor: Remove explicit date and cell text parsing options from Excel to CSV conversion --- apps/api/src/app/shared/services/file/file.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/api/src/app/shared/services/file/file.service.ts b/apps/api/src/app/shared/services/file/file.service.ts index 0f9fa500..3a7bea3a 100644 --- a/apps/api/src/app/shared/services/file/file.service.ts +++ b/apps/api/src/app/shared/services/file/file.service.ts @@ -13,14 +13,13 @@ export class ExcelFileService { async convertToCsv(file: Express.Multer.File, sheetName?: string): Promise { return new Promise(async (resolve, reject) => { try { - const wb = XLSX.read(file.buffer as any, { cellDates: true, cellText: false }); + const wb = XLSX.read(file.buffer as any); const ws = sheetName && wb.SheetNames.includes(sheetName) ? wb.Sheets[sheetName] : wb.Sheets[wb.SheetNames[0]]; resolve( XLSX.utils.sheet_to_csv(ws, { blankrows: false, skipHidden: true, forceQuotes: true, - dateNF: Defaults.DATE_FORMAT.toLowerCase(), // rawNumbers: true, // was converting 12:12:12 to 1.3945645673 }) ); From 65613f96f3039f2f6c2d0bafa50518020c4cf6e4 Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 19 Dec 2025 20:03:31 +0530 Subject: [PATCH 02/12] feat: Added expectedDateFormat to accept the user provided date format --- apps/api/src/app/template/dtos/template-response.dto.ts | 7 +++++++ .../src/app/template/dtos/update-template-request.dto.ts | 7 +++++++ apps/api/src/app/template/template.controller.ts | 1 + .../get-template-details/get-template-details.usecase.ts | 3 ++- .../usecases/update-template/update-template.command.ts | 4 ++++ .../make-upload-entry/make-upload-entry.usecase.ts | 3 ++- apps/web/types/global.d.ts | 1 + libs/dal/src/repositories/template/template.entity.ts | 2 ++ libs/dal/src/repositories/template/template.schema.ts | 4 ++++ libs/shared/src/entities/Template/Template.interface.ts | 1 + 10 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/api/src/app/template/dtos/template-response.dto.ts b/apps/api/src/app/template/dtos/template-response.dto.ts index e1e8b616..1231d023 100644 --- a/apps/api/src/app/template/dtos/template-response.dto.ts +++ b/apps/api/src/app/template/dtos/template-response.dto.ts @@ -66,4 +66,11 @@ export class TemplateResponseDto { @IsString() @IsDefined() mode: string; + + @ApiPropertyOptional({ + description: 'Expected Date Format', + }) + @IsString() + @IsOptional() + expectedDateFormat?: string; } diff --git a/apps/api/src/app/template/dtos/update-template-request.dto.ts b/apps/api/src/app/template/dtos/update-template-request.dto.ts index cf0cd60f..d6734b08 100644 --- a/apps/api/src/app/template/dtos/update-template-request.dto.ts +++ b/apps/api/src/app/template/dtos/update-template-request.dto.ts @@ -24,4 +24,11 @@ export class UpdateTemplateRequestDto { @IsOptional() @IsEnum(IntegrationEnum) integration?: IntegrationEnum; + + @ApiProperty({ + description: 'Expected Date Format', + nullable: true, + }) + @IsOptional() + expectedDateFormat?: string; } diff --git a/apps/api/src/app/template/template.controller.ts b/apps/api/src/app/template/template.controller.ts index 4d75d0a0..081730a8 100644 --- a/apps/api/src/app/template/template.controller.ts +++ b/apps/api/src/app/template/template.controller.ts @@ -223,6 +223,7 @@ export class TemplateController { UpdateTemplateCommand.create({ name: body.name, mode: body.mode, + expectedDateFormat: body.expectedDateFormat, }), templateId ); diff --git a/apps/api/src/app/template/usecases/get-template-details/get-template-details.usecase.ts b/apps/api/src/app/template/usecases/get-template-details/get-template-details.usecase.ts index 066bd18c..21f22ee0 100644 --- a/apps/api/src/app/template/usecases/get-template-details/get-template-details.usecase.ts +++ b/apps/api/src/app/template/usecases/get-template-details/get-template-details.usecase.ts @@ -17,7 +17,7 @@ export class GetTemplateDetails { }): Promise { const template = await this.templateRepository.findOne( { _id: _templateId, _projectId }, - '_projectId name sampleFileUrl _id totalUploads totalInvalidRecords totalRecords mode integration' + '_projectId name sampleFileUrl _id totalUploads totalInvalidRecords totalRecords mode integration expectedDateFormat' ); if (!template) { throw new DocumentNotFoundException('Template', _templateId); @@ -33,6 +33,7 @@ export class GetTemplateDetails { totalRecords: template.totalRecords, mode: template.mode, integration: template.integration as IntegrationEnum, + expectedDateFormat: template.expectedDateFormat, }; } } diff --git a/apps/api/src/app/template/usecases/update-template/update-template.command.ts b/apps/api/src/app/template/usecases/update-template/update-template.command.ts index 371c6277..4c2f43b7 100644 --- a/apps/api/src/app/template/usecases/update-template/update-template.command.ts +++ b/apps/api/src/app/template/usecases/update-template/update-template.command.ts @@ -20,4 +20,8 @@ export class UpdateTemplateCommand extends BaseCommand { @IsEnum(IntegrationEnum) @IsOptional() integration?: IntegrationEnum; + + @IsString() + @IsOptional() + expectedDateFormat?: string; } diff --git a/apps/api/src/app/upload/usecases/make-upload-entry/make-upload-entry.usecase.ts b/apps/api/src/app/upload/usecases/make-upload-entry/make-upload-entry.usecase.ts index b243643e..65afbfee 100644 --- a/apps/api/src/app/upload/usecases/make-upload-entry/make-upload-entry.usecase.ts +++ b/apps/api/src/app/upload/usecases/make-upload-entry/make-upload-entry.usecase.ts @@ -66,6 +66,7 @@ export class MakeUploadEntry { file.mimetype === FileMimeTypesEnum.EXCELM ) { try { + const template = await this.templateRepository.findOne({ _id: templateId }, 'expectedDateFormat'); const fileService = new ExcelFileService(); const opts = await fileService.getExcelRowsColumnsCount(file, selectedSheetName); @@ -77,7 +78,7 @@ export class MakeUploadEntry { } this.analyzeLargeFile(opts, true, maxRecords); - csvFile = await fileService.convertToCsv(file, selectedSheetName); + csvFile = await fileService.convertToCsv(file, selectedSheetName, template?.expectedDateFormat); } catch (error) { if (error instanceof FileSizeException || error instanceof MaxRecordsExceededException) { throw error; diff --git a/apps/web/types/global.d.ts b/apps/web/types/global.d.ts index e43f171c..86a7e28e 100644 --- a/apps/web/types/global.d.ts +++ b/apps/web/types/global.d.ts @@ -88,6 +88,7 @@ interface IUpdateTemplateData { mode?: string; name?: string; integration?: string; + expectedDateFormat?: string; } interface Window { diff --git a/libs/dal/src/repositories/template/template.entity.ts b/libs/dal/src/repositories/template/template.entity.ts index fa2e27ef..02642ae6 100644 --- a/libs/dal/src/repositories/template/template.entity.ts +++ b/libs/dal/src/repositories/template/template.entity.ts @@ -22,4 +22,6 @@ export class TemplateEntity { imageColumns: string[]; integration: IntegrationEnum; + + expectedDateFormat: string; } diff --git a/libs/dal/src/repositories/template/template.schema.ts b/libs/dal/src/repositories/template/template.schema.ts index 5c9d1532..8e9f981c 100644 --- a/libs/dal/src/repositories/template/template.schema.ts +++ b/libs/dal/src/repositories/template/template.schema.ts @@ -39,6 +39,10 @@ const templateSchema = new Schema( default: 'manual', }, + expectedDateFormat: { + type: Schema.Types.String, + }, + integration: { type: Schema.Types.String, }, diff --git a/libs/shared/src/entities/Template/Template.interface.ts b/libs/shared/src/entities/Template/Template.interface.ts index 9ff897de..a5755720 100644 --- a/libs/shared/src/entities/Template/Template.interface.ts +++ b/libs/shared/src/entities/Template/Template.interface.ts @@ -15,6 +15,7 @@ export interface ITemplate { totalInvalidRecords: number; mode: string; integration: IntegrationEnum; + expectedDateFormat?: string; } export interface ITemplateSchema { From 13d8a431b027d95016ce7bd694aca6e50bb6dc7b Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 19 Dec 2025 20:06:14 +0530 Subject: [PATCH 03/12] feat: Added the dateNF?: string as a parameter to convertToCsv to dynamically pass the format --- apps/api/src/app/shared/services/file/file.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/src/app/shared/services/file/file.service.ts b/apps/api/src/app/shared/services/file/file.service.ts index 3a7bea3a..6b40650f 100644 --- a/apps/api/src/app/shared/services/file/file.service.ts +++ b/apps/api/src/app/shared/services/file/file.service.ts @@ -10,10 +10,10 @@ import { InvalidFileException } from '@shared/exceptions/invalid-file.exception' import { IExcelFileHeading } from '@shared/types/file.types'; export class ExcelFileService { - async convertToCsv(file: Express.Multer.File, sheetName?: string): Promise { + async convertToCsv(file: Express.Multer.File, sheetName?: string, dateNF?: string): Promise { return new Promise(async (resolve, reject) => { try { - const wb = XLSX.read(file.buffer as any); + const wb = XLSX.read(file.buffer as any, { dateNF }); const ws = sheetName && wb.SheetNames.includes(sheetName) ? wb.Sheets[sheetName] : wb.Sheets[wb.SheetNames[0]]; resolve( XLSX.utils.sheet_to_csv(ws, { From 8f9a91d1e2c0a7b405c5380cb7d009914695f9c6 Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 19 Dec 2025 20:07:41 +0530 Subject: [PATCH 04/12] feat: Added the fields import type and the date format in the form --- .../imports/forms/UpdateImportForm.tsx | 105 ++++++++++++++++-- 1 file changed, 98 insertions(+), 7 deletions(-) diff --git a/apps/web/components/imports/forms/UpdateImportForm.tsx b/apps/web/components/imports/forms/UpdateImportForm.tsx index f8c1fa16..9ff24180 100644 --- a/apps/web/components/imports/forms/UpdateImportForm.tsx +++ b/apps/web/components/imports/forms/UpdateImportForm.tsx @@ -1,20 +1,26 @@ import { useEffect } from 'react'; -import { Stack, TextInput as Input } from '@mantine/core'; -import { useForm } from 'react-hook-form'; +import { Stack, TextInput as Input, Text, SegmentedControl, Box } from '@mantine/core'; +import { useForm, Controller } from 'react-hook-form'; import { useFocusTrap } from '@mantine/hooks'; import { Button } from '@ui/button'; -import { ITemplate } from '@impler/shared'; +import { ITemplate, TemplateModeEnum } from '@impler/shared'; +import { TooltipLabel } from '@components/guide-point'; +import { DOCUMENTATION_REFERENCE_LINKS, SAMPLE_DATE_FORMATS, VARIABLES } from '@config'; +import { MultiSelect } from '@ui/multi-select'; +import { validateDateFormatString } from '@shared/utils'; interface UpdateImportFormProps { data: ITemplate; onSubmit: (data: IUpdateTemplateData) => void; + isAutoImportAvailable: boolean; } -export function UpdateImportForm({ onSubmit, data }: UpdateImportFormProps) { +export function UpdateImportForm({ onSubmit, data, isAutoImportAvailable }: UpdateImportFormProps) { const focusTrapRef = useFocusTrap(); const { reset, + control, register, handleSubmit, formState: { errors }, @@ -23,20 +29,105 @@ export function UpdateImportForm({ onSubmit, data }: UpdateImportFormProps) { useEffect(() => { reset({ name: data.name, + mode: data.mode || TemplateModeEnum.MANUAL, + expectedDateFormat: data.expectedDateFormat, }); }, [data, reset]); + const handleFormSubmit = (formData: IUpdateTemplateData) => { + onSubmit(formData); + }; + return ( -
- + + - From d9c559801939a5cab8c4b528bcd58e5bbc51ea5d Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 19 Dec 2025 20:08:37 +0530 Subject: [PATCH 05/12] feat: Added SAMPLE_DATE_FORMATS and ERROR_UPDATING_IMPORT_TEMPLATE --- apps/web/config/constants.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/web/config/constants.config.ts b/apps/web/config/constants.config.ts index eb946f17..9b944f38 100644 --- a/apps/web/config/constants.config.ts +++ b/apps/web/config/constants.config.ts @@ -260,6 +260,7 @@ export const NOTIFICATION_KEYS = { ERROR_DELETING_INVITATION: 'INVITATION_DELETED', PERMISSION_DENIED_WHILE_DELETING_PROJECT: 'PERMISSION_DENIED_WHILE_DELETING_PROJECT', SUBSCRIPTION_FEATURE_NOT_INCLUDED_IN_CURRENT_PLAN: 'SUBSCRIPTION_FEATURE_NOT_INCLUDED_IN_CURRENT_PLAN', + ERROR_UPDATING_IMPORT_TEMPLATE: 'ERROR_UPDATING_IMPORT_TEMPLATE', } as const; export const ROUTES = { @@ -881,3 +882,5 @@ export const defaultWidgetAppereanceThemeDark = { buttonShadow: '0px 4px 16px rgba(0, 0, 0, 0.6)', }, }; + +export const SAMPLE_DATE_FORMATS = ['DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY-MM-DD', 'DD-MM-YYYY', 'MM-DD-YYYY']; From 0995f2a92b9640cede083590c000ce80a2b30bc5 Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 19 Dec 2025 20:09:11 +0530 Subject: [PATCH 06/12] feat: Added the invalidateQueries to refetch the updated template data --- apps/web/hooks/useImportDetails.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/web/hooks/useImportDetails.tsx b/apps/web/hooks/useImportDetails.tsx index cb3679bf..2757cf61 100644 --- a/apps/web/hooks/useImportDetails.tsx +++ b/apps/web/hooks/useImportDetails.tsx @@ -51,7 +51,7 @@ export function useImportDetails({ templateId }: useImportDetailProps) { [API_KEYS.TEMPLATES_LIST, profileInfo!._projectId], (oldData) => oldData?.map((item) => (item._id === data._id ? data : item)) ); - queryClient.setQueryData([API_KEYS.TEMPLATE_DETAILS, templateId], data); + queryClient.invalidateQueries([API_KEYS.TEMPLATE_DETAILS, templateId]); notify(NOTIFICATION_KEYS.IMPORT_UPDATED); }, } @@ -111,7 +111,15 @@ export function useImportDetails({ templateId }: useImportDetailProps) { modalId: MODAL_KEYS.IMPORT_UPDATE, title: MODAL_TITLES.IMPORT_UPDATE, - children: , + children: ( + + ), + size: 'xl', + centered: true, }); }; From b82a33428af2c6fc04d495ed65979d5773dcc7d6 Mon Sep 17 00:00:00 2001 From: Mayur Date: Fri, 19 Dec 2025 20:11:38 +0530 Subject: [PATCH 07/12] feat: Made some UI changes to open that UpdateImportForm on clicking on the Button rather than Icon --- apps/web/pages/imports/[id].tsx | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/apps/web/pages/imports/[id].tsx b/apps/web/pages/imports/[id].tsx index 74d3543f..ab67da19 100644 --- a/apps/web/pages/imports/[id].tsx +++ b/apps/web/pages/imports/[id].tsx @@ -2,10 +2,10 @@ import Link from 'next/link'; import React, { useCallback, useEffect, useState } from 'react'; import dynamic from 'next/dynamic'; import { useRouter } from 'next/router'; -import { ActionIcon, Flex, Group, LoadingOverlay, Title, Select } from '@mantine/core'; +import { Flex, Group, LoadingOverlay, Title } from '@mantine/core'; import { track } from '@libs/amplitude'; -import { defaultWidgetAppereance, TemplateModeEnum } from '@impler/shared'; -import { CONSTANTS, IMPORT_MODES, MODAL_KEYS, ROUTES, SubjectsEnum, colors } from '@config'; +import { defaultWidgetAppereance } from '@impler/shared'; +import { CONSTANTS, MODAL_KEYS, ROUTES, SubjectsEnum, colors } from '@config'; import { useImportDetails } from '@hooks/useImportDetails'; import { Tabs } from '@ui/Tabs'; @@ -44,10 +44,8 @@ function ImportDetails() { const [activeTab, setActiveTab] = useState<'schema' | 'destination' | 'snippet' | 'validator' | 'output'>(); const { - meta, columns, profileInfo, - updateImport, templateData, onUpdateClick, onDeleteClick, @@ -149,23 +147,12 @@ function ImportDetails() { {templateData?.name} - - - -