Skip to content

Commit

Permalink
fix(dashboard): wait for save when switching preview tab
Browse files Browse the repository at this point in the history
  • Loading branch information
ChmaraX committed Dec 13, 2024
1 parent 224a74d commit bc77f6d
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 173 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import { Separator } from '@/components/primitives/separator';
import { getComponentByType } from '@/components/workflow-editor/steps/component-utils';
import { EmailPreviewHeader } from '@/components/workflow-editor/steps/email/email-preview';
import { EmailTabsSection } from '@/components/workflow-editor/steps/email/email-tabs-section';
import { type UiSchema } from '@novu/shared';
import { UiSchemaGroupEnum, type UiSchema } from '@novu/shared';

const subjectKey = 'subject';
const emailEditorKey = 'emailEditor';

type EmailEditorProps = { uiSchema: UiSchema };
export const EmailEditor = (props: EmailEditorProps) => {
const { uiSchema } = props;
const { [emailEditorKey]: emailEditor, [subjectKey]: subject } = uiSchema?.properties ?? {};
export const EmailEditor = ({ uiSchema }: { uiSchema: UiSchema }) => {
if (uiSchema.group !== UiSchemaGroupEnum.EMAIL) {
return null;
}

const { [emailEditorKey]: emailEditor, [subjectKey]: subject } = uiSchema.properties ?? {};

return (
<div className="flex h-full flex-col">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,76 +1,41 @@
import { Cross2Icon } from '@radix-ui/react-icons';
import { useFormContext } from 'react-hook-form';
import { RiEdit2Line, RiPencilRuler2Line } from 'react-icons/ri';
import { useNavigate } from 'react-router-dom';

import { Notification5Fill } from '@/components/icons';
import { Button } from '@/components/primitives/button';
import { Separator } from '@/components/primitives/separator';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/primitives/tabs';
import { StepEditorProps } from '@/components/workflow-editor/steps/configure-step-template-form';
import { WorkflowOriginEnum } from '@novu/shared';
import { EmailEditor } from '@/components/workflow-editor/steps/email/email-editor';
import { EmailEditorPreview } from '@/components/workflow-editor/steps/email/email-editor-preview';
import { CustomStepControls } from '../controls/custom-step-controls';
import { EmailTabsSection } from '@/components/workflow-editor/steps/email/email-tabs-section';
import { WorkflowOriginEnum } from '@novu/shared';
import { StepEditorProps } from '@/components/workflow-editor/steps/configure-step-template-form';
import { TemplateTabs } from '@/components/workflow-editor/steps/template-tabs';
import { useState } from 'react';

const tabsContentClassName = 'h-full w-full overflow-y-auto data-[state=inactive]:hidden';

export const EmailTabs = (props: StepEditorProps) => {
const { workflow, step } = props;
const { dataSchema, uiSchema } = step.controls;
const form = useFormContext();
const navigate = useNavigate();
const [tabsValue, setTabsValue] = useState('editor');

return (
<Tabs defaultValue="editor" value={tabsValue} onValueChange={setTabsValue} className="flex h-full flex-1 flex-col">
<header className="flex flex-row items-center gap-3 px-3 py-1.5">
<div className="mr-auto flex items-center gap-2.5 text-sm font-medium">
<RiEdit2Line className="size-4" />
<span>Configure Template</span>
</div>
<TabsList className="w-min">
<TabsTrigger value="editor" className="gap-1.5">
<RiPencilRuler2Line className="size-5 p-0.5" />
<span>Editor</span>
</TabsTrigger>
<TabsTrigger value="preview" className="gap-1.5">
<Notification5Fill className="size-5 p-0.5" />
<span>Preview</span>
</TabsTrigger>
</TabsList>
const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema;
const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL;

const editorContent = (
<>
{isNovuCloud && <EmailEditor uiSchema={uiSchema} />}
{isExternal && (
<EmailTabsSection>
<CustomStepControls dataSchema={dataSchema} origin={workflow.origin} />
</EmailTabsSection>
)}
</>
);

<Button
variant="ghost"
size="xs"
className="size-6"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
navigate('../', { relative: 'path' });
}}
>
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</Button>
</header>
<Separator />
<TabsContent value="editor" forceMount className={tabsContentClassName}>
{workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema && <EmailEditor uiSchema={uiSchema} />}
{workflow.origin === WorkflowOriginEnum.EXTERNAL && (
<EmailTabsSection>
<CustomStepControls dataSchema={dataSchema} origin={workflow.origin} />
</EmailTabsSection>
)}
</TabsContent>
<TabsContent value="preview" forceMount className={tabsContentClassName}>
{tabsValue === 'preview' && (
<EmailEditorPreview workflow={workflow} step={step} formValues={form.getValues()} />
)}
</TabsContent>
<Separator />
</Tabs>
const previewContent = <EmailEditorPreview workflow={workflow} step={step} formValues={form.getValues()} />;

return (
<TemplateTabs
editorContent={editorContent}
previewContent={previewContent}
tabsValue={tabsValue}
onTabChange={setTabsValue}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ const redirectKey = 'redirect';
const primaryActionKey = 'primaryAction';
const secondaryActionKey = 'secondaryAction';

export const InAppEditor = ({ uiSchema }: { uiSchema?: UiSchema }) => {
if (!uiSchema || uiSchema?.group !== UiSchemaGroupEnum.IN_APP) {
export const InAppEditor = ({ uiSchema }: { uiSchema: UiSchema }) => {
if (uiSchema.group !== UiSchemaGroupEnum.IN_APP) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,69 +1,41 @@
import { Cross2Icon } from '@radix-ui/react-icons';
import { useFormContext } from 'react-hook-form';
import { RiEdit2Line, RiPencilRuler2Line } from 'react-icons/ri';
import { useNavigate } from 'react-router-dom';

import { Notification5Fill } from '@/components/icons';
import { Button } from '@/components/primitives/button';
import { Separator } from '@/components/primitives/separator';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/primitives/tabs';
import { InAppEditor } from '@/components/workflow-editor/steps/in-app/in-app-editor';
import { InAppEditorPreview } from '@/components/workflow-editor/steps/in-app/in-app-editor-preview';
import { CustomStepControls } from '../controls/custom-step-controls';
import { StepEditorProps } from '@/components/workflow-editor/steps/configure-step-template-form';
import { InAppTabsSection } from '@/components/workflow-editor/steps/in-app/in-app-tabs-section';

const tabsContentClassName = 'h-full w-full overflow-y-auto';
import { TemplateTabs } from '@/components/workflow-editor/steps/template-tabs';
import { WorkflowOriginEnum } from '@/utils/enums';
import { useState } from 'react';

export const InAppTabs = (props: StepEditorProps) => {
const { workflow, step } = props;
const { dataSchema, uiSchema } = step.controls;
const form = useFormContext();
const navigate = useNavigate();
const [tabsValue, setTabsValue] = useState('editor');

return (
<Tabs defaultValue="editor" className="flex h-full flex-1 flex-col">
<header className="flex flex-row items-center gap-3 px-3 py-1.5">
<div className="mr-auto flex items-center gap-2.5 text-sm font-medium">
<RiEdit2Line className="size-4" />
<span>Configure Template</span>
</div>
<TabsList className="w-min">
<TabsTrigger value="editor" className="gap-1.5">
<RiPencilRuler2Line className="size-5 p-0.5" />
<span>Editor</span>
</TabsTrigger>
<TabsTrigger value="preview" className="gap-1.5">
<Notification5Fill className="size-5 p-0.5" />
<span>Preview</span>
</TabsTrigger>
</TabsList>
const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema;
const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL;

<Button
variant="ghost"
size="xs"
className="size-6"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
navigate('../', { relative: 'path' });
}}
>
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</Button>
</header>
<Separator />
<TabsContent value="editor" className={tabsContentClassName}>
<InAppEditor uiSchema={uiSchema} />
const editorContent = (
<>
{isNovuCloud && <InAppEditor uiSchema={uiSchema} />}
{isExternal && (
<InAppTabsSection>
<CustomStepControls dataSchema={dataSchema} origin={workflow.origin} />
</InAppTabsSection>
</TabsContent>
<TabsContent value="preview" className={tabsContentClassName}>
<InAppEditorPreview workflow={workflow} step={step} formValues={form.getValues()} />
</TabsContent>
<Separator />
</Tabs>
)}
</>
);

const previewContent = <InAppEditorPreview workflow={workflow} step={step} formValues={form.getValues()} />;

return (
<TemplateTabs
editorContent={editorContent}
previewContent={previewContent}
tabsValue={tabsValue}
onTabChange={setTabsValue}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,62 +1,26 @@
/**
* This component is used as a placeholder for the other step configuration until the actual configuration is implemented.
*/
import { Cross2Icon } from '@radix-ui/react-icons';
import { RiEdit2Line, RiPencilRuler2Line } from 'react-icons/ri';
import { useNavigate } from 'react-router-dom';

import { Notification5Fill } from '@/components/icons';
import { Button } from '@/components/primitives/button';
import { Separator } from '@/components/primitives/separator';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/primitives/tabs';
import { CustomStepControls } from '@/components/workflow-editor/steps/controls/custom-step-controls';
import type { StepEditorProps } from '@/components/workflow-editor/steps/configure-step-template-form';

const tabsContentClassName = 'h-full w-full px-3 py-3.5 overflow-y-auto';
import { useState } from 'react';
import { CustomStepControls } from './controls/custom-step-controls';
import { TemplateTabs } from './template-tabs';
import type { StepEditorProps } from './configure-step-template-form';

export const OtherStepTabs = ({ workflow, step }: StepEditorProps) => {
const { dataSchema } = step.controls;
const navigate = useNavigate();
const [tabsValue, setTabsValue] = useState('editor');

return (
<Tabs defaultValue="editor" className="flex h-full flex-1 flex-col">
<header className="flex flex-row items-center gap-3 px-3 py-1.5">
<div className="mr-auto flex items-center gap-2.5 text-sm font-medium">
<RiEdit2Line className="size-4" />
<span>Configure Template</span>
</div>
<TabsList className="w-min">
<TabsTrigger value="editor" className="gap-1.5">
<RiPencilRuler2Line className="size-5 p-0.5" />
<span>Editor</span>
</TabsTrigger>
<TabsTrigger value="preview" className="gap-1.5" disabled>
<Notification5Fill className="size-5 p-0.5" />
<span>Preview</span>
</TabsTrigger>
</TabsList>
const editorContent = (
<div className="px-3 py-5">
<CustomStepControls dataSchema={dataSchema} origin={workflow.origin} />
</div>
);

const previewContent = null;

<Button
variant="ghost"
size="xs"
className="size-6"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
navigate('../', { relative: 'path' });
}}
>
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</Button>
</header>
<Separator />
<TabsContent value="editor" className={tabsContentClassName}>
<div className="px-3 py-5">
<CustomStepControls dataSchema={dataSchema} origin={workflow.origin} />
</div>
</TabsContent>
<Separator />
</Tabs>
return (
<TemplateTabs
editorContent={editorContent}
previewContent={previewContent}
tabsValue={tabsValue}
onTabChange={setTabsValue}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Cross2Icon } from '@radix-ui/react-icons';
import { RiEdit2Line, RiPencilRuler2Line } from 'react-icons/ri';
import { useNavigate } from 'react-router-dom';

import { Notification5Fill } from '@/components/icons';
import { Button } from '@/components/primitives/button';
import { Separator } from '@/components/primitives/separator';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/primitives/tabs';

interface TemplateTabsProps {
editorContent: React.ReactNode;
previewContent?: React.ReactNode;
tabsValue: string;
onTabChange: (tab: string) => void;
}

export const TemplateTabs = ({ editorContent, previewContent, tabsValue, onTabChange }: TemplateTabsProps) => {
const navigate = useNavigate();

return (
<Tabs defaultValue="editor" value={tabsValue} onValueChange={onTabChange} className="flex h-full flex-1 flex-col">
<header className="flex flex-row items-center gap-3 px-3 py-1.5">
<div className="mr-auto flex items-center gap-2.5 text-sm font-medium">
<RiEdit2Line className="size-4" />
<span>Configure Template</span>
</div>
<TabsList className="w-min">
<TabsTrigger value="editor" className="gap-1.5">
<RiPencilRuler2Line className="size-5 p-0.5" />
<span>Editor</span>
</TabsTrigger>
<TabsTrigger value="preview" className="gap-1.5" disabled={!previewContent}>
<Notification5Fill className="size-5 p-0.5" />
<span>Preview</span>
</TabsTrigger>
</TabsList>

<Button
variant="ghost"
size="xs"
className="size-6"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
navigate('../', { relative: 'path' });
}}
>
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</Button>
</header>
<Separator />
<TabsContent value="editor" className="h-full w-full overflow-y-auto">
{editorContent}
</TabsContent>
<TabsContent value="preview" className="h-full w-full overflow-y-auto">
{previewContent}
</TabsContent>
<Separator />
</Tabs>
);
};

0 comments on commit bc77f6d

Please sign in to comment.