Skip to content

Commit

Permalink
Merge branch 'import-utils-refactor-schemas' into feat-package-class-…
Browse files Browse the repository at this point in the history
…validator-support
  • Loading branch information
rifont authored Nov 11, 2024
2 parents b918978 + 7e31412 commit 4140bda
Show file tree
Hide file tree
Showing 63 changed files with 570 additions and 754 deletions.
12 changes: 7 additions & 5 deletions apps/api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@novu/api",
"version": "2.0.0",
"version": "2.0.1",
"description": "description",
"author": "",
"private": "true",
Expand All @@ -20,6 +20,7 @@
"lint": "eslint src",
"lint:fix": "pnpm lint -- --fix",
"lint:openapi": "spectral lint http://127.0.0.1:${PORT:-3000}/openapi.yaml",
"pretest": "pnpm build:metadata",
"test": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test E2E_RUNNER=true mocha --require ts-node/register --exit 'src/**/*.spec.ts'",
"test:e2e": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test E2E_RUNNER=true mocha --require ts-node/register --exit --file e2e/setup.ts src/**/*.e2e.ts ",
"test:e2e:ee": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test E2E_RUNNER=true CI_EE_TEST=true CLERK_ENABLED=true mocha --grep @skip-in-ee --invert --require ts-node/register --exit --file e2e/setup.ts 'src/**/*.e2e{,-ee}.ts'",
Expand All @@ -31,6 +32,7 @@
"dependencies": {
"@godaddy/terminus": "^4.12.1",
"@google-cloud/storage": "^6.2.3",
"@maily-to/render": "^0.0.12",
"@nestjs/axios": "3.0.3",
"@nestjs/common": "10.4.1",
"@nestjs/core": "10.4.1",
Expand All @@ -56,8 +58,6 @@
"@sentry/tracing": "^7.40.0",
"@types/newrelic": "^9.14.0",
"@upstash/ratelimit": "^0.4.4",
"zod-to-json-schema": "^3.23.3",
"@maily-to/render": "^0.0.12",
"axios": "^1.6.8",
"bcrypt": "^5.0.0",
"body-parser": "^1.20.0",
Expand Down Expand Up @@ -92,8 +92,10 @@
"sanitize-html": "^2.4.0",
"shortid": "^2.2.16",
"swagger-ui-express": "^4.4.0",
"twilio": "^4.14.1","zod": "^3.23.8",
"uuid": "^8.3.2"
"twilio": "^4.14.1",
"uuid": "^8.3.2",
"zod": "^3.23.8",
"zod-to-json-schema": "^3.23.3"
},
"devDependencies": {
"@faker-js/faker": "^6.0.0",
Expand Down
8 changes: 6 additions & 2 deletions apps/api/src/config/env.validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ export const envValidators = {
REDIS_TLS: json({ default: undefined }),
JWT_SECRET: str(),
SENDGRID_API_KEY: str({ default: '' }),
MONGO_URL: str(),
/** @deprecated - use `MONGO_AUTO_CREATE_INDEXES` instead */
AUTO_CREATE_INDEXES: bool({ default: true }),
MONGO_AUTO_CREATE_INDEXES: bool({ default: true }),
MONGO_MAX_IDLE_TIME_IN_MS: num({ default: 1000 * 30 }),
MONGO_MAX_POOL_SIZE: num({ default: 50 }),
MONGO_MIN_POOL_SIZE: num({ default: 10 }),
MONGO_MAX_POOL_SIZE: num({ default: 500 }),
MONGO_URL: str(),
NOVU_API_KEY: str({ default: '' }),
STORE_ENCRYPTION_KEY: str(),
NEW_RELIC_APP_NAME: str({ default: '' }),
Expand Down
File renamed without changes
5 changes: 3 additions & 2 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@
"test:e2e:merge-report": "playwright merge-reports --reporter html"
},
"dependencies": {
"@clerk/clerk-react": "^5.2.5",
"@clerk/clerk-react": "^5.15.1",
"@codemirror/lang-liquid": "^6.2.1",
"@hookform/resolvers": "^3.9.0",
"@lezer/highlight": "^1.2.1",
"@novu/framework": "workspace:*",
"@novu/js": "workspace:*",
"@novu/react": "workspace:*",
"@novu/shared": "workspace:*",
"@radix-ui/react-accordion": "^1.2.1",
Expand Down Expand Up @@ -78,7 +79,7 @@
"zod": "^3.23.8"
},
"devDependencies": {
"@clerk/types": "^4.6.1",
"@clerk/types": "^4.30.0",
"@eslint/js": "^9.9.0",
"@hookform/devtools": "^4.3.0",
"@playwright/test": "^1.44.0",
Expand Down
2 changes: 2 additions & 0 deletions apps/dashboard/proxy-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ app.use(
})
);

