Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';
import { EnvironmentTypeEnum, PermissionsEnum, ResourceOriginEnum } from '@novu/shared';
import { VisuallyHidden } from '@radix-ui/react-visually-hidden';
import { formatDistanceToNow } from 'date-fns';
import { forwardRef, useCallback, useState } from 'react';
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { RiDeleteBin2Line, RiSettings4Line } from 'react-icons/ri';
import { useBlocker, useNavigate } from 'react-router-dom';
Expand Down Expand Up @@ -81,13 +81,20 @@ export const LayoutEditorSettingsDrawer = forwardRef<HTMLDivElement, LayoutEdito
layoutId: layout?.layoutId || '',
isTranslationEnabled: layout?.isTranslationEnabled || false,
},
values: {
name: layout?.name || '',
layoutId: layout?.layoutId || '',
isTranslationEnabled: layout?.isTranslationEnabled || false,
},
});

const wasOpenRef = useRef(false);
useEffect(() => {
if (isOpen && !wasOpenRef.current && layout) {
form.reset({
name: layout.name || '',
layoutId: layout.layoutId || '',
isTranslationEnabled: layout.isTranslationEnabled || false,
});
}
wasOpenRef.current = isOpen;
}, [isOpen, layout, form]);

const hasUnsavedChanges = form.formState.isDirty;

useBeforeUnload(hasUnsavedChanges);
Expand All @@ -105,7 +112,12 @@ export const LayoutEditorSettingsDrawer = forwardRef<HTMLDivElement, LayoutEdito
});

