Skip to content
Merged

v1.9.3 #1145

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
599202a
refactor: Remove explicit date and cell text parsing options from Exc…
thisismayuresh Dec 15, 2025
797d292
refactor: Remove explicit date and cell text parsing options from Exc…
thisismayuresh Dec 15, 2025
65613f9
feat: Added expectedDateFormat to accept the user provided date format
thisismayuresh Dec 19, 2025
13d8a43
feat: Added the dateNF?: string as a parameter to convertToCsv to dyn…
thisismayuresh Dec 19, 2025
8f9a91d
feat: Added the fields import type and the date format in the form
thisismayuresh Dec 19, 2025
d9c5598
feat: Added SAMPLE_DATE_FORMATS and ERROR_UPDATING_IMPORT_TEMPLATE
thisismayuresh Dec 19, 2025
0995f2a
feat: Added the invalidateQueries to refetch the updated template data
thisismayuresh Dec 19, 2025
b82a334
feat: Made some UI changes to open that UpdateImportForm on clicking …
thisismayuresh Dec 19, 2025
9597583
feat: Added a custom date format parser for checking if the provided …
thisismayuresh Dec 19, 2025
922fb3d
feat: Removed the Redundant field description
thisismayuresh Dec 19, 2025
ec0afb5
Fix/user date format in edit template (#1141)
chavda-bhavik Dec 19, 2025
7563d9b
feat: Added the LockIcon for the AutomaticImports if this feature not…
thisismayuresh Dec 22, 2025
f9ff2b4
feat: Added the LockIcon for the AutomaticImports if this feature not…
thisismayuresh Dec 22, 2025
90b2696
v1.9.3
thisismayuresh Dec 22, 2025
db21a57
feat: Updated docker-compose images version
thisismayuresh Dec 22, 2025
a9340d2
Release/1.9.3 (#1143)
thisismayuresh Dec 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@impler/api",
"version": "1.9.2",
"version": "1.9.3",
"author": "implerhq",
"license": "MIT",
"private": true,
Expand Down
5 changes: 2 additions & 3 deletions apps/api/src/app/shared/services/file/file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,16 @@ 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<string> {
async convertToCsv(file: Express.Multer.File, sheetName?: string, dateNF?: string): Promise<string> {
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, { dateNF });
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
})
);
Expand Down
7 changes: 7 additions & 0 deletions apps/api/src/app/template/dtos/template-response.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,11 @@ export class TemplateResponseDto {
@IsString()
@IsDefined()
mode: string;

@ApiPropertyOptional({
description: 'Expected Date Format',
})
@IsString()
@IsOptional()
expectedDateFormat?: string;
}
7 changes: 7 additions & 0 deletions apps/api/src/app/template/dtos/update-template-request.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,11 @@ export class UpdateTemplateRequestDto {
@IsOptional()
@IsEnum(IntegrationEnum)
integration?: IntegrationEnum;

@ApiProperty({
description: 'Expected Date Format',
nullable: true,
})
@IsOptional()
expectedDateFormat?: string;
}
1 change: 1 addition & 0 deletions apps/api/src/app/template/template.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ export class TemplateController {
UpdateTemplateCommand.create({
name: body.name,
mode: body.mode,
expectedDateFormat: body.expectedDateFormat,
}),
templateId
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class GetTemplateDetails {
}): Promise<TemplateResponseDto> {
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);
Expand All @@ -33,6 +33,7 @@ export class GetTemplateDetails {
totalRecords: template.totalRecords,
mode: template.mode,
integration: template.integration as IntegrationEnum,
expectedDateFormat: template.expectedDateFormat,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ export class UpdateTemplateCommand extends BaseCommand {
@IsEnum(IntegrationEnum)
@IsOptional()
integration?: IntegrationEnum;

@IsString()
@IsOptional()
expectedDateFormat?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion apps/queue-manager/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@impler/queue-manager",
"version": "1.9.2",
"version": "1.9.3",
"author": "implerhq",
"license": "MIT",
"private": true,
Expand Down
115 changes: 108 additions & 7 deletions apps/web/components/imports/forms/UpdateImportForm.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
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, Group, Flex } from '@mantine/core';
import { LockIcon } from '@assets/icons/Lock.icon';
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 { ImportConfigEnum } from '@types';
import { 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 },
Expand All @@ -23,20 +30,114 @@ 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 (
<form onSubmit={handleSubmit(onSubmit)} ref={focusTrapRef}>
<Stack spacing="sm">
<form onSubmit={handleSubmit(handleFormSubmit)} ref={focusTrapRef}>
<Stack spacing="lg">
<Input
autoFocus
required
label="Import Name"
{...register('name')}
error={errors.name?.message}
placeholder="I want to import..."
description="A descriptive name for this import"
/>
<Button type="submit" fullWidth>

<Box>
<Text size="sm" weight={500} mb={4}>
Import Mode
</Text>
<Text size="xs" color="dimmed" mb="xs">
Choose whether this import is triggered manually or automatically
</Text>
<Controller
name="mode"
control={control}
render={({ field }) => (
<SegmentedControl
fullWidth
value={field.value || ImportConfigEnum.MANUAL}
onChange={field.onChange}
data={[
{ label: 'Manual', value: ImportConfigEnum.MANUAL },
{
value: ImportConfigEnum.AUTOMATED,
disabled: !isAutoImportAvailable,
label: (
<Group position="center" spacing={4}>
{!isAutoImportAvailable && (
<Flex>
<LockIcon size="md" />
</Flex>
)}
Automatic
</Group>
),
},
]}
/>
)}
/>
</Box>

<Box>
<Controller
name="expectedDateFormat"
control={control}
rules={{
validate: (value) => {
if (!value) return true;

const result = validateDateFormatString(value as string);
if (typeof result === 'object' && 'isValid' in result) {
return result.isValid ? true : result.error || 'Invalid date format';
}

return result === true ? true : (result as string);
},
}}
render={({ field, fieldState }) => (
<MultiSelect
creatable
maxSelectedValues={VARIABLES.ONE}
clearable
searchable
label="Date Formats"
placeholder="Date Formats"
description="Define the date format you expect in your import data."
data={[
...SAMPLE_DATE_FORMATS,
...(field.value && !SAMPLE_DATE_FORMATS.includes(field.value) ? [field.value] : []),
]}
getCreateLabel={(query) => `Add "${query}"`}
onCreate={(newItem) => {
field.onChange(newItem);

return newItem;
}}
onChange={(value) => {
field.onChange(value[0]);
}}
error={fieldState.error?.message}
value={field.value ? [field.value] : []}
/>
)}
/>
<Text size="xs" color="dimmed" mt="xs">
Example formats: DD/MM/YYYY, MM/DD/YYYY, YYYY-MM-DD, DD-MMM-YYYY
</Text>
</Box>

<Button type="submit" fullWidth mt="md">
Update
</Button>
</Stack>
Expand Down
3 changes: 3 additions & 0 deletions apps/web/config/constants.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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'];
12 changes: 10 additions & 2 deletions apps/web/hooks/useImportDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<ITemplate>([API_KEYS.TEMPLATE_DETAILS, templateId], data);
queryClient.invalidateQueries([API_KEYS.TEMPLATE_DETAILS, templateId]);
notify(NOTIFICATION_KEYS.IMPORT_UPDATED);
},
}
Expand Down Expand Up @@ -111,7 +111,15 @@ export function useImportDetails({ templateId }: useImportDetailProps) {
modalId: MODAL_KEYS.IMPORT_UPDATE,
title: MODAL_TITLES.IMPORT_UPDATE,

children: <UpdateImportForm onSubmit={updateImport} data={templateData} />,
children: (
<UpdateImportForm
onSubmit={updateImport}
data={templateData}
isAutoImportAvailable={meta?.AUTOMATIC_IMPORTS ? true : false}
/>
),
size: 'xl',
centered: true,
});
};

Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@impler/web",
"version": "1.9.2",
"version": "1.9.3",
"author": "implerhq",
"license": "MIT",
"private": true,
Expand Down
25 changes: 6 additions & 19 deletions apps/web/pages/imports/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -44,10 +44,8 @@ function ImportDetails() {
const [activeTab, setActiveTab] = useState<'schema' | 'destination' | 'snippet' | 'validator' | 'output'>();

const {
meta,
columns,
profileInfo,
updateImport,
templateData,
onUpdateClick,
onDeleteClick,
Expand Down Expand Up @@ -149,23 +147,12 @@ function ImportDetails() {
</Button>
<Group spacing={0}>
<Title order={2}>{templateData?.name}</Title>
<ActionIcon radius={0} onClick={onUpdateClick} p={0}>
<EditIcon color={colors.blue} size="sm" />
</ActionIcon>
</Group>
</Group>
<Group spacing="xs">
<Select
size="sm"
maw={125}
placeholder="Mode"
data={IMPORT_MODES.map((mode) => ({
...mode,
disabled: mode.value === TemplateModeEnum.AUTOMATIC && !meta?.AUTOMATIC_IMPORTS ? true : false,
}))}
value={templateData?.mode || TemplateModeEnum.MANUAL}
onChange={(mode) => updateImport({ mode: mode || undefined })}
/>
<Button leftIcon={<EditIcon />} onClick={onUpdateClick}>
Edit
</Button>
<Button
color="green"
id="import"
Expand Down
Loading
Loading