Skip to content

Commit

Permalink
Merge pull request #5143 from novuhq/nv-2995-reusable-email-preview-c…
Browse files Browse the repository at this point in the history
…omponent

Nv 2995 reusable email preview component
  • Loading branch information
BiswaViraj authored Feb 9, 2024
2 parents bfd20c5 + cd9f145 commit 729c99e
Show file tree
Hide file tree
Showing 52 changed files with 1,327 additions and 517 deletions.
2 changes: 1 addition & 1 deletion .source
24 changes: 19 additions & 5 deletions apps/api/src/app/content-templates/content-templates.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ export class ContentTemplatesController {
@Body('contentType') contentType: MessageTemplateContentType,
@Body('payload') payload: any,
@Body('subject') subject: string,
@Body('layoutId') layoutId: string
@Body('layoutId') layoutId: string,
@Body('locale') locale?: string
) {
return this.compileEmailTemplateUsecase.execute(
CompileEmailTemplateCommand.create({
Expand All @@ -46,6 +47,7 @@ export class ContentTemplatesController {
payload,
subject,
layoutId,
locale,
}),
this.initiateTranslations.bind(this)
);
Expand All @@ -56,7 +58,8 @@ export class ContentTemplatesController {
@UserSession() user: IJwtPayload,
@Body('content') content: string,
@Body('payload') payload: any,
@Body('cta') cta: IMessageCTA
@Body('cta') cta: IMessageCTA,
@Body('locale') locale?: string
) {
return this.compileInAppTemplate.execute(
CompileInAppTemplateCommand.create({
Expand All @@ -66,20 +69,27 @@ export class ContentTemplatesController {
content,
payload,
cta,
locale,
}),
this.initiateTranslations.bind(this)
);
}
// TODO: refactor this to use params and single endpoint to manage all the channels
@Post('/preview/sms')
public previewSms(@UserSession() user: IJwtPayload, @Body('content') content: string, @Body('payload') payload: any) {
public previewSms(
@UserSession() user: IJwtPayload,
@Body('content') content: string,
@Body('payload') payload: any,
@Body('locale') locale?: string
) {
return this.compileStepTemplate.execute(
CompileStepTemplateCommand.create({
userId: user._id,
organizationId: user.organizationId,
environmentId: user.environmentId,
content,
payload,
locale,
}),
this.initiateTranslations.bind(this)
);
Expand All @@ -89,7 +99,8 @@ export class ContentTemplatesController {
public previewChat(
@UserSession() user: IJwtPayload,
@Body('content') content: string,
@Body('payload') payload: any
@Body('payload') payload: any,
@Body('locale') locale?: string
) {
return this.compileStepTemplate.execute(
CompileStepTemplateCommand.create({
Expand All @@ -98,6 +109,7 @@ export class ContentTemplatesController {
environmentId: user.environmentId,
content,
payload,
locale,
}),
this.initiateTranslations.bind(this)
);
Expand All @@ -107,7 +119,8 @@ export class ContentTemplatesController {
public previewPush(
@UserSession() user: IJwtPayload,
@Body('content') content: string,
@Body('payload') payload: any
@Body('payload') payload: any,
@Body('locale') locale?: string
) {
return this.compileStepTemplate.execute(
CompileStepTemplateCommand.create({
Expand All @@ -116,6 +129,7 @@ export class ContentTemplatesController {
environmentId: user.environmentId,
content,
payload,
locale,
}),
this.initiateTranslations.bind(this)
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { UserSession } from '@novu/testing';
import { expect } from 'chai';

const createTranslationGroup = {
name: 'test',
identifier: 'test',
locales: ['hi_IN', 'en_US'],
};

const content = 'Hello {{i18n "test.key1"}}, {{i18n "test.key2"}}, {{i18n "test.key3"}}';

describe('Get locales from content - /translations/groups/:identifier/locales/:locale (PATCH)', async () => {
let session: UserSession;

before(async () => {
session = new UserSession();
await session.initialize();
await session.testAgent.put(`/v1/organizations/language`).send({
locale: createTranslationGroup.locales[0],
});

await session.testAgent.post('/v1/translations/groups').send(createTranslationGroup);
});

it('should get locales from the content', async () => {
const { body } = await session.testAgent.post('/v1/translations/groups/preview/locales').send({
content,
});

const locales = body.data;

expect(locales.length).to.equal(2);
expect(locales[0].langIso).to.equal(createTranslationGroup.locales[0]);
expect(locales[1].langIso).to.equal(createTranslationGroup.locales[1]);
});
});
4 changes: 2 additions & 2 deletions apps/web/cypress/tests/tenants-page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('Tenants Page', function () {
cy.getByTestId('tenants-list-table')
.find('tr')
.eq(1)
.click()
.click({ force: true })
.then(() => {
cy.getByTestId('tenant-name').clear().type('New Name');
cy.getByTestId('update-tenant-sidebar-submit').click();
Expand All @@ -40,7 +40,7 @@ describe('Tenants Page', function () {
cy.getByTestId('tenants-list-table')
.find('tr')
.eq(1)
.click()
.click({ force: true })
.then(() => {
cy.getByTestId('tenant-identifier').clear().type('new-identifier');
cy.getByTestId('update-tenant-sidebar-submit').click();
Expand Down
30 changes: 27 additions & 3 deletions apps/web/src/api/content-templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,40 @@ export async function previewEmail({
payload,
subject,
layoutId,
locale,
}: {
content?: string | IEmailBlock[];
contentType?: MessageTemplateContentType;
payload: string;
subject?: string;
layoutId?: string;
locale?: string;
}) {
return api.post('/v1/content-templates/preview/email', { content, contentType, payload, subject, layoutId });
return api.post('/v1/content-templates/preview/email', { content, contentType, payload, subject, layoutId, locale });
}

export async function previewInApp({ content, cta, payload }: { content?: string; cta: any; payload: string }) {
return api.post('/v1/content-templates/preview/in-app', { content, payload, cta });
export async function previewInApp({
content,
cta,
payload,
locale,
}: {
content?: string;
cta: any;
payload: string;
locale?: string;
}) {
return api.post('/v1/content-templates/preview/in-app', { content, payload, cta, locale });
}

export async function previewChat({
content,
payload,
locale,
}: {
content?: string;
payload: string;
locale?: string;
}) {
return api.post('/v1/content-templates/preview/chat', { content, payload, locale });
}
2 changes: 2 additions & 0 deletions apps/web/src/api/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export * from './useInAppActivated';
export * from './useDeleteIntegration';
export * from './useWebhookSupportStatus';
export * from './notification-templates';
export * from './useGetLocalesFromContent';
export * from './usePreviewEmail';
56 changes: 56 additions & 0 deletions apps/web/src/api/hooks/useGetLocalesFromContent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { errorMessage } from '@novu/design-system';
import { IEmailBlock } from '@novu/shared';
import { IS_DOCKER_HOSTED } from '@novu/shared-web';
import { useMutation } from '@tanstack/react-query';
import { useCallback } from 'react';
import { getLocalesFromContent } from '../translations';

export interface ILocale {
name: string;
officialName: string | null;
numeric: string;
alpha2: string;
alpha3: string;
currencyName: string | null;
currencyAlphabeticCode: string | null;
langName: string;
langIso: string;
}

type Payload = {
content: string | IEmailBlock[];
};

export const useGetLocalesFromContent = () => {
const {
mutateAsync: getLocalesFromContentMutation,
isLoading,
data,
} = useMutation<ILocale[], { error: string; message: string; statusCode: number }, Payload>(
({ content }) => getLocalesFromContent({ content }),
{
onError: (e: any) => {
errorMessage(e.message || 'Unexpected error');
},
}
);

const getLocalesFromContentCallback = useCallback(
async ({ content }: Payload) => {
if (IS_DOCKER_HOSTED) {
return;
}

await getLocalesFromContentMutation({
content,
});
},
[getLocalesFromContentMutation]
);

return {
getLocalesFromContent: getLocalesFromContentCallback,
isLoading,
data: data || [],
};
};
58 changes: 58 additions & 0 deletions apps/web/src/api/hooks/usePreviewEmail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { errorMessage } from '@novu/design-system';
import { IEmailBlock, MessageTemplateContentType } from '@novu/shared';
import { IS_DOCKER_HOSTED } from '@novu/shared-web';
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
import { useCallback } from 'react';
import { previewEmail } from '../content-templates';

export type PayloadType = {
content?: string | IEmailBlock[];
contentType?: MessageTemplateContentType;
payload: string;
subject?: string;
layoutId?: string;
locale?: string;
};

export type ResultType = { html: string; subject: string };

type ErrorType = { error: string; message: string; statusCode: number };

export const usePreviewEmail = (options: UseMutationOptions<ResultType, ErrorType, PayloadType> = {}) => {
const { mutateAsync, isLoading } = useMutation<ResultType, ErrorType, PayloadType>(
({ content, payload, contentType, layoutId, locale, subject }) =>
previewEmail({ content, payload, contentType, layoutId, locale, subject }),

{
onError: (e: any) => {
errorMessage(e.message || 'Unexpected error');
},
onSuccess: (result, variables, context) => {
options?.onSuccess?.(result, variables, context);
},
}
);

const getEmailPreviewCallback = useCallback(
async ({ content, payload, contentType, layoutId, locale, subject }: PayloadType) => {
if (IS_DOCKER_HOSTED) {
return;
}

await mutateAsync({
content,
payload,
contentType,
layoutId,
locale,
subject,
});
},
[mutateAsync]
);

return {
getEmailPreview: getEmailPreviewCallback,
isLoading,
};
};
5 changes: 5 additions & 0 deletions apps/web/src/api/translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { api } from './api.client';

export async function getLocalesFromContent({ content }) {
return api.post('/v1/translations/groups/preview/locales', { content });
}
Loading

0 comments on commit 729c99e

Please sign in to comment.