const { updateLayout, isPending: isUpdating } = useUpdateLayout({
onSuccess: () => {
onSuccess: (data) => {
form.reset({
name: data.name || '',
layoutId: data.layoutId || '',
isTranslationEnabled: data.isTranslationEnabled || false,
});
showSuccessToast('Layout updated successfully', '', toastOptions);
onOpenChange(false);
},
Expand Down Expand Up @@ -240,7 +252,10 @@ export const LayoutEditorSettingsDrawer = forwardRef<HTMLDivElement, LayoutEdito
id="layout-settings"
autoComplete="off"
noValidate
onSubmit={form.handleSubmit(onSubmit)}
onSubmit={(e) => {
e.stopPropagation();
form.handleSubmit(onSubmit)(e);
}}
className="flex h-full flex-col"
>
<VisuallyHidden>
Expand Down
4 changes: 3 additions & 1 deletion apps/dashboard/src/components/primitives/variable-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type VariableEditorProps = {
skipContainerClick?: boolean;
children?: React.ReactNode;
disabled?: boolean;
readOnly?: boolean;
} & Pick<
EditorProps,
| 'className'
Expand Down Expand Up @@ -97,6 +98,7 @@ export function VariableEditor({
onManageSchemaClick = () => {},
children,
disabled = false,
readOnly = false,
}: VariableEditorProps) {
const containerRef = useRef<HTMLDivElement>(null);
const track = useTelemetry();
Expand Down Expand Up @@ -377,7 +379,7 @@ export function VariableEditor({
onChange={onChange}
onBlur={onBlur}
tagStyles={tagStyles}
editable={!disabled}
editable={!disabled && !readOnly}
/>
{isVariablePopoverOpen && (
<EditVariablePopover
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type ControlInputProps = {
indentWithTab?: boolean;
enableTranslations?: boolean;
disabled?: boolean;
readOnly?: boolean;
};

export function ControlInput({
Expand All @@ -60,6 +61,7 @@ export function ControlInput({
isAllowedVariable,
enableTranslations = false,
disabled = false,
readOnly = false,
}: ControlInputProps) {
const viewRef = useRef<EditorView | null>(null);
const lastCompletionRef = useRef<CompletionRange | null>(null);
Expand Down Expand Up @@ -134,6 +136,7 @@ export function ControlInput({
onManageSchemaClick={openSchemaDrawer}
onCreateNewVariable={handleCreateNewVariable}
disabled={disabled}
readOnly={readOnly}
>
<EditorOverlays
resourceId={resourceId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export function TextWidget(props: WidgetProps) {
variables={variables}
isAllowedVariable={isAllowedVariable}
size="sm"
readOnly={readonly}
disabled={disabled}
/>
</InputWrapper>
</InputRoot>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,6 @@ export class SubscriberProcessWorker extends SubscriberProcessWorkerService {
return;
}

const organizationExists = await this.organizationExist(data);

if (!organizationExists) {
Logger.log(`Organization not found for organizationId ${data.organizationId}. Skipping job.`, LOG_CONTEXT);

return;
}

return await new Promise((resolve, reject) => {
const _this = this;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,30 +256,35 @@ export class GetPreferences {
organizationId: command.organizationId,
};

const workflowPreferences = await this.inMemoryLRUCacheService.get(
InMemoryLRUCacheStore.WORKFLOW_PREFERENCES,
`${command.environmentId}:${command.templateId}`,
async (): Promise<[PreferencesEntity | null, PreferencesEntity | null]> => {
const preferences = await this.preferencesRepository.find(
{
...baseQuery,
_templateId: command.templateId,
type: { $in: [PreferencesTypeEnum.WORKFLOW_RESOURCE, PreferencesTypeEnum.USER_WORKFLOW] },
},
undefined,
queryOptions
);

const workflowResourcePreference =
preferences.find((p) => p.type === PreferencesTypeEnum.WORKFLOW_RESOURCE) ?? null;
const workflowUserPreference = preferences.find((p) => p.type === PreferencesTypeEnum.USER_WORKFLOW) ?? null;

return [workflowResourcePreference, workflowUserPreference];
},
cacheOptions
);
let workflowResourcePreference: PreferencesEntity | null = null;
let workflowUserPreference: PreferencesEntity | null = null;

if (command.templateId) {
const workflowPreferences = await this.inMemoryLRUCacheService.get(
InMemoryLRUCacheStore.WORKFLOW_PREFERENCES,
`${command.environmentId}:${command.templateId}`,
async (): Promise<[PreferencesEntity | null, PreferencesEntity | null]> => {
const preferences = await this.preferencesRepository.find(
{
...baseQuery,
_templateId: command.templateId,
type: { $in: [PreferencesTypeEnum.WORKFLOW_RESOURCE, PreferencesTypeEnum.USER_WORKFLOW] },
},
undefined,
queryOptions
);

const workflowResourcePref =
preferences.find((p) => p.type === PreferencesTypeEnum.WORKFLOW_RESOURCE) ?? null;
const workflowUserPref = preferences.find((p) => p.type === PreferencesTypeEnum.USER_WORKFLOW) ?? null;

return [workflowResourcePref, workflowUserPref];
},
cacheOptions
);

const [workflowResourcePreference, workflowUserPreference] = workflowPreferences;
[workflowResourcePreference, workflowUserPreference] = workflowPreferences;
}

let subscriberWorkflowPreference: PreferencesEntity | null = null;
let subscriberGlobalPreference: PreferencesEntity | null = null;
Expand All @@ -295,18 +300,20 @@ export class GetPreferences {
enabled: useContextFiltering,
});

[subscriberWorkflowPreference, subscriberGlobalPreference] = await Promise.all([
this.preferencesRepository.findOne(
{
...baseQuery,
_subscriberId: command.subscriberId,
_templateId: command.templateId,
type: PreferencesTypeEnum.SUBSCRIBER_WORKFLOW,
...contextQuery,
},
undefined,
queryOptions
),
const [workflowPref, globalPref] = await Promise.all([
command.templateId
? this.preferencesRepository.findOne(
{
...baseQuery,
_subscriberId: command.subscriberId,
_templateId: command.templateId,
type: PreferencesTypeEnum.SUBSCRIBER_WORKFLOW,
...contextQuery,
},
undefined,
queryOptions
)
: Promise.resolve(null),
this.preferencesRepository.findOne(
{
...baseQuery,
Expand All @@ -318,6 +325,9 @@ export class GetPreferences {
queryOptions
),
]);

subscriberWorkflowPreference = workflowPref;
subscriberGlobalPreference = globalPref;
}

const result: PreferenceSet = {};
Expand Down
1 change: 1 addition & 0 deletions packages/novu/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"commander": "^9.0.0",
"configstore": "^5.0.0",
"dotenv": "^16.4.5",
"esbuild": "^0.19.0",
"form-data": "^4.0.5",
"get-port": "^5.1.1",
"gradient-string": "^2.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Body, Container, Html } from '@react-email/components';

export function EmailComponent() {
return (
<Html>
<Body>
<Container>No default export</Container>
</Body>
</Html>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

export default function RegularComponent() {
return (
<div>
<h1>This has JSX but no React Email imports</h1>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Body, Container, Html } from '@react-email/components';

export default function IgnoredEmail() {
return (
<Html>
<Body>
<Container>This file matches *.test.tsx pattern and should be ignored</Container>
</Body>
</Html>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Body, Container, Html } from '@react-email/components';

export default function TestEmail() {
return (
<Html>
<Body>
<Container>This is a test file and should be ignored</Container>
</Body>
</Html>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Body, Container, Html } from '@react-email/components';

export default function TestEmail() {
return (
<Html>
<Body>
<Container>This is a test file and should be ignored</Container>
</Body>
</Html>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Body, Container, Head, Heading, Html, Text } from '@react-email/components';

interface WelcomeEmailProps {
name?: string;
}

export default function WelcomeEmail({ name = 'User' }: WelcomeEmailProps) {
return (
<Html>
<Head />
<Body>
<Container>
<Heading>Welcome, {name}!</Heading>
<Text>Thanks for joining us.</Text>
</Container>
</Body>
</Html>
);
}
Loading
Loading