From cc949fed50e7fc62895f92560617fd9615e00f4d Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Thu, 1 Feb 2024 10:39:31 +0530 Subject: [PATCH 01/28] feat: email preview --- .source | 2 +- .../templates/components/ChannelPreview.tsx | 38 ++- .../src/pages/templates/editor/Preview.tsx | 132 ++++++---- .../templates/editor/PreviewEditOverlay.tsx | 39 +++ .../pages/templates/editor/PreviewMobile.tsx | 156 ++++++++---- .../templates/editor/PreviewUserIcon.tsx | 17 +- .../src/pages/templates/editor/PreviewWeb.tsx | 234 ++++++++++-------- 7 files changed, 397 insertions(+), 221 deletions(-) create mode 100644 apps/web/src/pages/templates/editor/PreviewEditOverlay.tsx diff --git a/.source b/.source index ec6ad1589d4..a93a0a65b29 160000 --- a/.source +++ b/.source @@ -1 +1 @@ -Subproject commit ec6ad1589d410122cdba6fb7c6ac07ed19bb4925 +Subproject commit a93a0a65b2996798326f31a2b87f6e34d97c5830 diff --git a/apps/web/src/pages/templates/components/ChannelPreview.tsx b/apps/web/src/pages/templates/components/ChannelPreview.tsx index 79dbd03b8c2..f13b6f52b70 100644 --- a/apps/web/src/pages/templates/components/ChannelPreview.tsx +++ b/apps/web/src/pages/templates/components/ChannelPreview.tsx @@ -1,12 +1,40 @@ -import { useParams } from 'react-router-dom'; import { StepTypeEnum } from '@novu/shared'; +import { useParams } from 'react-router-dom'; -import { useStepIndex } from '../hooks/useStepIndex'; +import { Preview as EmailPreview } from '../editor/Preview'; import { useNavigateFromEditor } from '../hooks/useNavigateFromEditor'; +import { useStepIndex } from '../hooks/useStepIndex'; import { ChannelPreviewSidebar } from './ChannelPreviewSidebar'; -const DummyPreviewComponent = () => { - return
dummy
; +const PreviewComponent = ({ channel }: { channel: StepTypeEnum }) => { + switch (channel) { + case StepTypeEnum.EMAIL: + return ; + + case StepTypeEnum.TRIGGER: + return <>TRIGGER; + + case StepTypeEnum.SMS: + return <>SMS; + + case StepTypeEnum.IN_APP: + return <>IN APP; + + case StepTypeEnum.CHAT: + return <>CHAT; + + case StepTypeEnum.PUSH: + return <>PUSH; + + case StepTypeEnum.DELAY: + return <>DELAY; + + case StepTypeEnum.DIGEST: + return <>DIGEST; + + default: + return <>dummy; + } }; export const ChannelPreview = () => { @@ -24,7 +52,7 @@ export const ChannelPreview = () => { return ( <> - + ); diff --git a/apps/web/src/pages/templates/editor/Preview.tsx b/apps/web/src/pages/templates/editor/Preview.tsx index 11e7bf24ebd..38f19300f23 100644 --- a/apps/web/src/pages/templates/editor/Preview.tsx +++ b/apps/web/src/pages/templates/editor/Preview.tsx @@ -14,10 +14,12 @@ import { errorMessage } from '../../../utils/notifications'; import { useActiveIntegrations } from '../../../hooks'; import { useStepFormPath } from '../hooks/useStepFormPath'; import type { IForm } from '../components/formTypes'; +import { useStepFormErrors } from '../hooks/useStepFormErrors'; -export const Preview = ({ view }: { view: string }) => { +export const Preview = ({ showVariables = true, view }: { view: string; showVariables?: boolean }) => { const { control } = useFormContext(); const path = useStepFormPath(); + const error = useStepFormErrors(); const subject = useWatch({ name: `${path}.template.subject`, @@ -98,64 +100,88 @@ export const Preview = ({ view }: { view: string }) => { return ( <> - - - - - - - - - + + + + + + + + + + + + + + + + +
+ + +
-
-
-
- -
- - -
-
-
+ + + + ) : ( + + )} ); }; diff --git a/apps/web/src/pages/templates/editor/PreviewEditOverlay.tsx b/apps/web/src/pages/templates/editor/PreviewEditOverlay.tsx new file mode 100644 index 00000000000..aa460f3041d --- /dev/null +++ b/apps/web/src/pages/templates/editor/PreviewEditOverlay.tsx @@ -0,0 +1,39 @@ +import { Flex, Group, Overlay, UnstyledButton } from '@mantine/core'; +import { colors, PencilOutlined, Text, Variant } from '@novu/design-system'; +import { StepTypeEnum } from '@novu/shared'; +import { useNavigate, useParams } from 'react-router-dom'; +import { useBasePath } from '../hooks/useBasePath'; + +export function PreviewEditOverlay() { + const basePath = useBasePath(); + const { channel, stepUuid, variantUuid } = useParams<{ + channel: StepTypeEnum; + stepUuid: string; + variantUuid: string; + }>(); + + const navigate = useNavigate(); + + const handleEditMessage = () => { + const isVariant = !!variantUuid; + let path = `${basePath}/${channel}/${stepUuid}`; + if (isVariant) { + path += `/variants/${variantUuid}`; + } + + navigate(path); + }; + + return ( + + + + + + Edit message + + + + + ); +} diff --git a/apps/web/src/pages/templates/editor/PreviewMobile.tsx b/apps/web/src/pages/templates/editor/PreviewMobile.tsx index e0426f27ced..97b58329e34 100644 --- a/apps/web/src/pages/templates/editor/PreviewMobile.tsx +++ b/apps/web/src/pages/templates/editor/PreviewMobile.tsx @@ -1,15 +1,17 @@ -import { Center, createStyles, Group, Loader } from '@mantine/core'; -import { format } from 'date-fns'; -import Frame from 'react-frame-component'; -import { colors } from '@novu/design-system'; -import { PreviewDateIcon } from './PreviewDateIcon'; -import { PreviewUserIcon } from './PreviewUserIcon'; +import { Center, createStyles, Group, Loader, Skeleton, Stack } from '@mantine/core'; +import { colors, Text } from '@novu/design-system'; +import { useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; -import { Mobile } from './Mobile'; +import Frame from 'react-frame-component'; +import { FieldError, FieldErrorsImpl, Merge } from 'react-hook-form'; import { When } from '../../../components/utils/When'; +import { IFormStep } from '../components/formTypes'; import { EmailIntegrationInfo } from './EmailIntegrationInfo'; +import { Mobile } from './Mobile'; +import { PreviewEditOverlay } from './PreviewEditOverlay'; +import { PreviewUserIcon } from './PreviewUserIcon'; -const useStyles = createStyles((theme) => ({ +const useStyles = createStyles((theme, { error }: { error: boolean }) => ({ header: { width: '100%', marginTop: '20px', @@ -24,10 +26,6 @@ const useStyles = createStyles((theme) => ({ color: theme.colorScheme === 'dark' ? colors.B60 : colors.B70, fontWeight: 'normal', }, - date: { - color: theme.colorScheme === 'dark' ? colors.B60 : colors.B70, - fontWeight: 'normal', - }, line: { height: '2px', width: '340px', @@ -50,6 +48,10 @@ const useStyles = createStyles((theme) => ({ padding: '15px', textAlign: 'center', }, + content: { + position: 'relative', + border: error ? `1px solid ${colors.error}` : 'none', + }, })); export const PreviewMobile = ({ @@ -57,13 +59,31 @@ export const PreviewMobile = ({ subject, content, loading = false, + error, + showEditOverlay = false, }: { integration: any; subject?: string; content: string; loading?: boolean; + error?: Merge>; + showEditOverlay?: boolean; }) => { - const { classes } = useStyles(); + const { classes } = useStyles({ error: !!(error && error.template?.content && error.template?.content?.message) }); + + const [isEditOverlayVisible, setIsEditOverlayVisible] = useState(false); + + const handleMouseEnter = () => { + if (showEditOverlay) { + setIsEditOverlayVisible(true); + } + }; + + const handleMouseLeave = () => { + if (showEditOverlay && isEditOverlayVisible) { + setIsEditOverlayVisible(false); + } + }; return ( <> @@ -83,54 +103,82 @@ export const PreviewMobile = ({ }} spacing={13} > - -
-
- {subject} + + + + + + + + + + +
+ {error && error.template?.subject && error.template?.subject?.message ? ( + {error.template.subject.message} + ) : ( + <> +
+ {subject} +
+ +
+ +
+
+ + )}
- -
- -
-
- - - {format(new Date(), 'EEE, MMM d, HH:mm')} - -
-
-
+
- -
-
- -
-
-
- - ( -
- Oops! We've recognized some glitch in this HTML. Please give it another look! -
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + {isEditOverlayVisible && } + ( +
+ Oops! We've recognized some glitch in this HTML. Please give it another look! +
+ )} + resetKeys={[content]} + > + + <> + +
+ {error && error.template?.content && error.template?.content?.message && ( + {error?.template?.content?.message} )} - resetKeys={[content]} - > - - <> - - -
+ +
); diff --git a/apps/web/src/pages/templates/editor/PreviewUserIcon.tsx b/apps/web/src/pages/templates/editor/PreviewUserIcon.tsx index a9ec055729e..ea08468539e 100644 --- a/apps/web/src/pages/templates/editor/PreviewUserIcon.tsx +++ b/apps/web/src/pages/templates/editor/PreviewUserIcon.tsx @@ -1,16 +1,11 @@ -import { useMantineTheme } from '@mantine/core'; -import { colors } from '@novu/design-system'; - -/* eslint-disable */ -export const PreviewUserIcon = () => { - const theme = useMantineTheme(); - +/* eslint-disable max-len */ +export const PreviewUserIcon = (props) => { return ( - - + + ); diff --git a/apps/web/src/pages/templates/editor/PreviewWeb.tsx b/apps/web/src/pages/templates/editor/PreviewWeb.tsx index 4b05b5401a0..7d8351af0f3 100644 --- a/apps/web/src/pages/templates/editor/PreviewWeb.tsx +++ b/apps/web/src/pages/templates/editor/PreviewWeb.tsx @@ -1,36 +1,39 @@ -import { Center, createStyles, Group, Loader } from '@mantine/core'; -import { format } from 'date-fns'; -import { colors } from '@novu/design-system'; -import { PreviewDateIcon } from './PreviewDateIcon'; -import { PreviewUserIcon } from './PreviewUserIcon'; -import Frame from 'react-frame-component'; +import { createStyles, Group, Skeleton, Stack } from '@mantine/core'; +import { colors, Text } from '@novu/design-system'; +import { useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; +import Frame from 'react-frame-component'; +import { FieldError, FieldErrorsImpl, Merge } from 'react-hook-form'; import { When } from '../../../components/utils/When'; +import { IFormStep } from '../components/formTypes'; import { EmailIntegrationInfo } from './EmailIntegrationInfo'; +import { PreviewEditOverlay } from './PreviewEditOverlay'; +import { PreviewUserIcon } from './PreviewUserIcon'; -const useStyles = createStyles((theme) => ({ +const useStyles = createStyles((theme, { error }: { error: boolean }) => ({ browser: { - background: theme.colorScheme === 'dark' ? colors.B17 : colors.B98, - borderRadius: '7px', + backgroundColor: theme.colorScheme === 'dark' ? colors.B15 : colors.B98, + borderRadius: '8px', + height: '95%', + minHeight: '50vh', }, bar: { - borderRadius: '7px 7px 0 0', - background: theme.colorScheme === 'dark' ? colors.B20 : colors.B85, + borderRadius: '8px 8px 0 0', + backgroundColor: theme.colorScheme === 'dark' ? colors.B20 : colors.B85, width: '100%', - height: '45px', - maxHeight: '45px', + height: '28px', + display: 'flex', + alignItems: 'center', + padding: '12px', }, barAction: { - height: '10px', - width: '10px', - borderRadius: '10px', - background: theme.colorScheme === 'dark' ? colors.B17 : colors.B98, + height: '8px', + width: '8px', + borderRadius: '50%', + backgroundColor: theme.colorScheme === 'dark' ? colors.B17 : colors.B98, }, header: { width: '100%', - paddingLeft: 10, - paddingRight: 10, - marginTop: '40px', }, subject: { marginBottom: '3px', @@ -40,24 +43,26 @@ const useStyles = createStyles((theme) => ({ color: theme.colorScheme === 'dark' ? colors.B60 : colors.B40, fontWeight: 'normal', }, - date: { - height: '20px', - marginTop: '20px', - color: theme.colorScheme === 'dark' ? colors.B60 : colors.B40, - fontWeight: 'normal', - }, content: { - borderRadius: '7px 7px 0 0', - marginLeft: 10, - marginRight: 10, - height: '50vh', - background: theme.colorScheme === 'dark' ? colors.B15 : colors.white, - marginTop: '20px', + borderRadius: '8px', + backgroundColor: theme.colorScheme === 'dark' ? colors.B20 : colors.white, + flex: 1, + border: error ? `1px solid ${colors.error}` : 'none', + position: 'relative', + }, + contentContainer: { + padding: '24px', + paddingBottom: '32px', + height: 'calc(100% - 28px)', + display: 'flex', + flexDirection: 'column', + gap: '16px', }, frame: { border: '0px', width: '100%', height: '100%', + borderRadius: '8px', }, fallbackFrame: { border: '0px', @@ -73,92 +78,127 @@ export const PreviewWeb = ({ subject, content, loading = false, + error, + showEditOverlay = false, }: { integration: any; subject?: string; content: string; loading?: boolean; + error?: Merge>; + showEditOverlay?: boolean; }) => { - const { classes } = useStyles(); + const { classes } = useStyles({ error: !!(error && error.template?.content && error.template?.content?.message) }); + + const [isEditOverlayVisible, setIsEditOverlayVisible] = useState(false); + + const handleMouseEnter = () => { + if (showEditOverlay) { + setIsEditOverlayVisible(true); + } + }; + + const handleMouseLeave = () => { + if (showEditOverlay && isEditOverlayVisible) { + setIsEditOverlayVisible(false); + } + }; return ( <>
- +
-
- -
- +
+
+ + + + + + + + +
-
- {subject} -
-
- -
-
-
-
-
-
- - - {format(new Date(), 'EEE, MMM d, HH:mm')} - -
-
- - -
-
- -
-
-
-
- -
- ( -
- Oops! We've recognized some glitch in this HTML. Please give it another look! + {error && error.template?.subject && error.template?.subject?.message ? ( + {error.template.subject.message} + ) : ( + <> +
+ {subject} +
+
+ +
+ + )}
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + {isEditOverlayVisible && } + + ( +
+ + Oops! We've recognized some glitch in this HTML. Please give it another look! + +
+ )} + resetKeys={[content]} + > + + <> + +
+ + {error && error.template?.content && error.template?.content?.message && ( + {error?.template?.content?.message} )} - resetKeys={[content]} - > - - <> - - +
-
+
); From 62164cc223b24fcc77808fae77fe2cb6fff7b72e Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Thu, 1 Feb 2024 11:08:16 +0530 Subject: [PATCH 02/28] feat: add custom locale support --- .../content-templates.controller.ts | 24 +++++++++++++++---- apps/web/src/api/content-templates.ts | 18 +++++++++++--- .../compile-email-template.command.ts | 4 ++++ .../compile-email-template.usecase.ts | 4 +++- .../compile-in-app-template.command.ts | 6 ++++- .../compile-in-app-template.usecase.ts | 4 +++- .../compile-step-template.command.ts | 6 ++++- .../compile-step-template.usecase.ts | 4 +++- 8 files changed, 57 insertions(+), 13 deletions(-) diff --git a/apps/api/src/app/content-templates/content-templates.controller.ts b/apps/api/src/app/content-templates/content-templates.controller.ts index e9b9146d118..0bdae1d8214 100644 --- a/apps/api/src/app/content-templates/content-templates.controller.ts +++ b/apps/api/src/app/content-templates/content-templates.controller.ts @@ -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({ @@ -46,6 +47,7 @@ export class ContentTemplatesController { payload, subject, layoutId, + locale, }), this.initiateTranslations.bind(this) ); @@ -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({ @@ -66,13 +69,19 @@ 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, @@ -80,6 +89,7 @@ export class ContentTemplatesController { environmentId: user.environmentId, content, payload, + locale, }), this.initiateTranslations.bind(this) ); @@ -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({ @@ -98,6 +109,7 @@ export class ContentTemplatesController { environmentId: user.environmentId, content, payload, + locale, }), this.initiateTranslations.bind(this) ); @@ -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({ @@ -116,6 +129,7 @@ export class ContentTemplatesController { environmentId: user.environmentId, content, payload, + locale, }), this.initiateTranslations.bind(this) ); diff --git a/apps/web/src/api/content-templates.ts b/apps/web/src/api/content-templates.ts index 4aebf00c119..35d54603eb7 100644 --- a/apps/web/src/api/content-templates.ts +++ b/apps/web/src/api/content-templates.ts @@ -7,16 +7,28 @@ 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 }); } diff --git a/packages/application-generic/src/usecases/compile-email-template/compile-email-template.command.ts b/packages/application-generic/src/usecases/compile-email-template/compile-email-template.command.ts index 2a12049b09d..1e26222cd65 100644 --- a/packages/application-generic/src/usecases/compile-email-template/compile-email-template.command.ts +++ b/packages/application-generic/src/usecases/compile-email-template/compile-email-template.command.ts @@ -28,4 +28,8 @@ export class CompileEmailTemplateCommand extends EnvironmentWithUserCommand { @IsString() @IsOptional() senderName?: string | null; + + @IsString() + @IsOptional() + locale?: string; } diff --git a/packages/application-generic/src/usecases/compile-email-template/compile-email-template.usecase.ts b/packages/application-generic/src/usecases/compile-email-template/compile-email-template.usecase.ts index 84a2e05002a..080a21d0083 100644 --- a/packages/application-generic/src/usecases/compile-email-template/compile-email-template.usecase.ts +++ b/packages/application-generic/src/usecases/compile-email-template/compile-email-template.usecase.ts @@ -43,7 +43,9 @@ export class CompileEmailTemplate extends CompileTemplateBase { await initiateTranslations( command.environmentId, command.organizationId, - command.payload.subscriber?.locale || organization.defaultLocale + command.locale || + command.payload.subscriber?.locale || + organization.defaultLocale ); } diff --git a/packages/application-generic/src/usecases/compile-in-app-template/compile-in-app-template.command.ts b/packages/application-generic/src/usecases/compile-in-app-template/compile-in-app-template.command.ts index 7d65b6eea44..b982b3d920c 100644 --- a/packages/application-generic/src/usecases/compile-in-app-template/compile-in-app-template.command.ts +++ b/packages/application-generic/src/usecases/compile-in-app-template/compile-in-app-template.command.ts @@ -1,4 +1,4 @@ -import { IsDefined, IsOptional } from 'class-validator'; +import { IsDefined, IsOptional, IsString } from 'class-validator'; import { IMessageCTA } from '@novu/shared'; import { EnvironmentWithUserCommand } from '../../commands/project.command'; @@ -12,4 +12,8 @@ export class CompileInAppTemplateCommand extends EnvironmentWithUserCommand { @IsOptional() cta?: IMessageCTA; + + @IsString() + @IsOptional() + locale?: string; } diff --git a/packages/application-generic/src/usecases/compile-in-app-template/compile-in-app-template.usecase.ts b/packages/application-generic/src/usecases/compile-in-app-template/compile-in-app-template.usecase.ts index 7145b6c5310..5a52f083f7d 100644 --- a/packages/application-generic/src/usecases/compile-in-app-template/compile-in-app-template.usecase.ts +++ b/packages/application-generic/src/usecases/compile-in-app-template/compile-in-app-template.usecase.ts @@ -35,7 +35,9 @@ export class CompileInAppTemplate extends CompileTemplateBase { await initiateTranslations( command.environmentId, command.organizationId, - command.payload.subscriber?.locale || organization.defaultLocale + command.locale || + command.payload.subscriber?.locale || + organization.defaultLocale ); } diff --git a/packages/application-generic/src/usecases/compile-step-template/compile-step-template.command.ts b/packages/application-generic/src/usecases/compile-step-template/compile-step-template.command.ts index 4947dfef0b7..879ab7532d2 100644 --- a/packages/application-generic/src/usecases/compile-step-template/compile-step-template.command.ts +++ b/packages/application-generic/src/usecases/compile-step-template/compile-step-template.command.ts @@ -1,4 +1,4 @@ -import { IsDefined } from 'class-validator'; +import { IsDefined, IsOptional, IsString } from 'class-validator'; import { EnvironmentWithUserCommand } from '../../commands/project.command'; @@ -8,4 +8,8 @@ export class CompileStepTemplateCommand extends EnvironmentWithUserCommand { @IsDefined() payload: any; // eslint-disable-line @typescript-eslint/no-explicit-any + + @IsString() + @IsOptional() + locale?: string; } diff --git a/packages/application-generic/src/usecases/compile-step-template/compile-step-template.usecase.ts b/packages/application-generic/src/usecases/compile-step-template/compile-step-template.usecase.ts index 8a578b5d7ff..f1ba936caa9 100644 --- a/packages/application-generic/src/usecases/compile-step-template/compile-step-template.usecase.ts +++ b/packages/application-generic/src/usecases/compile-step-template/compile-step-template.usecase.ts @@ -34,7 +34,9 @@ export class CompileStepTemplate extends CompileTemplateBase { await initiateTranslations( command.environmentId, command.organizationId, - command.payload.subscriber?.locale || organization.defaultLocale + command.locale || + command.payload.subscriber?.locale || + organization.defaultLocale ); } From 66ba987102fd902f693c1052b365235fde091f57 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Thu, 1 Feb 2024 11:09:16 +0530 Subject: [PATCH 03/28] feat: add locale selection state --- .../web/src/pages/templates/editor/Preview.tsx | 18 ++++++++++++++++-- .../templates/editor/PreviewMobile.cy.tsx | 2 ++ .../pages/templates/editor/PreviewMobile.tsx | 2 ++ .../pages/templates/editor/PreviewWeb.cy.tsx | 2 ++ .../src/pages/templates/editor/PreviewWeb.tsx | 2 ++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/apps/web/src/pages/templates/editor/Preview.tsx b/apps/web/src/pages/templates/editor/Preview.tsx index 38f19300f23..56fb8930bfc 100644 --- a/apps/web/src/pages/templates/editor/Preview.tsx +++ b/apps/web/src/pages/templates/editor/Preview.tsx @@ -7,7 +7,7 @@ import type { IEmailBlock, MessageTemplateContentType } from '@novu/shared'; import { previewEmail } from '../../../api/content-templates'; import { When } from '../../../components/utils/When'; import { Button, colors, inputStyles } from '@novu/design-system'; -import { useProcessVariables } from '../../../hooks'; +import { useAuthController, useProcessVariables } from '../../../hooks'; import { PreviewMobile } from './PreviewMobile'; import { PreviewWeb } from './PreviewWeb'; import { errorMessage } from '../../../utils/notifications'; @@ -54,6 +54,13 @@ export const Preview = ({ showVariables = true, view }: { view: string; showVari const processedVariables = useProcessVariables(variables); const [payloadValue, setPayloadValue] = useState('{}'); + const { organization } = useAuthController(); + const [selectedLocale, setSelectedLocale] = useState(undefined); + + useEffect(() => { + setSelectedLocale(organization?.defaultLocale); + }, [organization?.defaultLocale]); + useEffect(() => { setPayloadValue(processedVariables); }, [processedVariables, setPayloadValue]); @@ -63,6 +70,7 @@ export const Preview = ({ showVariables = true, view }: { view: string; showVari content?: string | IEmailBlock[]; payload: any; layoutId?: string; + locale?: string; }) => { mutateAsync({ ...args, @@ -86,9 +94,10 @@ export const Preview = ({ showVariables = true, view }: { view: string; showVari content: contentType === 'editor' ? editorContent : htmlContent, payload: processedVariables, layoutId, + locale: selectedLocale, }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [contentType, htmlContent, editorContent, processedVariables]); + }, [contentType, htmlContent, editorContent, processedVariables, selectedLocale]); const theme = useMantineTheme(); useEffect(() => { @@ -98,6 +107,8 @@ export const Preview = ({ showVariables = true, view }: { view: string; showVari setIntegration(integrations.find((item) => item.channel === 'email' && item.primary) || null); }, [integrations, setIntegration]); + console.log(selectedLocale, organization); + return ( <> {showVariables ? ( @@ -112,6 +123,7 @@ export const Preview = ({ showVariables = true, view }: { view: string; showVari integration={integration} error={error} showEditOverlay={!showVariables} + setSelectedLocale={setSelectedLocale} /> @@ -123,6 +135,7 @@ export const Preview = ({ showVariables = true, view }: { view: string; showVari content={content} integration={integration} error={error} + setSelectedLocale={setSelectedLocale} /> @@ -180,6 +193,7 @@ export const Preview = ({ showVariables = true, view }: { view: string; showVari integration={integration} error={error} showEditOverlay={!showVariables} + setSelectedLocale={setSelectedLocale} /> )} diff --git a/apps/web/src/pages/templates/editor/PreviewMobile.cy.tsx b/apps/web/src/pages/templates/editor/PreviewMobile.cy.tsx index 437f9bab567..298910478ef 100644 --- a/apps/web/src/pages/templates/editor/PreviewMobile.cy.tsx +++ b/apps/web/src/pages/templates/editor/PreviewMobile.cy.tsx @@ -23,6 +23,7 @@ describe('Preview mobile', () => { from: 'novu@novu', }, }} + setSelectedLocale={() => {}} /> @@ -50,6 +51,7 @@ describe('Preview mobile', () => { from: 'novu@novu', }, }} + setSelectedLocale={() => {}} /> diff --git a/apps/web/src/pages/templates/editor/PreviewMobile.tsx b/apps/web/src/pages/templates/editor/PreviewMobile.tsx index 97b58329e34..939f15e5404 100644 --- a/apps/web/src/pages/templates/editor/PreviewMobile.tsx +++ b/apps/web/src/pages/templates/editor/PreviewMobile.tsx @@ -61,6 +61,7 @@ export const PreviewMobile = ({ loading = false, error, showEditOverlay = false, + setSelectedLocale, }: { integration: any; subject?: string; @@ -68,6 +69,7 @@ export const PreviewMobile = ({ loading?: boolean; error?: Merge>; showEditOverlay?: boolean; + setSelectedLocale: (locale: string) => void; }) => { const { classes } = useStyles({ error: !!(error && error.template?.content && error.template?.content?.message) }); diff --git a/apps/web/src/pages/templates/editor/PreviewWeb.cy.tsx b/apps/web/src/pages/templates/editor/PreviewWeb.cy.tsx index c506b130e19..282af30bd78 100644 --- a/apps/web/src/pages/templates/editor/PreviewWeb.cy.tsx +++ b/apps/web/src/pages/templates/editor/PreviewWeb.cy.tsx @@ -23,6 +23,7 @@ describe('Preview web', () => { from: 'novu@novu', }, }} + setSelectedLocale={() => {}} /> @@ -50,6 +51,7 @@ describe('Preview web', () => { from: 'novu@novu', }, }} + setSelectedLocale={() => {}} /> diff --git a/apps/web/src/pages/templates/editor/PreviewWeb.tsx b/apps/web/src/pages/templates/editor/PreviewWeb.tsx index 7d8351af0f3..85acae946bc 100644 --- a/apps/web/src/pages/templates/editor/PreviewWeb.tsx +++ b/apps/web/src/pages/templates/editor/PreviewWeb.tsx @@ -80,6 +80,7 @@ export const PreviewWeb = ({ loading = false, error, showEditOverlay = false, + setSelectedLocale, }: { integration: any; subject?: string; @@ -87,6 +88,7 @@ export const PreviewWeb = ({ loading?: boolean; error?: Merge>; showEditOverlay?: boolean; + setSelectedLocale: (locale: string) => void; }) => { const { classes } = useStyles({ error: !!(error && error.template?.content && error.template?.content?.message) }); From 2dafcc2b851fe7701e7ad9570ccf98870a3dd485 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Thu, 1 Feb 2024 11:10:01 +0530 Subject: [PATCH 04/28] feat: remove log --- apps/web/src/pages/templates/editor/Preview.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/web/src/pages/templates/editor/Preview.tsx b/apps/web/src/pages/templates/editor/Preview.tsx index 56fb8930bfc..667f0abbaae 100644 --- a/apps/web/src/pages/templates/editor/Preview.tsx +++ b/apps/web/src/pages/templates/editor/Preview.tsx @@ -107,8 +107,6 @@ export const Preview = ({ showVariables = true, view }: { view: string; showVari setIntegration(integrations.find((item) => item.channel === 'email' && item.primary) || null); }, [integrations, setIntegration]); - console.log(selectedLocale, organization); - return ( <> {showVariables ? ( From f58e97094f3a15a0371da78bf083b40307374c3c Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Sat, 3 Feb 2024 23:18:08 +0530 Subject: [PATCH 05/28] feat: added locale dropdown --- apps/web/src/api/translations.ts | 5 ++ .../pages/templates/editor/LocaleSelect.tsx | 59 +++++++++++++++++++ .../src/pages/templates/editor/Preview.tsx | 32 +++++++--- .../templates/editor/PreviewMobile.cy.tsx | 2 + .../pages/templates/editor/PreviewMobile.tsx | 13 ++++ .../pages/templates/editor/PreviewWeb.cy.tsx | 2 + .../src/pages/templates/editor/PreviewWeb.tsx | 13 ++++ libs/design-system/src/select/Select.tsx | 1 + 8 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 apps/web/src/api/translations.ts create mode 100644 apps/web/src/pages/templates/editor/LocaleSelect.tsx diff --git a/apps/web/src/api/translations.ts b/apps/web/src/api/translations.ts new file mode 100644 index 00000000000..ba7473442a6 --- /dev/null +++ b/apps/web/src/api/translations.ts @@ -0,0 +1,5 @@ +import { api } from './api.client'; + +export async function getLocalesFromContent({ content }) { + return api.post('/v1/translations/groups/preview/locales', { content }); +} diff --git a/apps/web/src/pages/templates/editor/LocaleSelect.tsx b/apps/web/src/pages/templates/editor/LocaleSelect.tsx new file mode 100644 index 00000000000..fd8ba02fdc7 --- /dev/null +++ b/apps/web/src/pages/templates/editor/LocaleSelect.tsx @@ -0,0 +1,59 @@ +import { SelectItemProps, Group } from '@mantine/core'; +import { Select, Text } from '@novu/design-system'; +import { forwardRef } from 'react'; +import { IS_DOCKER_HOSTED } from '../../../config'; + +export function LocaleSelect({ locales, value, isLoading, readonly, setSelectedLocale }) { + if (IS_DOCKER_HOSTED) { + return null; + } + + return ( +
+ { + return { + value: locale.langIso, + label: locale.langName, + }; + })} + icon={} + loading={isLoading} + limit={50} + searchable + withinPortal + disabled={readonly} + onChange={(val) => { + setSelectedLocale(val); + }} + value={value} + variant="unstyled" + rightSectionWidth={20} + /> +
+ ); +} + +const SelectItem = forwardRef(({ label, value, ...others }: SelectItemProps, ref) => { + return ( + + + {label} + + ); +}); + +export const FlagIcon = ({ locale }) => { + if (IS_DOCKER_HOSTED) { + return null; + } + + try { + const module = require('@novu/ee-translation-web'); + + return module.FlagIcon({ locale }); + } catch (e) {} + + return []; +}; From 097ea7d4f79850980f9cecd1b19c9d1b99efc846 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Sun, 4 Feb 2024 00:18:47 +0530 Subject: [PATCH 09/28] feat: implemented chat preview foundation --- .../workflow/Preview/Email/PreviewMobile.tsx | 1 - .../workflow/Preview/Email/PreviewWeb.tsx | 1 - .../workflow/Preview/Email/index.ts | 1 + .../workflow/Preview/chat/ChatContent.tsx | 50 +++++++++++++++ .../workflow/Preview/chat/ChatInput.tsx | 29 +++++++++ .../workflow/Preview/chat/ChatPreview.tsx | 61 +++++++++++++++++++ .../components/workflow/Preview/chat/index.ts | 1 + .../workflow/Preview/common/EmojiIcon.tsx | 24 ++++++++ .../workflow/Preview/common/LocaleSelect.tsx | 5 +- .../workflow/Preview/common/NovuGreyIcon.tsx | 19 ++++++ .../workflow/Preview/common/SendIcon.tsx | 21 +++++++ .../workflow/Preview/common/index.ts | 8 +++ .../src/components/workflow/Preview/index.ts | 2 + .../templates/components/ChannelPreview.tsx | 4 +- .../email-editor/EmailMessagesCards.tsx | 6 +- 15 files changed, 223 insertions(+), 10 deletions(-) create mode 100644 apps/web/src/components/workflow/Preview/Email/index.ts create mode 100644 apps/web/src/components/workflow/Preview/chat/ChatContent.tsx create mode 100644 apps/web/src/components/workflow/Preview/chat/ChatInput.tsx create mode 100644 apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx create mode 100644 apps/web/src/components/workflow/Preview/chat/index.ts create mode 100644 apps/web/src/components/workflow/Preview/common/EmojiIcon.tsx create mode 100644 apps/web/src/components/workflow/Preview/common/NovuGreyIcon.tsx create mode 100644 apps/web/src/components/workflow/Preview/common/SendIcon.tsx create mode 100644 apps/web/src/components/workflow/Preview/common/index.ts create mode 100644 apps/web/src/components/workflow/Preview/index.ts diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx index 6397fd2593e..15e3e693caa 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx @@ -140,7 +140,6 @@ export const PreviewMobile = ({ diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx index 6bad6278212..961b1081c3e 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx @@ -157,7 +157,6 @@ export const PreviewWeb = ({ diff --git a/apps/web/src/components/workflow/Preview/Email/index.ts b/apps/web/src/components/workflow/Preview/Email/index.ts new file mode 100644 index 00000000000..364e1ec431b --- /dev/null +++ b/apps/web/src/components/workflow/Preview/Email/index.ts @@ -0,0 +1 @@ +export * from './EmailPreview'; diff --git a/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx b/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx new file mode 100644 index 00000000000..3f5d3ea8cb2 --- /dev/null +++ b/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx @@ -0,0 +1,50 @@ +import styled from '@emotion/styled'; +import { Group, Stack } from '@mantine/core'; +import { colors, Text, Title } from '@novu/design-system'; +import React from 'react'; +import { NovuGreyIcon } from '../common'; + +export function ChatContent() { + return ( + +
+ +
+ + + + Your App + + APP + now + + + Lorem ipsum dolor sit amet consectetur, adipisicing elit. Ipsam, quae. Est voluptas exercitationem nobis optio + eos incidunt delectus deleniti omnis cum ipsa assumenda veritatis dolor provident voluptatibus, quisquam, + laudantium illo! Explicabo molestias vel ea quia placeat, ducimus facere labore repellendus earum veniam + voluptatibus soluta quos, temporibus dicta fugiat aut perferendis mollitia sapiente eaque laboriosam? Quidem + earum porro fuga nesciunt blanditiis! + + +
+ ); +} + +const PillStyled = styled.div` + background-color: ${colors.B40}; + border-radius: 4px; + display-flex; + padding: 0px 6px; + align-items: center; + color: ${colors.B80}; + font-size: 10px; + font-weight: 400; + line-height: 20px; +`; diff --git a/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx b/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx new file mode 100644 index 00000000000..40be7f26744 --- /dev/null +++ b/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx @@ -0,0 +1,29 @@ +import styled from '@emotion/styled'; +import { Group } from '@mantine/core'; +import { colors, Text } from '@novu/design-system'; +import { EmojiIcon, SendIcon } from '../common'; + +export function ChatInput() { + return ( + + Message Bot + + + + + + ); +} + +const InputStyled = styled.div` + display: flex; + padding: 16px; + justify-content: space-between; + align-items: center; + align-self: stretch; + + border-radius: 8px; + border: 1px solid ${colors.B40}; + opacity: 0.2; + background: ${colors.B17}; +`; diff --git a/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx b/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx new file mode 100644 index 00000000000..c78f706cb02 --- /dev/null +++ b/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx @@ -0,0 +1,61 @@ +import { Divider, Stack } from '@mantine/core'; +import { colors, Text } from '@novu/design-system'; +import { useAuthController } from '@novu/shared-web'; +import { useMutation } from '@tanstack/react-query'; +import React, { useEffect, useState } from 'react'; +import { useFormContext, useWatch } from 'react-hook-form'; +import { getLocalesFromContent } from '../../../../api/translations'; +import { IForm } from '../../../../pages/templates/components/formTypes'; +import { useStepFormErrors } from '../../../../pages/templates/hooks/useStepFormErrors'; +import { useStepFormPath } from '../../../../pages/templates/hooks/useStepFormPath'; +import { LocaleSelect } from '../common'; +import { ChatContent } from './ChatContent'; +import { ChatInput } from './ChatInput'; + +export function ChatPreview() { + const { organization } = useAuthController(); + const [selectedLocale, setSelectedLocale] = useState(undefined); + const { mutateAsync: translationLocales, data: locales } = useMutation(getLocalesFromContent); + + const { control } = useFormContext(); + const path = useStepFormPath(); + const error = useStepFormErrors(); + + const content = useWatch({ + name: `${path}.template.content`, + control, + }); + + useEffect(() => { + setSelectedLocale(organization?.defaultLocale); + }, [organization?.defaultLocale]); + + useEffect(() => { + if (content) { + translationLocales({ + content, + }); + } + }, [content, translationLocales]); + + return ( +
+ + + Today + + } + labelPosition="center" + /> + + +
+ ); +} diff --git a/apps/web/src/components/workflow/Preview/chat/index.ts b/apps/web/src/components/workflow/Preview/chat/index.ts new file mode 100644 index 00000000000..4fdde8ad750 --- /dev/null +++ b/apps/web/src/components/workflow/Preview/chat/index.ts @@ -0,0 +1 @@ +export * from './ChatPreview'; diff --git a/apps/web/src/components/workflow/Preview/common/EmojiIcon.tsx b/apps/web/src/components/workflow/Preview/common/EmojiIcon.tsx new file mode 100644 index 00000000000..023dceef392 --- /dev/null +++ b/apps/web/src/components/workflow/Preview/common/EmojiIcon.tsx @@ -0,0 +1,24 @@ +/* eslint-disable max-len */ +export const EmojiIcon = (props) => { + return ( + + + + + + + + + ); +}; diff --git a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx index 0d6a99ccd66..54b13925958 100644 --- a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx +++ b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx @@ -3,8 +3,8 @@ import { Select, Text } from '@novu/design-system'; import { forwardRef } from 'react'; import { IS_DOCKER_HOSTED } from '../../../../config'; -export function LocaleSelect({ locales, value, isLoading, readonly, setSelectedLocale }) { - if (IS_DOCKER_HOSTED) { +export function LocaleSelect({ locales, value, isLoading, setSelectedLocale }) { + if (IS_DOCKER_HOSTED || locales.length === 0) { return null; } @@ -23,7 +23,6 @@ export function LocaleSelect({ locales, value, isLoading, readonly, setSelectedL limit={50} searchable withinPortal - disabled={readonly} onChange={(val) => { setSelectedLocale(val); }} diff --git a/apps/web/src/components/workflow/Preview/common/NovuGreyIcon.tsx b/apps/web/src/components/workflow/Preview/common/NovuGreyIcon.tsx new file mode 100644 index 00000000000..cabd3d6ac66 --- /dev/null +++ b/apps/web/src/components/workflow/Preview/common/NovuGreyIcon.tsx @@ -0,0 +1,19 @@ +/* eslint-disable max-len */ +export const NovuGreyIcon = (props) => { + return ( + + + + + + ); +}; diff --git a/apps/web/src/components/workflow/Preview/common/SendIcon.tsx b/apps/web/src/components/workflow/Preview/common/SendIcon.tsx new file mode 100644 index 00000000000..a0bce8de46e --- /dev/null +++ b/apps/web/src/components/workflow/Preview/common/SendIcon.tsx @@ -0,0 +1,21 @@ +/* eslint-disable max-len */ +export const SendIcon = (props) => { + return ( + + + + + + + + + ); +}; diff --git a/apps/web/src/components/workflow/Preview/common/index.ts b/apps/web/src/components/workflow/Preview/common/index.ts new file mode 100644 index 00000000000..779e9d18762 --- /dev/null +++ b/apps/web/src/components/workflow/Preview/common/index.ts @@ -0,0 +1,8 @@ +export * from './LocaleSelect'; +export * from './Mobile'; +export * from './PreviewDateIcon'; +export * from './PreviewEditOverlay'; +export * from './PreviewUserIcon'; +export * from './NovuGreyIcon'; +export * from './EmojiIcon'; +export * from './SendIcon'; diff --git a/apps/web/src/components/workflow/Preview/index.ts b/apps/web/src/components/workflow/Preview/index.ts new file mode 100644 index 00000000000..0d3ec48a45c --- /dev/null +++ b/apps/web/src/components/workflow/Preview/index.ts @@ -0,0 +1,2 @@ +export * from './chat'; +export * from './email'; diff --git a/apps/web/src/pages/templates/components/ChannelPreview.tsx b/apps/web/src/pages/templates/components/ChannelPreview.tsx index f982a816d63..f4bd09be53f 100644 --- a/apps/web/src/pages/templates/components/ChannelPreview.tsx +++ b/apps/web/src/pages/templates/components/ChannelPreview.tsx @@ -1,7 +1,7 @@ import { StepTypeEnum } from '@novu/shared'; import { useParams } from 'react-router-dom'; +import { ChatPreview, EmailPreview } from '../../../components/workflow/Preview'; -import { EmailPreview } from '../../../components/workflow/Preview/Email/EmailPreview'; import { useNavigateFromEditor } from '../hooks/useNavigateFromEditor'; import { useStepIndex } from '../hooks/useStepIndex'; import { ChannelPreviewSidebar } from './ChannelPreviewSidebar'; @@ -21,7 +21,7 @@ const PreviewComponent = ({ channel }: { channel: StepTypeEnum }) => { return <>IN APP; case StepTypeEnum.CHAT: - return <>CHAT; + return ; case StepTypeEnum.PUSH: return <>PUSH; diff --git a/apps/web/src/pages/templates/components/email-editor/EmailMessagesCards.tsx b/apps/web/src/pages/templates/components/email-editor/EmailMessagesCards.tsx index 94cdecf585b..0212535da09 100644 --- a/apps/web/src/pages/templates/components/email-editor/EmailMessagesCards.tsx +++ b/apps/web/src/pages/templates/components/email-editor/EmailMessagesCards.tsx @@ -2,13 +2,13 @@ import { useState } from 'react'; import { EmailContentCard } from './EmailContentCard'; import { useAuthContext } from '../../../../components/providers/AuthProvider'; import { When } from '../../../../components/utils/When'; -import { EmailPreview } from '../../../../components/workflow/Preview/Email/EmailPreview'; +import { EmailPreview } from '../../../../components/workflow/Preview/email/EmailPreview'; import { EditorPreviewSwitch } from '../EditorPreviewSwitch'; import { Grid, SegmentedControl, useMantineTheme } from '@mantine/core'; import { TestSendEmail } from './TestSendEmail'; import { colors } from '@novu/design-system'; -import { MobileIcon } from '../../../../components/workflow/Preview/Email/PreviewSegment/MobileIcon'; -import { WebIcon } from '../../../../components/workflow/Preview/Email/PreviewSegment/WebIcon'; +import { MobileIcon } from '../../../../components/workflow/Preview/email/PreviewSegment/MobileIcon'; +import { WebIcon } from '../../../../components/workflow/Preview/email/PreviewSegment/WebIcon'; import { useHotkeys } from '@mantine/hooks'; import { VariablesManagement } from './variables-management/VariablesManagement'; import { From e4cc4bc93908c9018895feb5e9c1a7f13ae6b96a Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Sun, 4 Feb 2024 00:24:25 +0530 Subject: [PATCH 10/28] feat: update locale select --- .../workflow/Preview/Email/PreviewMobile.tsx | 14 +++--- .../workflow/Preview/Email/PreviewWeb.tsx | 15 ++++--- .../workflow/Preview/chat/ChatContent.tsx | 10 +---- .../workflow/Preview/chat/ChatPreview.tsx | 20 +++++---- .../workflow/Preview/common/LocaleSelect.tsx | 43 +++++++++---------- 5 files changed, 50 insertions(+), 52 deletions(-) diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx index 15e3e693caa..9eda50804be 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx @@ -137,12 +137,14 @@ export const PreviewMobile = ({ )}
- +
+ +
diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx index 961b1081c3e..16263c2ac60 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx @@ -153,13 +153,14 @@ export const PreviewWeb = ({ )} - - +
+ +
diff --git a/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx b/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx index 3f5d3ea8cb2..2de0b1e38f1 100644 --- a/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx +++ b/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx @@ -4,7 +4,7 @@ import { colors, Text, Title } from '@novu/design-system'; import React from 'react'; import { NovuGreyIcon } from '../common'; -export function ChatContent() { +export function ChatContent({ content }) { return (
@@ -25,13 +25,7 @@ export function ChatContent() { APP now - - Lorem ipsum dolor sit amet consectetur, adipisicing elit. Ipsam, quae. Est voluptas exercitationem nobis optio - eos incidunt delectus deleniti omnis cum ipsa assumenda veritatis dolor provident voluptatibus, quisquam, - laudantium illo! Explicabo molestias vel ea quia placeat, ducimus facere labore repellendus earum veniam - voluptatibus soluta quos, temporibus dicta fugiat aut perferendis mollitia sapiente eaque laboriosam? Quidem - earum porro fuga nesciunt blanditiis! - + {content} ); diff --git a/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx b/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx index c78f706cb02..74ed6c6f005 100644 --- a/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx +++ b/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx @@ -1,8 +1,8 @@ -import { Divider, Stack } from '@mantine/core'; +import { Divider } from '@mantine/core'; import { colors, Text } from '@novu/design-system'; import { useAuthController } from '@novu/shared-web'; import { useMutation } from '@tanstack/react-query'; -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; import { getLocalesFromContent } from '../../../../api/translations'; import { IForm } from '../../../../pages/templates/components/formTypes'; @@ -40,12 +40,14 @@ export function ChatPreview() { return (
- +
+ +
@@ -54,7 +56,7 @@ export function ChatPreview() { } labelPosition="center" /> - +
); diff --git a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx index 54b13925958..0a8a2feca58 100644 --- a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx +++ b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx @@ -9,28 +9,27 @@ export function LocaleSelect({ locales, value, isLoading, setSelectedLocale }) { } return ( -
- { + return { + value: locale.langIso, + label: locale.langName, + }; + })} + icon={} + loading={isLoading} + limit={50} + searchable + withinPortal + onChange={(val) => { + setSelectedLocale(val); + }} + value={value} + variant="unstyled" + rightSectionWidth={20} + /> ); } From d26128d1d1ea7c1a31f95e4a326d002682e23dee Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Sun, 4 Feb 2024 00:26:44 +0530 Subject: [PATCH 11/28] feat: error --- .../src/components/workflow/Preview/chat/ChatContent.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx b/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx index 2de0b1e38f1..7c7836adfa7 100644 --- a/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx +++ b/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx @@ -4,7 +4,7 @@ import { colors, Text, Title } from '@novu/design-system'; import React from 'react'; import { NovuGreyIcon } from '../common'; -export function ChatContent({ content }) { +export function ChatContent({ content, error }) { return (
@@ -25,7 +25,11 @@ export function ChatContent({ content }) { APP now - {content} + {error && error.template?.content && error.template?.content?.message ? ( + {error.template.content.message} + ) : ( + {content} + )} ); From 81e6e6bd89ad06beaea1afeacdc5ab379f4d30d8 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Sun, 4 Feb 2024 00:27:12 +0530 Subject: [PATCH 12/28] fix: error --- apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx b/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx index 74ed6c6f005..645e222e546 100644 --- a/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx +++ b/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx @@ -56,7 +56,7 @@ export function ChatPreview() { } labelPosition="center" /> - +
); From 6d3c1220d61f9628e35a2938df87bc90ccdc66a8 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Sun, 4 Feb 2024 00:37:59 +0530 Subject: [PATCH 13/28] feat: overlay --- .../workflow/Preview/chat/ChatContent.tsx | 32 ++++++++++++++++--- .../workflow/Preview/chat/ChatInput.tsx | 1 + .../Preview/common/PreviewEditOverlay.tsx | 12 +++++-- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx b/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx index 7c7836adfa7..ae953b66d24 100644 --- a/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx +++ b/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx @@ -1,12 +1,34 @@ import styled from '@emotion/styled'; import { Group, Stack } from '@mantine/core'; -import { colors, Text, Title } from '@novu/design-system'; -import React from 'react'; -import { NovuGreyIcon } from '../common'; +import { colors, Text } from '@novu/design-system'; +import { useState } from 'react'; +import { NovuGreyIcon, PreviewEditOverlay } from '../common'; export function ChatContent({ content, error }) { + const [isEditOverlayVisible, setIsEditOverlayVisible] = useState(false); + + const handleMouseEnter = () => { + setIsEditOverlayVisible(true); + }; + + const handleMouseLeave = () => { + setIsEditOverlayVisible(false); + }; + return ( - + + {isEditOverlayVisible && }
@@ -28,7 +50,7 @@ export function ChatContent({ content, error }) { {error && error.template?.content && error.template?.content?.message ? ( {error.template.content.message} ) : ( - {content} + {content} )}
diff --git a/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx b/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx index 40be7f26744..22a2ee611b6 100644 --- a/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx +++ b/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx @@ -26,4 +26,5 @@ const InputStyled = styled.div` border: 1px solid ${colors.B40}; opacity: 0.2; background: ${colors.B17}; + margin-top: 20px; `; diff --git a/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx b/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx index 2dbdaf08c51..87fbe2a3126 100644 --- a/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx +++ b/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx @@ -1,3 +1,4 @@ +import styled from '@emotion/styled'; import { Flex, Group, Overlay, UnstyledButton } from '@mantine/core'; import { colors, PencilOutlined, Text } from '@novu/design-system'; import { StepTypeEnum } from '@novu/shared'; @@ -25,7 +26,7 @@ export function PreviewEditOverlay() { }; return ( - + @@ -34,6 +35,13 @@ export function PreviewEditOverlay() { - + ); } + +const OverlayStyled = styled(Overlay)` + backdrop-filter: blur(2px); + border-radius: 8px; + border: 1px solid ${colors.B30}; + background: rgba(41, 41, 51, 0.8); +`; From c9ca9945ec6cf6523057a2556be6b0cb7ff5dedf Mon Sep 17 00:00:00 2001 From: ainouzgali Date: Sun, 4 Feb 2024 16:33:42 +0200 Subject: [PATCH 14/28] feat: chat preview --- apps/web/src/api/content-templates.ts | 12 +++ .../workflow/Preview/chat/ChatContent.tsx | 73 +++++++++++-------- .../workflow/Preview/chat/ChatInput.tsx | 2 +- .../workflow/Preview/chat/ChatPreview.tsx | 48 ++++++++++-- .../workflow/Preview/common/NovuGreyIcon.tsx | 12 +-- .../Preview/common/PreviewEditOverlay.tsx | 2 +- .../src/components/workflow/Preview/index.ts | 2 +- .../email-editor/EmailMessagesCards.tsx | 6 +- 8 files changed, 108 insertions(+), 49 deletions(-) diff --git a/apps/web/src/api/content-templates.ts b/apps/web/src/api/content-templates.ts index 35d54603eb7..59d3760ca95 100644 --- a/apps/web/src/api/content-templates.ts +++ b/apps/web/src/api/content-templates.ts @@ -32,3 +32,15 @@ export async function previewInApp({ }) { 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 }); +} diff --git a/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx b/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx index ae953b66d24..bd6831fad79 100644 --- a/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx +++ b/apps/web/src/components/workflow/Preview/chat/ChatContent.tsx @@ -1,11 +1,14 @@ import styled from '@emotion/styled'; -import { Group, Stack } from '@mantine/core'; +import { Group, Skeleton, Stack, useMantineColorScheme } from '@mantine/core'; import { colors, Text } from '@novu/design-system'; import { useState } from 'react'; import { NovuGreyIcon, PreviewEditOverlay } from '../common'; +import { When } from '../../../utils/When'; -export function ChatContent({ content, error }) { +export function ChatContent({ isLoading, content, error }) { const [isEditOverlayVisible, setIsEditOverlayVisible] = useState(false); + const { colorScheme } = useMantineColorScheme(); + const isDark = colorScheme === 'dark'; const handleMouseEnter = () => { setIsEditOverlayVisible(true); @@ -28,42 +31,50 @@ export function ChatContent({ content, error }) { position: 'relative', }} > - {isEditOverlayVisible && } -
- -
- - - - Your App - - APP - now - - {error && error.template?.content && error.template?.content?.message ? ( - {error.template.content.message} - ) : ( - {content} - )} - + + + + + + + + + {isEditOverlayVisible && } +
+ +
+ + + + Your App + + APP + now + + {error && error.template?.content && error.template?.content?.message ? ( + {error.template.content.message} + ) : ( + {content} + )} + +
); } -const PillStyled = styled.div` - background-color: ${colors.B40}; +const PillStyled = styled.div<{ isDark: boolean }>` + background-color: ${({ isDark }) => (isDark ? colors.B40 : colors.BGLight)}; border-radius: 4px; - display-flex; padding: 0px 6px; align-items: center; - color: ${colors.B80}; + color: ${({ isDark }) => (isDark ? colors.B80 : colors.B40)}; font-size: 10px; font-weight: 400; line-height: 20px; diff --git a/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx b/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx index 22a2ee611b6..c07679926e8 100644 --- a/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx +++ b/apps/web/src/components/workflow/Preview/chat/ChatInput.tsx @@ -25,6 +25,6 @@ const InputStyled = styled.div` border-radius: 8px; border: 1px solid ${colors.B40}; opacity: 0.2; - background: ${colors.B17}; + background: transparent; margin-top: 20px; `; diff --git a/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx b/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx index 645e222e546..69ccd0aaef4 100644 --- a/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx +++ b/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx @@ -1,4 +1,4 @@ -import { Divider } from '@mantine/core'; +import { Divider, useMantineColorScheme } from '@mantine/core'; import { colors, Text } from '@novu/design-system'; import { useAuthController } from '@novu/shared-web'; import { useMutation } from '@tanstack/react-query'; @@ -11,11 +11,22 @@ import { useStepFormPath } from '../../../../pages/templates/hooks/useStepFormPa import { LocaleSelect } from '../common'; import { ChatContent } from './ChatContent'; import { ChatInput } from './ChatInput'; +import { previewChat } from '../../../../api/content-templates'; +import { errorMessage } from '../../../../utils/notifications'; export function ChatPreview() { const { organization } = useAuthController(); + const { colorScheme } = useMantineColorScheme(); + const isDark = colorScheme === 'dark'; const [selectedLocale, setSelectedLocale] = useState(undefined); - const { mutateAsync: translationLocales, data: locales } = useMutation(getLocalesFromContent); + const { isLoading, mutateAsync } = useMutation(previewChat); + const [compiledContent, setCompiledContent] = useState(''); + + const { + mutateAsync: translationLocales, + data: locales, + isLoading: isLoadingLocales, + } = useMutation(getLocalesFromContent); const { control } = useFormContext(); const path = useStepFormPath(); @@ -38,25 +49,50 @@ export function ChatPreview() { } }, [content, translationLocales]); + const parseContent = (args: { content?: string | any; payload: any; locale?: string }) => { + mutateAsync({ + ...args, + payload: JSON.parse(args.payload), + }) + .then((result: { content: string }) => { + setCompiledContent(result.content); + + return result; + }) + .catch((e: any) => { + errorMessage(e?.message || 'Un-expected error occurred'); + }); + }; + + useEffect(() => { + parseContent({ + content, + payload: `{}`, + locale: selectedLocale, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [compiledContent, selectedLocale]); + return (
+ Today } labelPosition="center" /> - +
); diff --git a/apps/web/src/components/workflow/Preview/common/NovuGreyIcon.tsx b/apps/web/src/components/workflow/Preview/common/NovuGreyIcon.tsx index cabd3d6ac66..cf8c8d6e6a9 100644 --- a/apps/web/src/components/workflow/Preview/common/NovuGreyIcon.tsx +++ b/apps/web/src/components/workflow/Preview/common/NovuGreyIcon.tsx @@ -3,17 +3,17 @@ export const NovuGreyIcon = (props) => { return ( + /> + /> + /> ); }; diff --git a/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx b/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx index 87fbe2a3126..6c7e6b2e32c 100644 --- a/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx +++ b/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx @@ -27,7 +27,7 @@ export function PreviewEditOverlay() { return ( - + diff --git a/apps/web/src/components/workflow/Preview/index.ts b/apps/web/src/components/workflow/Preview/index.ts index 0d3ec48a45c..54e324a0e00 100644 --- a/apps/web/src/components/workflow/Preview/index.ts +++ b/apps/web/src/components/workflow/Preview/index.ts @@ -1,2 +1,2 @@ export * from './chat'; -export * from './email'; +export * from './Email'; diff --git a/apps/web/src/pages/templates/components/email-editor/EmailMessagesCards.tsx b/apps/web/src/pages/templates/components/email-editor/EmailMessagesCards.tsx index 0212535da09..8cf1600770c 100644 --- a/apps/web/src/pages/templates/components/email-editor/EmailMessagesCards.tsx +++ b/apps/web/src/pages/templates/components/email-editor/EmailMessagesCards.tsx @@ -2,13 +2,13 @@ import { useState } from 'react'; import { EmailContentCard } from './EmailContentCard'; import { useAuthContext } from '../../../../components/providers/AuthProvider'; import { When } from '../../../../components/utils/When'; -import { EmailPreview } from '../../../../components/workflow/Preview/email/EmailPreview'; +import { EmailPreview } from '../../../../components/workflow/Preview'; import { EditorPreviewSwitch } from '../EditorPreviewSwitch'; import { Grid, SegmentedControl, useMantineTheme } from '@mantine/core'; import { TestSendEmail } from './TestSendEmail'; import { colors } from '@novu/design-system'; -import { MobileIcon } from '../../../../components/workflow/Preview/email/PreviewSegment/MobileIcon'; -import { WebIcon } from '../../../../components/workflow/Preview/email/PreviewSegment/WebIcon'; +import { MobileIcon } from '../../../../components/workflow/Preview/Email/PreviewSegment/MobileIcon'; +import { WebIcon } from '../../../../components/workflow/Preview/Email/PreviewSegment/WebIcon'; import { useHotkeys } from '@mantine/hooks'; import { VariablesManagement } from './variables-management/VariablesManagement'; import { From 5ce224ba0cb98feaac409b5c969b511bdd67eee0 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Mon, 5 Feb 2024 12:58:51 +0530 Subject: [PATCH 15/28] fix: tests --- .../src/components/workflow/Preview/Email/PreviewMobile.cy.tsx | 3 --- .../src/components/workflow/Preview/Email/PreviewWeb.cy.tsx | 3 --- 2 files changed, 6 deletions(-) diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.cy.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.cy.tsx index ec2aad6a02e..df3e481151d 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.cy.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.cy.tsx @@ -1,6 +1,5 @@ import { TestWrapper } from '../../../../testing'; import { PreviewMobile } from './PreviewMobile'; -import { format } from 'date-fns'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient({}); @@ -35,7 +34,6 @@ describe('Preview mobile', () => { cy.getByTestId('preview-content').contains( "Oops! We've recognized some glitch in this HTML. Please give it another look!" ); - cy.getByTestId('preview-date').contains(format(new Date(), 'EEE, MMM d, HH:mm')); }); it('should render with basic content', () => { @@ -62,6 +60,5 @@ describe('Preview mobile', () => { cy.getByTestId('preview-subject').contains('Subject'); cy.getByTestId('preview-from').contains('novu@novu'); cy.getByTestId('preview-content').invoke('attr', 'srcdoc').should('eq', content); - cy.getByTestId('preview-date').contains(format(new Date(), 'EEE, MMM d, HH:mm')); }); }); diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.cy.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.cy.tsx index 993ae839815..65fb7275d8b 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.cy.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.cy.tsx @@ -1,6 +1,5 @@ import { TestWrapper } from '../../../../testing'; import { PreviewWeb } from './PreviewWeb'; -import { format } from 'date-fns'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient({}); @@ -35,7 +34,6 @@ describe('Preview web', () => { cy.getByTestId('preview-content').contains( "Oops! We've recognized some glitch in this HTML. Please give it another look!" ); - cy.getByTestId('preview-date').contains(format(new Date(), 'EEE, MMM d, HH:mm')); }); it('should render with basic content', () => { @@ -62,6 +60,5 @@ describe('Preview web', () => { cy.getByTestId('preview-subject').contains('Subject'); cy.getByTestId('preview-from').contains('novu@novu'); cy.getByTestId('preview-content').invoke('attr', 'srcdoc').should('eq', content); - cy.getByTestId('preview-date').contains(format(new Date(), 'EEE, MMM d, HH:mm')); }); }); From 228d5796f3d5c52f03e5863aa7d2ad5766478fe3 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Tue, 6 Feb 2024 17:55:33 +0530 Subject: [PATCH 16/28] feat: click on overlay --- .../components/workflow/Preview/common/PreviewEditOverlay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx b/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx index 6c7e6b2e32c..b27b827899e 100644 --- a/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx +++ b/apps/web/src/components/workflow/Preview/common/PreviewEditOverlay.tsx @@ -28,7 +28,7 @@ export function PreviewEditOverlay() { return ( - + Edit message From 391d3233fdf45b594ca139e1a319e33f929fee73 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Tue, 6 Feb 2024 18:33:17 +0530 Subject: [PATCH 17/28] feat: apply suggestions --- .../workflow/Preview/Email/EmailPreview.tsx | 164 +++++++++--------- .../Preview/Email/PreviewMobile.cy.tsx | 4 +- .../workflow/Preview/Email/PreviewMobile.tsx | 6 +- .../workflow/Preview/Email/PreviewWeb.cy.tsx | 4 +- .../workflow/Preview/Email/PreviewWeb.tsx | 6 +- .../workflow/Preview/chat/ChatPreview.tsx | 6 +- .../workflow/Preview/common/LocaleSelect.tsx | 4 +- 7 files changed, 95 insertions(+), 99 deletions(-) diff --git a/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx b/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx index 13eb00b1957..aa7534ecfd8 100644 --- a/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx +++ b/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx @@ -1,7 +1,7 @@ import { Grid, JsonInput, useMantineTheme } from '@mantine/core'; import type { IEmailBlock, MessageTemplateContentType } from '@novu/shared'; import { useMutation } from '@tanstack/react-query'; -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; import { Button, colors, inputStyles } from '@novu/design-system'; @@ -47,7 +47,6 @@ export const EmailPreview = ({ showVariables = true, view }: { view: string; sho }); const { integrations = [] } = useActiveIntegrations(); - const [integration, setIntegration]: any = useState(null); const [parsedSubject, setParsedSubject] = useState(subject); const [content, setContent] = useState('
'); const { isLoading, mutateAsync } = useMutation(previewEmail); @@ -110,93 +109,90 @@ export const EmailPreview = ({ showVariables = true, view }: { view: string; sho }, [contentType, htmlContent, editorContent, processedVariables, selectedLocale]); const theme = useMantineTheme(); - useEffect(() => { - if (integrations.length === 0) { - return; - } - setIntegration(integrations.find((item) => item.channel === 'email' && item.primary) || null); - }, [integrations, setIntegration]); + const integration = useMemo(() => { + return integrations.find((item) => item.channel === 'email' && item.primary) || null; + }, [integrations]); + + const onLocaleChange = (locale: string) => { + setSelectedLocale(locale); + }; return ( <> {showVariables ? ( - <> - - - - - - - - - - - - - - - - -
- + + + + + + + + - -
-
+ +
- - + + + +
+ + +
+
+ ) : ( diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.cy.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.cy.tsx index df3e481151d..0ce89ba97de 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.cy.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.cy.tsx @@ -22,7 +22,7 @@ describe('Preview mobile', () => { from: 'novu@novu', }, }} - setSelectedLocale={() => {}} + onLocaleChange={() => {}} locales={[]} /> @@ -50,7 +50,7 @@ describe('Preview mobile', () => { from: 'novu@novu', }, }} - setSelectedLocale={() => {}} + onLocaleChange={() => {}} locales={[]} /> diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx index 9eda50804be..908af439387 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx @@ -62,7 +62,7 @@ export const PreviewMobile = ({ loading = false, error, showEditOverlay = false, - setSelectedLocale, + onLocaleChange, selectedLocale, locales, }: { @@ -72,7 +72,7 @@ export const PreviewMobile = ({ loading?: boolean; error?: Merge>; showEditOverlay?: boolean; - setSelectedLocale: (locale: string) => void; + onLocaleChange: (locale: string) => void; selectedLocale?: string; locales: any[]; }) => { @@ -142,7 +142,7 @@ export const PreviewMobile = ({ isLoading={loading} locales={locales} value={selectedLocale} - setSelectedLocale={setSelectedLocale} + onLocaleChange={onLocaleChange} />
diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.cy.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.cy.tsx index 65fb7275d8b..b9857df359f 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.cy.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.cy.tsx @@ -22,7 +22,7 @@ describe('Preview web', () => { from: 'novu@novu', }, }} - setSelectedLocale={() => {}} + onLocaleChange={() => {}} locales={[]} /> @@ -50,7 +50,7 @@ describe('Preview web', () => { from: 'novu@novu', }, }} - setSelectedLocale={() => {}} + onLocaleChange={() => {}} locales={[]} /> diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx index 16263c2ac60..ebd7bd16191 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx @@ -81,7 +81,7 @@ export const PreviewWeb = ({ loading = false, error, showEditOverlay = false, - setSelectedLocale, + onLocaleChange, selectedLocale, locales, }: { @@ -91,7 +91,7 @@ export const PreviewWeb = ({ loading?: boolean; error?: Merge>; showEditOverlay?: boolean; - setSelectedLocale: (locale: string) => void; + onLocaleChange: (locale: string) => void; selectedLocale?: string; locales: any[]; }) => { @@ -158,7 +158,7 @@ export const PreviewWeb = ({ isLoading={loading} locales={locales} value={selectedLocale} - setSelectedLocale={setSelectedLocale} + onLocaleChange={onLocaleChange} />
diff --git a/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx b/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx index 69ccd0aaef4..5fdefacf6c3 100644 --- a/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx +++ b/apps/web/src/components/workflow/Preview/chat/ChatPreview.tsx @@ -18,7 +18,7 @@ export function ChatPreview() { const { organization } = useAuthController(); const { colorScheme } = useMantineColorScheme(); const isDark = colorScheme === 'dark'; - const [selectedLocale, setSelectedLocale] = useState(undefined); + const [selectedLocale, onLocaleChange] = useState(undefined); const { isLoading, mutateAsync } = useMutation(previewChat); const [compiledContent, setCompiledContent] = useState(''); @@ -38,7 +38,7 @@ export function ChatPreview() { }); useEffect(() => { - setSelectedLocale(organization?.defaultLocale); + onLocaleChange(organization?.defaultLocale); }, [organization?.defaultLocale]); useEffect(() => { @@ -78,7 +78,7 @@ export function ChatPreview() {
diff --git a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx index 0a8a2feca58..fed68fce244 100644 --- a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx +++ b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx @@ -3,7 +3,7 @@ import { Select, Text } from '@novu/design-system'; import { forwardRef } from 'react'; import { IS_DOCKER_HOSTED } from '../../../../config'; -export function LocaleSelect({ locales, value, isLoading, setSelectedLocale }) { +export function LocaleSelect({ locales, value, isLoading, onLocaleChange }) { if (IS_DOCKER_HOSTED || locales.length === 0) { return null; } @@ -24,7 +24,7 @@ export function LocaleSelect({ locales, value, isLoading, setSelectedLocale }) { searchable withinPortal onChange={(val) => { - setSelectedLocale(val); + onLocaleChange(val); }} value={value} variant="unstyled" From 321d62fc61afcf9006923bd5771ad256d329dd34 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Tue, 6 Feb 2024 18:39:04 +0530 Subject: [PATCH 18/28] fix: pass locale --- .../web/src/components/workflow/Preview/Email/EmailPreview.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx b/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx index aa7534ecfd8..9d88beba337 100644 --- a/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx +++ b/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx @@ -1,7 +1,7 @@ import { Grid, JsonInput, useMantineTheme } from '@mantine/core'; import type { IEmailBlock, MessageTemplateContentType } from '@novu/shared'; import { useMutation } from '@tanstack/react-query'; -import { useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; import { Button, colors, inputStyles } from '@novu/design-system'; @@ -183,6 +183,7 @@ export const EmailPreview = ({ showVariables = true, view }: { view: string; sho content: contentType === 'editor' ? editorContent : htmlContent, payload: payloadValue, layoutId, + locale: selectedLocale, }); }} variant="outline" From 9f8d83e4d403f5089d94431009f3db6b89155e2b Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Tue, 6 Feb 2024 18:44:26 +0530 Subject: [PATCH 19/28] fix: text row --- .../web/src/components/workflow/Preview/common/LocaleSelect.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx index fed68fce244..823993f6769 100644 --- a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx +++ b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx @@ -37,7 +37,7 @@ const SelectItem = forwardRef(({ label, value, return ( - {label} + {label} ); }); From 45cbd7677e43d58e248f377db270ff899adc8e78 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Tue, 6 Feb 2024 19:36:40 +0530 Subject: [PATCH 20/28] feat: reusable header skeleton --- .../workflow/Preview/Email/HeaderSkeleton.tsx | 13 +++++++++++++ .../workflow/Preview/Email/PreviewMobile.tsx | 11 ++++------- .../workflow/Preview/Email/PreviewWeb.tsx | 9 +++------ .../workflow/Preview/common/LocaleSelect.tsx | 2 -- 4 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 apps/web/src/components/workflow/Preview/Email/HeaderSkeleton.tsx diff --git a/apps/web/src/components/workflow/Preview/Email/HeaderSkeleton.tsx b/apps/web/src/components/workflow/Preview/Email/HeaderSkeleton.tsx new file mode 100644 index 00000000000..f250b6f4b3a --- /dev/null +++ b/apps/web/src/components/workflow/Preview/Email/HeaderSkeleton.tsx @@ -0,0 +1,13 @@ +import { Skeleton, Stack } from '@mantine/core'; + +export function HeaderSkeleton() { + return ( + <> + + + + + + + ); +} diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx index 908af439387..8f126f9f4bb 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewMobile.tsx @@ -1,16 +1,17 @@ -import { Center, createStyles, Group, Loader, Skeleton, Stack } from '@mantine/core'; +import { createStyles, Group, Skeleton, Stack } from '@mantine/core'; import { colors, Text } from '@novu/design-system'; import { useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import Frame from 'react-frame-component'; import { FieldError, FieldErrorsImpl, Merge } from 'react-hook-form'; -import { When } from '../../../utils/When'; import { IFormStep } from '../../../../pages/templates/components/formTypes'; import { EmailIntegrationInfo } from '../../../../pages/templates/editor/EmailIntegrationInfo'; +import { When } from '../../../utils/When'; import { LocaleSelect } from '../common/LocaleSelect'; import { Mobile } from '../common/Mobile'; import { PreviewEditOverlay } from '../common/PreviewEditOverlay'; import { PreviewUserIcon } from '../common/PreviewUserIcon'; +import { HeaderSkeleton } from './HeaderSkeleton'; const useStyles = createStyles((theme, { error }: { error: boolean }) => ({ header: { @@ -112,11 +113,7 @@ export const PreviewMobile = ({ noWrap > - - - - - + diff --git a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx index ebd7bd16191..424ba173894 100644 --- a/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx +++ b/apps/web/src/components/workflow/Preview/Email/PreviewWeb.tsx @@ -4,12 +4,13 @@ import { useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import Frame from 'react-frame-component'; import { FieldError, FieldErrorsImpl, Merge } from 'react-hook-form'; -import { When } from '../../../utils/When'; import { IFormStep } from '../../../../pages/templates/components/formTypes'; import { EmailIntegrationInfo } from '../../../../pages/templates/editor/EmailIntegrationInfo'; +import { When } from '../../../utils/When'; import { LocaleSelect } from '../common/LocaleSelect'; import { PreviewEditOverlay } from '../common/PreviewEditOverlay'; import { PreviewUserIcon } from '../common/PreviewUserIcon'; +import { HeaderSkeleton } from './HeaderSkeleton'; const useStyles = createStyles((theme, { error }: { error: boolean }) => ({ browser: { @@ -131,11 +132,7 @@ export const PreviewWeb = ({ noWrap > - - - - - + diff --git a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx index 823993f6769..f195b69f7f7 100644 --- a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx +++ b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx @@ -10,7 +10,6 @@ export function LocaleSelect({ locales, value, isLoading, onLocaleChange }) { return ( { - return { - value: locale.langIso, - label: locale.langName, - }; - })} - icon={} - loading={isLoading} - limit={50} - searchable - withinPortal - disabled={readonly} - onChange={(val) => { - setSelectedLocale(val); - }} - value={value} - variant="unstyled" - rightSectionWidth={20} - /> -
- ); -} - -const SelectItem = forwardRef(({ label, value, ...others }: SelectItemProps, ref) => { - return ( - - - {label} - - ); -}); - -export const FlagIcon = ({ locale }) => { - if (IS_DOCKER_HOSTED) { - return null; - } - - try { - const module = require('@novu/ee-translation-web'); - - return module.FlagIcon({ locale }); - } catch (e) {} - - return []; -}; diff --git a/apps/web/src/pages/templates/editor/PreviewEditOverlay.tsx b/apps/web/src/pages/templates/editor/PreviewEditOverlay.tsx deleted file mode 100644 index aa460f3041d..00000000000 --- a/apps/web/src/pages/templates/editor/PreviewEditOverlay.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Flex, Group, Overlay, UnstyledButton } from '@mantine/core'; -import { colors, PencilOutlined, Text, Variant } from '@novu/design-system'; -import { StepTypeEnum } from '@novu/shared'; -import { useNavigate, useParams } from 'react-router-dom'; -import { useBasePath } from '../hooks/useBasePath'; - -export function PreviewEditOverlay() { - const basePath = useBasePath(); - const { channel, stepUuid, variantUuid } = useParams<{ - channel: StepTypeEnum; - stepUuid: string; - variantUuid: string; - }>(); - - const navigate = useNavigate(); - - const handleEditMessage = () => { - const isVariant = !!variantUuid; - let path = `${basePath}/${channel}/${stepUuid}`; - if (isVariant) { - path += `/variants/${variantUuid}`; - } - - navigate(path); - }; - - return ( - - - - - - Edit message - - - - - ); -} From a8f4db0e6b736d70d242437ac324b40f413e1952 Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Wed, 7 Feb 2024 18:22:20 +0530 Subject: [PATCH 23/28] feat: pr suggestions --- apps/web/src/api/hooks/index.ts | 2 + .../src/api/hooks/useGetLocalesFromContent.ts | 56 +++++++++++ apps/web/src/api/hooks/usePreviewEmail.ts | 58 +++++++++++ .../workflow/Preview/Email/EmailPreview.tsx | 99 ++++++++++--------- .../workflow/Preview/common/LocaleSelect.tsx | 3 +- 5 files changed, 171 insertions(+), 47 deletions(-) create mode 100644 apps/web/src/api/hooks/useGetLocalesFromContent.ts create mode 100644 apps/web/src/api/hooks/usePreviewEmail.ts diff --git a/apps/web/src/api/hooks/index.ts b/apps/web/src/api/hooks/index.ts index 9ff0778944c..877cb7a0f29 100644 --- a/apps/web/src/api/hooks/index.ts +++ b/apps/web/src/api/hooks/index.ts @@ -3,3 +3,5 @@ export * from './useInAppActivated'; export * from './useDeleteIntegration'; export * from './useWebhookSupportStatus'; export * from './notification-templates'; +export * from './useGetLocalesFromContent'; +export * from './usePreviewEmail'; diff --git a/apps/web/src/api/hooks/useGetLocalesFromContent.ts b/apps/web/src/api/hooks/useGetLocalesFromContent.ts new file mode 100644 index 00000000000..66448b4bdaf --- /dev/null +++ b/apps/web/src/api/hooks/useGetLocalesFromContent.ts @@ -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( + ({ 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 || [], + }; +}; diff --git a/apps/web/src/api/hooks/usePreviewEmail.ts b/apps/web/src/api/hooks/usePreviewEmail.ts new file mode 100644 index 00000000000..b67dbcf407d --- /dev/null +++ b/apps/web/src/api/hooks/usePreviewEmail.ts @@ -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 = {}) => { + const { mutateAsync, isLoading } = useMutation( + ({ 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, + }; +}; diff --git a/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx b/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx index 9d88beba337..5252b1f571e 100644 --- a/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx +++ b/apps/web/src/components/workflow/Preview/Email/EmailPreview.tsx @@ -6,7 +6,7 @@ import { useFormContext, useWatch } from 'react-hook-form'; import { Button, colors, inputStyles } from '@novu/design-system'; import { previewEmail } from '../../../../api/content-templates'; -import { getLocalesFromContent } from '../../../../api/translations'; + import { When } from '../../../utils/When'; import { useActiveIntegrations, useAuthController, useProcessVariables } from '../../../../hooks'; import { errorMessage } from '../../../../utils/notifications'; @@ -15,6 +15,8 @@ import { useStepFormErrors } from '../../../../pages/templates/hooks/useStepForm import { useStepFormPath } from '../../../../pages/templates/hooks/useStepFormPath'; import { PreviewMobile } from './PreviewMobile'; import { PreviewWeb } from './PreviewWeb'; +import { useGetLocalesFromContent, usePreviewEmail } from '../../../../api/hooks'; +import { IS_DOCKER_HOSTED, useDataRef } from '@novu/shared-web'; export const EmailPreview = ({ showVariables = true, view }: { view: string; showVariables?: boolean }) => { const { control } = useFormContext(); @@ -47,24 +49,42 @@ export const EmailPreview = ({ showVariables = true, view }: { view: string; sho }); const { integrations = [] } = useActiveIntegrations(); - const [parsedSubject, setParsedSubject] = useState(subject); - const [content, setContent] = useState('
'); - const { isLoading, mutateAsync } = useMutation(previewEmail); - const { mutateAsync: translationLocales, data: locales } = useMutation(getLocalesFromContent); + + const [parsedPreviewState, setParsedPreviewState] = useState({ + subject: subject, + content: '
', + }); + + const { isLoading, getEmailPreview } = usePreviewEmail({ + onSuccess: (result) => { + setParsedPreviewState({ + content: result.html, + subject: result.subject, + }); + }, + }); + + const { data: locales, getLocalesFromContent } = useGetLocalesFromContent(); const processedVariables = useProcessVariables(variables); const [payloadValue, setPayloadValue] = useState('{}'); const { organization } = useAuthController(); const [selectedLocale, setSelectedLocale] = useState(undefined); + const previewData = useDataRef({ contentType, htmlContent, editorContent, layoutId, subject }); + useEffect(() => { - const emailContent = contentType === 'editor' ? editorContent : htmlContent; - if (emailContent) { - translationLocales({ + const emailContent = + previewData.current.contentType === 'editor' + ? previewData.current.editorContent + : previewData.current.htmlContent; + + if (emailContent && !IS_DOCKER_HOSTED) { + getLocalesFromContent({ content: emailContent, }); } - }, [contentType, editorContent, htmlContent, translationLocales]); + }, [getLocalesFromContent, previewData]); useEffect(() => { setSelectedLocale(organization?.defaultLocale); @@ -74,39 +94,30 @@ export const EmailPreview = ({ showVariables = true, view }: { view: string; sho setPayloadValue(processedVariables); }, [processedVariables, setPayloadValue]); - const parseContent = (args: { - contentType?: MessageTemplateContentType; - content?: string | IEmailBlock[]; - payload: any; - layoutId?: string; - locale?: string; - }) => { - mutateAsync({ - ...args, - payload: JSON.parse(args.payload), - subject: subject ? subject : '', - }) - .then((result: { html: string; subject: string }) => { - setContent(result.html); - setParsedSubject(result.subject); - - return result; - }) - .catch((e: any) => { - errorMessage(e?.message || 'Un-expected error occurred'); + const parseContent = useCallback( + (args: { payload: any }) => { + getEmailPreview({ + ...args, + contentType: previewData.current.contentType, + content: + previewData.current.contentType === 'editor' + ? previewData.current.editorContent + : previewData.current.htmlContent, + layoutId: previewData.current.layoutId, + payload: JSON.parse(args.payload), + subject: previewData.current.subject ?? '', + locale: selectedLocale, }); - }; + }, + [getEmailPreview, previewData, selectedLocale] + ); useEffect(() => { parseContent({ - contentType, - content: contentType === 'editor' ? editorContent : htmlContent, payload: processedVariables, - layoutId, - locale: selectedLocale, }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [contentType, htmlContent, editorContent, processedVariables, selectedLocale]); + }, [parseContent, previewData, processedVariables, selectedLocale]); + const theme = useMantineTheme(); const integration = useMemo(() => { @@ -125,8 +136,8 @@ export const EmailPreview = ({ showVariables = true, view }: { view: string; sho { parseContent({ - contentType, - content: contentType === 'editor' ? editorContent : htmlContent, payload: payloadValue, - layoutId, - locale: selectedLocale, }); }} variant="outline" @@ -197,8 +204,8 @@ export const EmailPreview = ({ showVariables = true, view }: { view: string; sho ) : ( Date: Wed, 7 Feb 2024 18:28:34 +0530 Subject: [PATCH 24/28] fix: overlapping input --- .../workflow/Preview/common/LocaleSelect.tsx | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx index 2f64ab11018..5b991cc779f 100644 --- a/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx +++ b/apps/web/src/components/workflow/Preview/common/LocaleSelect.tsx @@ -1,8 +1,11 @@ +import styled from '@emotion/styled'; import { SelectItemProps, Group } from '@mantine/core'; import { Select, Text } from '@novu/design-system'; import { forwardRef } from 'react'; import { IS_DOCKER_HOSTED } from '../../../../config'; +const rightSectionWidth = 20; + export function LocaleSelect({ locales, value, isLoading, onLocaleChange }) { // Do not render locale select if self-hosted or no locale or only one locale if (IS_DOCKER_HOSTED || locales.length < 2) { @@ -10,25 +13,28 @@ export function LocaleSelect({ locales, value, isLoading, onLocaleChange }) { } return ( - { + return { + value: locale.langIso, + label: locale.langName, + }; + })} + icon={} + loading={isLoading} + limit={50} + searchable + withinPortal + onChange={(val) => { + onLocaleChange(val); + }} + value={value} + variant="unstyled" + rightSectionWidth={rightSectionWidth} + /> + ); } @@ -54,3 +60,9 @@ export const FlagIcon = ({ locale }) => { return []; }; + +const SelectContainer = styled.div` + .mantine-Select-input { + padding-right: ${rightSectionWidth}px; + } +`; From 8da6a70875dd44026c9c676050f734f199647dea Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Thu, 8 Feb 2024 16:15:10 +0530 Subject: [PATCH 25/28] feat: test --- .../get-locales-from-content.e2e-ee.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 apps/api/src/app/testing/translations/get-locales-from-content.e2e-ee.ts diff --git a/apps/api/src/app/testing/translations/get-locales-from-content.e2e-ee.ts b/apps/api/src/app/testing/translations/get-locales-from-content.e2e-ee.ts new file mode 100644 index 00000000000..13f6e714fa1 --- /dev/null +++ b/apps/api/src/app/testing/translations/get-locales-from-content.e2e-ee.ts @@ -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]); + }); +}); From 2ee0ed5769bdebeb9b90a029d91c3c967b26f5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Fri, 9 Feb 2024 08:51:30 +0100 Subject: [PATCH 26/28] chore: remove unnecessary files --- enterprise/packages/billing-web/src | 1 - enterprise/packages/billing/src | 1 - 2 files changed, 2 deletions(-) delete mode 120000 enterprise/packages/billing-web/src delete mode 120000 enterprise/packages/billing/src diff --git a/enterprise/packages/billing-web/src b/enterprise/packages/billing-web/src deleted file mode 120000 index 3e641089fb1..00000000000 --- a/enterprise/packages/billing-web/src +++ /dev/null @@ -1 +0,0 @@ -../../../.source/billing-web/src \ No newline at end of file diff --git a/enterprise/packages/billing/src b/enterprise/packages/billing/src deleted file mode 120000 index 176d5d043a9..00000000000 --- a/enterprise/packages/billing/src +++ /dev/null @@ -1 +0,0 @@ -../../../.source/billing/src \ No newline at end of file From 9ae98ee787062286e4eb4d99f21b0639b82ab7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Fri, 9 Feb 2024 10:05:08 +0100 Subject: [PATCH 27/28] chore: update the source file --- .source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.source b/.source index a93a0a65b29..5121f6954c6 160000 --- a/.source +++ b/.source @@ -1 +1 @@ -Subproject commit a93a0a65b2996798326f31a2b87f6e34d97c5830 +Subproject commit 5121f6954c6dc4a53afe5d2f9df001e2aa69d4d3 From cd9f145c575a62082e995e0acf081e0b289b4d0e Mon Sep 17 00:00:00 2001 From: Biswajeet Das Date: Fri, 9 Feb 2024 17:22:42 +0530 Subject: [PATCH 28/28] fix: test --- apps/web/cypress/tests/tenants-page.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/cypress/tests/tenants-page.spec.ts b/apps/web/cypress/tests/tenants-page.spec.ts index 8e9b1508e03..2df27625a43 100644 --- a/apps/web/cypress/tests/tenants-page.spec.ts +++ b/apps/web/cypress/tests/tenants-page.spec.ts @@ -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(); @@ -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();