app.use('/images', express.static('images'));

app.get('/legacy/*', (req, res, next) => {
legacyHandler(__dirname, req, res, next);
});
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/opt-in-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const OptInModal = () => {

const Image = () => (
<div className="relative">
<img src="/public/images/opt-in.png" alt="New Dashboard Preview" />
<img src="/images/opt-in.png" alt="New Dashboard Preview" />
<div
className="absolute inset-0 rounded-lg"
style={{ background: 'linear-gradient(163deg, rgba(255, 255, 255, 0.00) 7.65%, #FFF 92.93%)' }}
Expand Down
16 changes: 15 additions & 1 deletion apps/dashboard/src/components/user-profile.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import { UserButton } from '@clerk/clerk-react';
import { ROUTES } from '@/utils/routes';
import { useNewDashboardOptIn } from '@/hooks/use-new-dashboard-opt-in';
import { RiSignpostFill } from 'react-icons/ri';

export function UserProfile() {
return <UserButton afterSignOutUrl={ROUTES.SIGN_IN} />;
const { redirectToLegacyDashboard } = useNewDashboardOptIn();

return (
<UserButton afterSignOutUrl={ROUTES.SIGN_IN}>
<UserButton.MenuItems>
<UserButton.Action
label="Go back to the legacy dashboard"
labelIcon={<RiSignpostFill size="16" color="var(--nv-colors-typography-text-main)" />}
onClick={redirectToLegacyDashboard}
/>
</UserButton.MenuItems>
</UserButton>
);
}
87 changes: 53 additions & 34 deletions apps/dashboard/src/components/workflow-editor/in-app-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { InboxEllipsis } from '@/components/icons/inbox-ellipsis';
import { InboxSettings } from '@/components/icons/inbox-settings';
import { Button } from '@/components/primitives/button';
import { cn } from '@/utils/ui';
import { GeneratePreviewResponseDto, InAppRenderOutput } from '@novu/shared';
import { HTMLAttributes } from 'react';
import { ChannelTypeEnum, GeneratePreviewResponseDto, InAppRenderOutput } from '@novu/shared';
import { HTMLAttributes, useMemo } from 'react';
import { parseMarkdownIntoTokens } from '@novu/js/internal';

type InAppPreviewProps = HTMLAttributes<HTMLDivElement> & {
data: GeneratePreviewResponseDto;
Expand Down Expand Up @@ -38,44 +39,62 @@ export const InAppPreview = (props: InAppPreviewProps) => {
<InboxSettings />
</div>
</div>
<div className="z-20 mb-2 p-2">
<div className="mb-2 flex items-center gap-2">
{data?.result?.preview && 'avatar' in data.result.preview && (
<img src={data.result.preview.avatar as string} alt="avatar" className="h-5 w-5 rounded-full" />
)}
{data?.result?.preview && 'subject' in data.result.preview ? (
<span className="text-xs font-medium text-neutral-600">
{(data.result.preview as InAppRenderOutput).subject}
</span>
) : (
data?.result?.preview && 'body' in data.result.preview && <Body text={data.result.preview.body} />
)}
</div>
{data?.result?.preview && 'body' in data.result.preview && 'subject' in data.result.preview && (
<Body text={data.result.preview.body} />
)}
{data.result?.type === ChannelTypeEnum.IN_APP && (
<div className="z-20 mb-2 p-2">
<div className="mb-2 flex items-center gap-2">
{data.result.preview.avatar && (
<img src={data.result.preview.avatar as string} alt="avatar" className="h-5 w-5 rounded-full" />
)}
{data.result.preview.subject ? (
<Subject text={data.result.preview.subject as string} />
) : (
<Body text={data.result.preview.body} />
)}
</div>

{data.result.preview.subject && <Body text={data.result.preview.body} />}

<div className="mt-3 flex items-center justify-end gap-1">
{data?.result?.preview && 'primaryAction' in data.result.preview && (
<Button className="text-xs font-medium shadow-none" type="button" variant="primary">
{(data.result.preview as InAppRenderOutput).primaryAction?.label}
</Button>
)}
{data?.result?.preview && 'secondaryAction' in data.result.preview && (
<Button variant="outline" className="text-xs font-medium" type="button">
{(data.result.preview as InAppRenderOutput).secondaryAction?.label}
</Button>
)}
<div className="mt-3 flex items-center justify-end gap-1">
{data.result.preview.primaryAction && (
<Button className="text-xs font-medium shadow-none" type="button" variant="primary">
{(data.result.preview as InAppRenderOutput).primaryAction?.label}
</Button>
)}
{data.result.preview.secondaryAction && (
<Button variant="outline" className="text-xs font-medium" type="button">
{(data.result.preview as InAppRenderOutput).secondaryAction?.label}
</Button>
)}
</div>
</div>
</div>
)}
</div>
);
};

const Body = ({ text }: { text: string }) => {
type MarkdownProps = Omit<HTMLAttributes<HTMLParagraphElement>, 'children'> & { children: string };
const Markdown = (props: MarkdownProps) => {
const { children, ...rest } = props;

const tokens = useMemo(() => parseMarkdownIntoTokens(children), [children]);

return (
<div className="truncate text-xs text-neutral-400">
<span>{text}</span>
</div>
<p {...rest}>
{tokens.map((token) => {
if (token.type === 'bold') {
return <strong>{token.content}</strong>;
} else {
return <span>{token.content}</span>;
}
})}
</p>
);
};

const Subject = ({ text }: { text: string }) => {
return <Markdown className="text-xs font-medium text-neutral-600">{text}</Markdown>;
};

const Body = ({ text }: { text: string }) => {
return <Markdown className="truncate text-xs text-neutral-400">{text}</Markdown>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,18 @@ import { Button } from '@/components/primitives/button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/primitives/collapsible';
import { Editor } from '@/components/primitives/editor';
import { InAppPreview } from '@/components/workflow-editor/in-app-preview';
import { usePreviewStep } from '@/hooks/use-preview-step';
import { GeneratePreviewResponseDto } from '@novu/shared';
import { loadLanguage } from '@uiw/codemirror-extensions-langs';
import { useFormContext } from 'react-hook-form';
import { useParams } from 'react-router-dom';

type InAppEditorPreviewProps = {
value: string;
onChange: (value: string) => void;
previewData?: GeneratePreviewResponseDto;
applyPreview: () => void;
};
export const InAppEditorPreview = (props: InAppEditorPreviewProps) => {
const { value, onChange } = props;
const { value, onChange, previewData, applyPreview } = props;
const [isEditorOpen, setIsEditorOpen] = useState(true);
const { previewStep, data } = usePreviewStep();
const { workflowSlug = '', stepSlug = '' } = useParams<{
workflowSlug: string;
stepSlug: string;
}>();
const form = useFormContext();
const [payloadError, setPayloadError] = useState('');

return (
Expand Down Expand Up @@ -68,11 +62,7 @@ export const InAppEditorPreview = (props: InAppEditorPreviewProps) => {
className="self-end"
onClick={() => {
try {
previewStep({
workflowSlug,
stepSlug,
data: { controlValues: form.getValues(), previewPayload: JSON.parse(value) },
});
applyPreview();
} catch (e) {
setPayloadError(String(e));
}
Expand All @@ -83,7 +73,7 @@ export const InAppEditorPreview = (props: InAppEditorPreviewProps) => {
</CollapsibleContent>
</Collapsible>

{data && <InAppPreview data={data} />}
{previewData && <InAppPreview data={previewData} />}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { RiEdit2Line, RiPencilRuler2Line } from 'react-icons/ri';
import { Cross2Icon } from '@radix-ui/react-icons';
import { useNavigate, useParams } from 'react-router-dom';
import { useForm, useWatch } from 'react-hook-form';
import { FieldValues, useForm, useWatch } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { type WorkflowResponseDto, type StepDataDto, type StepUpdateDto } from '@novu/shared';

Expand Down Expand Up @@ -38,7 +38,7 @@ export const InAppTabs = ({ workflow, step }: { workflow: WorkflowResponseDto; s
const [editorValue, setEditorValue] = useState('{}');
const { reset, formState } = form;

const { previewStep } = usePreviewStep();
const { previewStep, data: previewData } = usePreviewStep();
const { updateWorkflow } = useUpdateWorkflow({
onSuccess: () => {
showToast({
Expand Down Expand Up @@ -159,7 +159,18 @@ export const InAppTabs = ({ workflow, step }: { workflow: WorkflowResponseDto; s
<CustomStepControls dataSchema={dataSchema} />
</TabsContent>
<TabsContent value="preview" className={tabsContentClassName}>
<InAppEditorPreview value={editorValue} onChange={setEditorValue} />
<InAppEditorPreview
value={editorValue}
onChange={setEditorValue}
previewData={previewData}
applyPreview={() => {
previewStep({
stepSlug,
workflowSlug,
data: { controlValues: form.getValues() as FieldValues, previewPayload: JSON.parse(editorValue) },
});
}}
/>
</TabsContent>
<Separator />
<footer className="flex justify-end px-3 py-3.5">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,24 @@ export const WorkflowEditorProvider = ({ children }: { children: ReactNode }) =>
const { currentEnvironment } = useEnvironment();
const { workflowSlug } = useParams<{ workflowSlug?: string }>();
const navigate = useNavigate();
const form = useForm<z.infer<typeof workflowSchema>>({ mode: 'onSubmit', resolver: zodResolver(workflowSchema) });

const { workflow, error } = useFetchWorkflow({
workflowSlug,
});
const defaultFormValues = useMemo(
() => ({ ...workflow, steps: workflow?.steps.map((step) => ({ ...step })) }),
[workflow]
);
const form = useForm<z.infer<typeof workflowSchema>>({
mode: 'onSubmit',
resolver: zodResolver(workflowSchema),
defaultValues: defaultFormValues,
});
const { reset, setError } = form;
const steps = useFieldArray({
control: form.control,
name: 'steps',
});

const { workflow, error } = useFetchWorkflow({
workflowSlug,
});
const isReadOnly = workflow?.origin === WorkflowOriginEnum.EXTERNAL;

useLayoutEffect(() => {
Expand All @@ -79,8 +87,8 @@ export const WorkflowEditorProvider = ({ children }: { children: ReactNode }) =>
return;
}

reset({ ...workflow, steps: workflow.steps.map((step) => ({ ...step })) });
}, [workflow, error, navigate, reset, currentEnvironment]);
reset(defaultFormValues);
}, [workflow, defaultFormValues, error, navigate, reset, currentEnvironment]);

const { updateWorkflow, isPending } = useUpdateWorkflow({
onSuccess: (data) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const WorkflowEditor = () => {

return (
<div className="flex h-full flex-1 flex-nowrap">
<Tabs defaultValue="workflow" className="-mt-[1px] flex h-full flex-1 flex-col" value="workflow">
<Tabs defaultValue="workflow" className="-mt-px flex h-full flex-1 flex-col" value="workflow">
<TabsList variant="regular">
<TabsTrigger value="workflow" asChild variant="regular">
<Link
Expand Down
2 changes: 2 additions & 0 deletions apps/dashboard/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ export const INTERCOM_APP_ID = import.meta.env.VITE_INTERCOM_APP_ID;
export const SEGMENT_KEY = import.meta.env.VITE_SEGMENT_KEY;

export const MIXPANEL_KEY = import.meta.env.VITE_MIXPANEL_KEY;

export const LEGACY_DASHBOARD_URL = import.meta.env.VITE_LEGACY_DASHBOARD_URL;
Loading

0 comments on commit 4140bda

Please sign in to comment.