-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(dashboard): wait for save when switching preview tab
- Loading branch information
Showing
6 changed files
with
138 additions
and
173 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 25 additions & 60 deletions
85
apps/dashboard/src/components/workflow-editor/steps/email/email-tabs.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 23 additions & 51 deletions
74
apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-tabs.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} | ||
/> | ||
); | ||
}; |
74 changes: 19 additions & 55 deletions
74
apps/dashboard/src/components/workflow-editor/steps/other-steps-tabs.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} | ||
/> | ||
); | ||
}; |
62 changes: 62 additions & 0 deletions
62
apps/dashboard/src/components/workflow-editor/steps/template-tabs.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |