From 65ea92814d1c40c2a3fef54498fa8d760f86e5ed Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 23 Dec 2024 16:29:08 +0200 Subject: [PATCH] refactor(dashboard): unify preview accordion component and preserve changes on tab switch (#7353) --- .../steps/chat/chat-editor-preview.tsx | 104 +++-------------- .../workflow-editor/steps/chat/chat-tabs.tsx | 18 ++- .../steps/email/email-editor-preview.tsx | 108 +++--------------- .../steps/email/email-tabs.tsx | 18 ++- .../steps/in-app/in-app-editor-preview.tsx | 105 +++-------------- .../steps/in-app/in-app-tabs.tsx | 18 ++- .../steps/push/push-editor-preview.tsx | 105 +++-------------- .../workflow-editor/steps/push/push-tabs.tsx | 18 ++- .../shared/configure-preview-accordion.tsx | 79 +++++++++++++ .../steps/sms/sms-editor-preview.tsx | 105 +++-------------- .../workflow-editor/steps/sms/sms-tabs.tsx | 18 ++- .../workflow-editor/steps/template-tabs.tsx | 18 ++- .../steps/use-editor-preview.tsx | 6 +- apps/dashboard/src/hooks/use-preview-step.ts | 12 +- 14 files changed, 272 insertions(+), 460 deletions(-) create mode 100644 apps/dashboard/src/components/workflow-editor/steps/shared/configure-preview-accordion.tsx diff --git a/apps/dashboard/src/components/workflow-editor/steps/chat/chat-editor-preview.tsx b/apps/dashboard/src/components/workflow-editor/steps/chat/chat-editor-preview.tsx index e28137a1e56..bc31871b01d 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/chat/chat-editor-preview.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/chat/chat-editor-preview.tsx @@ -1,61 +1,26 @@ -import { CSSProperties, useEffect, useRef, useState } from 'react'; import { RiChat1Fill } from 'react-icons/ri'; -import { type StepDataDto, type WorkflowResponseDto } from '@novu/shared'; +import { GeneratePreviewResponseDto } from '@novu/shared'; -import { Code2 } from '@/components/icons/code-2'; -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/primitives/accordion'; -import { Button } from '@/components/primitives/button'; -import { Editor } from '@/components/primitives/editor'; -import { loadLanguage } from '@uiw/codemirror-extensions-langs'; -import { useEditorPreview } from '@/components/workflow-editor/steps/use-editor-preview'; import { ChatPreview } from '@/components/workflow-editor/steps/chat/chat-preview'; import { TabsSection } from '@/components/workflow-editor/steps/tabs-section'; import { InlineToast } from '@/components/primitives/inline-toast'; - -const getInitialAccordionValue = (value: string) => { - try { - return Object.keys(JSON.parse(value)).length > 0 ? 'payload' : undefined; - } catch (e) { - return undefined; - } -}; +import { ConfigurePreviewAccordion } from '../shared/configure-preview-accordion'; type ChatEditorPreviewProps = { - workflow: WorkflowResponseDto; - step: StepDataDto; - formValues: Record; + editorValue: string; + setEditorValue: (value: string) => void; + previewStep: () => void; + previewData?: GeneratePreviewResponseDto; + isPreviewPending: boolean; }; -const extensions = [loadLanguage('json')?.extension ?? []]; - -export const ChatEditorPreview = ({ workflow, step, formValues }: ChatEditorPreviewProps) => { - const workflowSlug = workflow.workflowId; - const stepSlug = step.stepId; - const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({ - workflowSlug, - stepSlug, - controlValues: formValues, - }); - const [accordionValue, setAccordionValue] = useState(getInitialAccordionValue(editorValue)); - const [payloadError, setPayloadError] = useState(''); - const [height, setHeight] = useState(0); - const contentRef = useRef(null); - - useEffect(() => { - setAccordionValue(getInitialAccordionValue(editorValue)); - }, [editorValue]); - - useEffect(() => { - const timeout = setTimeout(() => { - if (contentRef.current) { - const rect = contentRef.current.getBoundingClientRect(); - setHeight(rect.height); - } - }, 0); - - return () => clearTimeout(timeout); - }, [editorValue]); - +export const ChatEditorPreview = ({ + editorValue, + setEditorValue, + previewStep, + previewData, + isPreviewPending = false, +}: ChatEditorPreviewProps) => { return (
@@ -70,46 +35,7 @@ export const ChatEditorPreview = ({ workflow, step, formValues }: ChatEditorPrev className="w-full px-3" />
- - - -
- - Configure preview -
-
- - - {payloadError &&

{payloadError}

} - -
-
-
+
); diff --git a/apps/dashboard/src/components/workflow-editor/steps/chat/chat-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/chat/chat-tabs.tsx index 92f6b83024a..848be0f7a96 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/chat/chat-tabs.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/chat/chat-tabs.tsx @@ -7,6 +7,7 @@ import { CustomStepControls } from '@/components/workflow-editor/steps/controls/ import { TemplateTabs } from '@/components/workflow-editor/steps/template-tabs'; import { ChatEditor } from '@/components/workflow-editor/steps/chat/chat-editor'; import { ChatEditorPreview } from '@/components/workflow-editor/steps/chat/chat-editor-preview'; +import { useEditorPreview } from '../use-editor-preview'; export const ChatTabs = (props: StepEditorProps) => { const { workflow, step } = props; @@ -16,6 +17,12 @@ export const ChatTabs = (props: StepEditorProps) => { const isNovuCloud = !!(workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema); const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL; + const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({ + workflowSlug: workflow.workflowId, + stepSlug: step.stepId, + controlValues: form.getValues(), + }); + const editorContent = ( <> {isNovuCloud && } @@ -23,7 +30,15 @@ export const ChatTabs = (props: StepEditorProps) => { ); - const previewContent = ; + const previewContent = ( + + ); return ( { previewContent={previewContent} tabsValue={tabsValue} onTabChange={setTabsValue} + previewStep={previewStep} /> ); }; diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-editor-preview.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-editor-preview.tsx index bdcf1fcb5e7..bb47108eca2 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-editor-preview.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-editor-preview.tsx @@ -1,12 +1,7 @@ -import { ChannelTypeEnum, type StepDataDto, type WorkflowResponseDto } from '@novu/shared'; -import { CSSProperties, useEffect, useRef, useState } from 'react'; +import { ChannelTypeEnum, GeneratePreviewResponseDto } from '@novu/shared'; +import { useState } from 'react'; +import { RiMacLine, RiSmartphoneFill } from 'react-icons/ri'; -import { Code2 } from '@/components/icons/code-2'; -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/primitives/accordion'; -import { Button } from '@/components/primitives/button'; -import { Editor } from '@/components/primitives/editor'; -import { Skeleton } from '@/components/primitives/skeleton'; -import { Tabs, TabsList, TabsTrigger } from '@/components/primitives/tabs'; import { EmailPreviewBody, EmailPreviewBodyMobile, @@ -16,54 +11,28 @@ import { EmailPreviewSubjectMobile, } from '@/components/workflow-editor/steps/email/email-preview'; import { EmailTabsSection } from '@/components/workflow-editor/steps/email/email-tabs-section'; +import { Tabs, TabsList, TabsTrigger } from '@/components/primitives/tabs'; import { TabsContent } from '@radix-ui/react-tabs'; -import { loadLanguage } from '@uiw/codemirror-extensions-langs'; -import { RiMacLine, RiSmartphoneFill } from 'react-icons/ri'; -import { useEditorPreview } from '../use-editor-preview'; import { Separator } from '@/components/primitives/separator'; - -const getInitialAccordionValue = (value: string) => { - try { - return Object.keys(JSON.parse(value)).length > 0 ? 'payload' : undefined; - } catch (e) { - return undefined; - } -}; +import { Skeleton } from '@/components/primitives/skeleton'; +import { ConfigurePreviewAccordion } from '../shared/configure-preview-accordion'; type EmailEditorPreviewProps = { - workflow: WorkflowResponseDto; - step: StepDataDto; - formValues: Record; + editorValue: string; + setEditorValue: (value: string) => void; + previewStep: () => void; + previewData?: GeneratePreviewResponseDto; + isPreviewPending: boolean; }; -export const EmailEditorPreview = ({ workflow, step, formValues }: EmailEditorPreviewProps) => { - const workflowSlug = workflow.workflowId; - const stepSlug = step.stepId; - const { editorValue, setEditorValue, isPreviewPending, previewData, previewStep } = useEditorPreview({ - workflowSlug, - stepSlug, - controlValues: formValues, - }); - const [accordionValue, setAccordionValue] = useState(getInitialAccordionValue(editorValue)); - const [payloadError, setPayloadError] = useState(''); - const [height, setHeight] = useState(0); +export const EmailEditorPreview = ({ + editorValue, + setEditorValue, + previewStep, + previewData, + isPreviewPending = false, +}: EmailEditorPreviewProps) => { const [activeTab, setActiveTab] = useState('desktop'); - const contentRef = useRef(null); - - useEffect(() => { - setAccordionValue(getInitialAccordionValue(editorValue)); - }, [editorValue]); - - useEffect(() => { - const timeout = setTimeout(() => { - if (contentRef.current) { - const rect = contentRef.current.getBoundingClientRect(); - setHeight(rect.height); - } - }, 0); - - return () => clearTimeout(timeout); - }, [editorValue]); return ( @@ -115,46 +84,7 @@ export const EmailEditorPreview = ({ workflow, step, formValues }: EmailEditorPr )} - - - -
- - Configure preview -
-
- - - {payloadError &&

{payloadError}

} - -
-
-
+
diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-tabs.tsx index e549af18818..c8ce24b76ff 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-tabs.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-tabs.tsx @@ -6,6 +6,7 @@ import { EmailEditorPreview } from '@/components/workflow-editor/steps/email/ema import { CustomStepControls } from '../controls/custom-step-controls'; import { StepEditorProps } from '@/components/workflow-editor/steps/configure-step-template-form'; import { TemplateTabs } from '@/components/workflow-editor/steps/template-tabs'; +import { useEditorPreview } from '../use-editor-preview'; export const EmailTabs = (props: StepEditorProps) => { const { workflow, step } = props; @@ -16,6 +17,12 @@ export const EmailTabs = (props: StepEditorProps) => { const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema; const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL; + const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({ + workflowSlug: workflow.workflowId, + stepSlug: step.stepId, + controlValues: form.getValues(), + }); + const editorContent = ( <> {isNovuCloud && } @@ -23,10 +30,19 @@ export const EmailTabs = (props: StepEditorProps) => { ); - const previewContent = ; + const previewContent = ( + + ); return ( { - try { - return Object.keys(JSON.parse(value)).length > 0 ? 'payload' : undefined; - } catch (e) { - return undefined; - } -}; +import { ConfigurePreviewAccordion } from '../shared/configure-preview-accordion'; type InAppEditorPreviewProps = { - workflow: WorkflowResponseDto; - step: StepDataDto; - formValues: Record; + editorValue: string; + setEditorValue: (value: string) => void; + previewStep: () => void; + previewData?: GeneratePreviewResponseDto; + isPreviewPending: boolean; }; -const extensions = [loadLanguage('json')?.extension ?? []]; - -export const InAppEditorPreview = ({ workflow, step, formValues }: InAppEditorPreviewProps) => { - const workflowSlug = workflow.workflowId; - const stepSlug = step.stepId; - const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({ - workflowSlug, - stepSlug, - controlValues: formValues, - }); - const [accordionValue, setAccordionValue] = useState(getInitialAccordionValue(editorValue)); - const [payloadError, setPayloadError] = useState(''); - const [height, setHeight] = useState(0); - const contentRef = useRef(null); - - useEffect(() => { - setAccordionValue(getInitialAccordionValue(editorValue)); - }, [editorValue]); - - useEffect(() => { - const timeout = setTimeout(() => { - if (contentRef.current) { - const rect = contentRef.current.getBoundingClientRect(); - setHeight(rect.height); - } - }, 0); - - return () => clearTimeout(timeout); - }, [editorValue]); - +export const InAppEditorPreview = ({ + editorValue, + setEditorValue, + previewStep, + previewData, + isPreviewPending = false, +}: InAppEditorPreviewProps) => { return (
@@ -63,46 +27,7 @@ export const InAppEditorPreview = ({ workflow, step, formValues }: InAppEditorPr In-App template editor
- - - -
- - Configure preview -
-
- - - {payloadError &&

{payloadError}

} - -
-
-
+
); diff --git a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-tabs.tsx index 2ee9cf46e3f..50cc98a4179 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-tabs.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-tabs.tsx @@ -6,6 +6,7 @@ import { CustomStepControls } from '../controls/custom-step-controls'; import { StepEditorProps } from '@/components/workflow-editor/steps/configure-step-template-form'; import { TemplateTabs } from '@/components/workflow-editor/steps/template-tabs'; import { WorkflowOriginEnum } from '@/utils/enums'; +import { useEditorPreview } from '../use-editor-preview'; export const InAppTabs = (props: StepEditorProps) => { const { workflow, step } = props; @@ -16,6 +17,12 @@ export const InAppTabs = (props: StepEditorProps) => { const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema; const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL; + const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({ + workflowSlug: workflow.workflowId, + stepSlug: step.stepId, + controlValues: form.getValues(), + }); + const editorContent = ( <> {isNovuCloud && } @@ -23,10 +30,19 @@ export const InAppTabs = (props: StepEditorProps) => { ); - const previewContent = ; + const previewContent = ( + + ); return ( { - try { - return Object.keys(JSON.parse(value)).length > 0 ? 'payload' : undefined; - } catch (e) { - return undefined; - } -}; +import { ConfigurePreviewAccordion } from '../shared/configure-preview-accordion'; +import { GeneratePreviewResponseDto } from '@novu/shared'; type PushEditorPreviewProps = { - workflow: WorkflowResponseDto; - step: StepDataDto; - formValues: Record; + editorValue: string; + setEditorValue: (value: string) => void; + previewStep: () => void; + previewData?: GeneratePreviewResponseDto; + isPreviewPending: boolean; }; -const extensions = [loadLanguage('json')?.extension ?? []]; - -export const PushEditorPreview = ({ workflow, step, formValues }: PushEditorPreviewProps) => { - const workflowSlug = workflow.workflowId; - const stepSlug = step.stepId; - const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({ - workflowSlug, - stepSlug, - controlValues: formValues, - }); - const [accordionValue, setAccordionValue] = useState(getInitialAccordionValue(editorValue)); - const [payloadError, setPayloadError] = useState(''); - const [height, setHeight] = useState(0); - const contentRef = useRef(null); - - useEffect(() => { - setAccordionValue(getInitialAccordionValue(editorValue)); - }, [editorValue]); - - useEffect(() => { - const timeout = setTimeout(() => { - if (contentRef.current) { - const rect = contentRef.current.getBoundingClientRect(); - setHeight(rect.height); - } - }, 0); - - return () => clearTimeout(timeout); - }, [editorValue]); - +export const PushEditorPreview = ({ + editorValue, + setEditorValue, + previewStep, + previewData, + isPreviewPending, +}: PushEditorPreviewProps) => { return (
@@ -70,46 +34,7 @@ export const PushEditorPreview = ({ workflow, step, formValues }: PushEditorPrev className="w-full px-3" />
- - - -
- - Configure preview -
-
- - - {payloadError &&

{payloadError}

} - -
-
-
+
); diff --git a/apps/dashboard/src/components/workflow-editor/steps/push/push-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/push/push-tabs.tsx index ad9fb2a1e39..23a725daaaf 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/push/push-tabs.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/push/push-tabs.tsx @@ -6,6 +6,7 @@ import { CustomStepControls } from '../controls/custom-step-controls'; import { TemplateTabs } from '../template-tabs'; import { PushEditorPreview } from './push-editor-preview'; import { useFormContext } from 'react-hook-form'; +import { useEditorPreview } from '../use-editor-preview'; export const PushTabs = (props: StepEditorProps) => { const { workflow, step } = props; @@ -15,6 +16,12 @@ export const PushTabs = (props: StepEditorProps) => { const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema; const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL; + const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({ + workflowSlug: workflow.workflowId, + stepSlug: step.stepId, + controlValues: form.getValues(), + }); + const editorContent = ( <> {isNovuCloud && } @@ -22,10 +29,19 @@ export const PushTabs = (props: StepEditorProps) => { ); - const previewContent = ; + const previewContent = ( + + ); return ( void; + onUpdate: () => void; +}; + +export const ConfigurePreviewAccordion = ({ + editorValue, + setEditorValue, + onUpdate, +}: ConfigurePreviewAccordionProps) => { + const [accordionValue, setAccordionValue] = useState('payload'); + const [payloadError, setPayloadError] = useState(''); + const [height, setHeight] = useState(0); + const contentRef = useRef(null); + + useEffect(() => { + const timeout = setTimeout(() => { + if (contentRef.current) { + const rect = contentRef.current.getBoundingClientRect(); + setHeight(rect.height); + } + }, 0); + + return () => clearTimeout(timeout); + }, [editorValue]); + + return ( + + + +
+ + Configure preview +
+
+ + + {payloadError &&

{payloadError}

} + +
+
+
+ ); +}; diff --git a/apps/dashboard/src/components/workflow-editor/steps/sms/sms-editor-preview.tsx b/apps/dashboard/src/components/workflow-editor/steps/sms/sms-editor-preview.tsx index 8f28420699b..8848158b799 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/sms/sms-editor-preview.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/sms/sms-editor-preview.tsx @@ -1,61 +1,25 @@ -import { type StepDataDto, type WorkflowResponseDto } from '@novu/shared'; -import { CSSProperties, useEffect, useRef, useState } from 'react'; - import { Sms } from '@/components/icons'; -import { Code2 } from '@/components/icons/code-2'; -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/primitives/accordion'; -import { Button } from '@/components/primitives/button'; -import { Editor } from '@/components/primitives/editor'; import { SmsPreview } from '@/components/workflow-editor/steps/sms/sms-preview'; -import { loadLanguage } from '@uiw/codemirror-extensions-langs'; -import { useEditorPreview } from '../use-editor-preview'; import { InlineToast } from '@/components/primitives/inline-toast'; import { TabsSection } from '@/components/workflow-editor/steps/tabs-section'; - -const getInitialAccordionValue = (value: string) => { - try { - return Object.keys(JSON.parse(value)).length > 0 ? 'payload' : undefined; - } catch (e) { - return undefined; - } -}; +import { ConfigurePreviewAccordion } from '../shared/configure-preview-accordion'; +import { GeneratePreviewResponseDto } from '@novu/shared'; type SmsEditorPreviewProps = { - workflow: WorkflowResponseDto; - step: StepDataDto; - formValues: Record; + editorValue: string; + setEditorValue: (value: string) => void; + previewStep: () => void; + previewData?: GeneratePreviewResponseDto; + isPreviewPending: boolean; }; -const extensions = [loadLanguage('json')?.extension ?? []]; - -export const SmsEditorPreview = ({ workflow, step, formValues }: SmsEditorPreviewProps) => { - const workflowSlug = workflow.workflowId; - const stepSlug = step.stepId; - const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({ - workflowSlug, - stepSlug, - controlValues: formValues, - }); - const [accordionValue, setAccordionValue] = useState(getInitialAccordionValue(editorValue)); - const [payloadError, setPayloadError] = useState(''); - const [height, setHeight] = useState(0); - const contentRef = useRef(null); - - useEffect(() => { - setAccordionValue(getInitialAccordionValue(editorValue)); - }, [editorValue]); - - useEffect(() => { - const timeout = setTimeout(() => { - if (contentRef.current) { - const rect = contentRef.current.getBoundingClientRect(); - setHeight(rect.height); - } - }, 0); - - return () => clearTimeout(timeout); - }, [editorValue]); - +export const SmsEditorPreview = ({ + editorValue, + setEditorValue, + previewStep, + previewData, + isPreviewPending = false, +}: SmsEditorPreviewProps) => { return (
@@ -70,46 +34,7 @@ export const SmsEditorPreview = ({ workflow, step, formValues }: SmsEditorPrevie className="w-full px-3" />
- - - -
- - Configure preview -
-
- - - {payloadError &&

{payloadError}

} - -
-
-
+
); diff --git a/apps/dashboard/src/components/workflow-editor/steps/sms/sms-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/sms/sms-tabs.tsx index 5bda1b6e89f..dc32dd5730c 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/sms/sms-tabs.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/sms/sms-tabs.tsx @@ -6,6 +6,7 @@ import { TemplateTabs } from '@/components/workflow-editor/steps/template-tabs'; import { WorkflowOriginEnum } from '@novu/shared'; import { useState } from 'react'; import { useFormContext } from 'react-hook-form'; +import { useEditorPreview } from '../use-editor-preview'; export const SmsTabs = (props: StepEditorProps) => { const { workflow, step } = props; @@ -13,6 +14,12 @@ export const SmsTabs = (props: StepEditorProps) => { const form = useFormContext(); const [tabsValue, setTabsValue] = useState('editor'); + const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({ + workflowSlug: workflow.workflowId, + stepSlug: step.stepId, + controlValues: form.getValues(), + }); + const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema; const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL; @@ -23,7 +30,15 @@ export const SmsTabs = (props: StepEditorProps) => { ); - const previewContent = ; + const previewContent = ( + + ); return ( { previewContent={previewContent} tabsValue={tabsValue} onTabChange={setTabsValue} + previewStep={previewStep} /> ); }; diff --git a/apps/dashboard/src/components/workflow-editor/steps/template-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/template-tabs.tsx index 3f3b31fdea3..bbe587ec1ce 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/template-tabs.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/template-tabs.tsx @@ -1,6 +1,7 @@ import { Cross2Icon } from '@radix-ui/react-icons'; import { RiEdit2Line, RiPencilRuler2Line } from 'react-icons/ri'; import { useNavigate } from 'react-router-dom'; +import React, { useEffect } from 'react'; import { Notification5Fill } from '@/components/icons'; import { Button } from '@/components/primitives/button'; @@ -12,11 +13,26 @@ interface TemplateTabsProps { previewContent?: React.ReactNode; tabsValue: string; onTabChange: (tab: string) => void; + previewStep?: () => void; } -export const TemplateTabs = ({ editorContent, previewContent, tabsValue, onTabChange }: TemplateTabsProps) => { +export const TemplateTabs = ({ + editorContent, + previewContent, + tabsValue, + onTabChange, + previewStep, +}: TemplateTabsProps) => { const navigate = useNavigate(); + useEffect(() => { + // We reload the preview when the tab changes to get the latest values + if (tabsValue === 'preview') { + previewStep?.(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [tabsValue]); + return (
diff --git a/apps/dashboard/src/components/workflow-editor/steps/use-editor-preview.tsx b/apps/dashboard/src/components/workflow-editor/steps/use-editor-preview.tsx index 73e7c78037c..181b33fe62f 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/use-editor-preview.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/use-editor-preview.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; import * as Sentry from '@sentry/react'; +import isEqual from 'lodash.isequal'; import { usePreviewStep } from '@/hooks/use-preview-step'; import { useDataRef } from '@/hooks/use-data-ref'; @@ -20,7 +21,10 @@ export const useEditorPreview = ({ isPending: isPreviewPending, } = usePreviewStep({ onSuccess: (res) => { - setEditorValue(JSON.stringify(res.previewPayloadExample, null, 2)); + const newValue = JSON.stringify(res.previewPayloadExample, null, 2); + if (!isEqual(editorValue, newValue)) { + setEditorValue(newValue); + } }, onError: (error) => { Sentry.captureException(error); diff --git a/apps/dashboard/src/hooks/use-preview-step.ts b/apps/dashboard/src/hooks/use-preview-step.ts index a31693f44ab..8fb2bb7590f 100644 --- a/apps/dashboard/src/hooks/use-preview-step.ts +++ b/apps/dashboard/src/hooks/use-preview-step.ts @@ -11,11 +11,13 @@ export const usePreviewStep = ({ onError, }: { onSuccess?: (data: GeneratePreviewResponseDto) => void; onError?: (error: Error) => void } = {}) => { const { currentEnvironment } = useEnvironment(); - const { mutateAsync, isPending, error, data } = useMutation({ - mutationFn: (args: PreviewStepParameters) => previewStep({ environment: currentEnvironment!, ...args }), - onSuccess, - onError, - }); + const { mutateAsync, isPending, error, data } = useMutation( + { + mutationFn: (args: PreviewStepParameters) => previewStep({ environment: currentEnvironment!, ...args }), + onSuccess, + onError, + } + ); return { previewStep: mutateAsync,