diff --git a/apps/dashboard/src/components/workflow-editor/nodes.tsx b/apps/dashboard/src/components/workflow-editor/nodes.tsx index 30279f9b9f5..a93228d0b9d 100644 --- a/apps/dashboard/src/components/workflow-editor/nodes.tsx +++ b/apps/dashboard/src/components/workflow-editor/nodes.tsx @@ -167,17 +167,19 @@ export const DelayNode = (props: NodeProps) => { const Icon = STEP_TYPE_TO_ICON[StepTypeEnum.DELAY]; return ( - - - - - - {data.name || 'Delay Step'} - - {data.content || 'You have been invited to the Novu party on "commentSnippet"'} - - - + + + + + + + {data.name || 'Delay Step'} + + {data.content || 'You have been invited to the Novu party on "commentSnippet"'} + + + + ); }; @@ -186,19 +188,21 @@ export const DigestNode = (props: NodeProps) => { const Icon = STEP_TYPE_TO_ICON[StepTypeEnum.DIGEST]; return ( - - - - - - {data.name || 'Digest Step'} - - - {data.content || 'Batches events into one coherent message before delivery to the subscriber.'} - - - - + + + + + + + {data.name || 'Digest Step'} + + + {data.content || 'Batches events into one coherent message before delivery to the subscriber.'} + + + + + ); }; @@ -207,17 +211,19 @@ export const CustomNode = (props: NodeProps) => { const Icon = STEP_TYPE_TO_ICON[StepTypeEnum.CUSTOM]; return ( - - - - - - {data.name || 'Custom Step'} - - Executes the business logic in your bridge application - - - + + + + + + + {data.name || 'Custom Step'} + + Executes the business logic in your bridge application + + + + ); }; diff --git a/apps/dashboard/src/components/workflow-editor/steps/chat.tsx b/apps/dashboard/src/components/workflow-editor/steps/chat.tsx deleted file mode 100644 index c49dba1c96b..00000000000 --- a/apps/dashboard/src/components/workflow-editor/steps/chat.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Separator } from '@/components/primitives/separator'; -import { CommonFields } from './common-fields'; -import { SdkBanner } from './sdk-banner'; - -export default function Chat() { - return ( - <> -
- -
- -
- -
- - ); -} diff --git a/apps/dashboard/src/components/workflow-editor/steps/configure-other-steps.tsx b/apps/dashboard/src/components/workflow-editor/steps/configure-other-steps.tsx new file mode 100644 index 00000000000..d5a00f5d988 --- /dev/null +++ b/apps/dashboard/src/components/workflow-editor/steps/configure-other-steps.tsx @@ -0,0 +1,38 @@ +/** + * This component is used as a placeholder for the other step configuration until the actual configuration is implemented. + */ +import { Separator } from '@/components/primitives/separator'; +import { CommonFields } from './common-fields'; +import { SdkBanner } from './sdk-banner'; +import { Link } from 'react-router-dom'; +import { Button } from '@/components/primitives/button'; +import { RiArrowRightSLine, RiPencilRuler2Fill } from 'react-icons/ri'; + +export default function ConfigureOtherSteps({ channelName }: { channelName?: string }) { + return ( + <> +
+ +
+ + + {channelName && ( + <> +
+ + + +
+ + + )} + +
+ +
+ + ); +} diff --git a/apps/dashboard/src/components/workflow-editor/steps/configure-step.tsx b/apps/dashboard/src/components/workflow-editor/steps/configure-step.tsx index 64393aae649..ebb1e71acab 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/configure-step.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/configure-step.tsx @@ -10,7 +10,7 @@ import { buildRoute, ROUTES } from '@/utils/routes'; import { motion } from 'framer-motion'; import { ConfigureInApp } from './in-app/configure-in-app'; import { useStep } from './use-step'; -import Chat from './chat'; +import ConfigureOtherSteps from './configure-other-steps'; import { useState } from 'react'; import { ConfirmationModal } from '@/components/confirmation-modal'; @@ -104,11 +104,23 @@ const Step = ({ stepType }: { stepType?: StepTypeEnum }) => { case StepTypeEnum.IN_APP: return ; + case StepTypeEnum.EMAIL: + return ; + + case StepTypeEnum.SMS: + return ; + + case StepTypeEnum.PUSH: + return ; + + case StepTypeEnum.CHAT: + return ; + /** * TODO: Add other step types here * For now, it is just a placeholder with the use sdk banner */ default: - return ; + return ; } }; diff --git a/apps/dashboard/src/components/workflow-editor/steps/controls/custom-step-controls.tsx b/apps/dashboard/src/components/workflow-editor/steps/controls/custom-step-controls.tsx index 9000a3d5b92..61456a675ea 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/controls/custom-step-controls.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/controls/custom-step-controls.tsx @@ -4,11 +4,18 @@ import { RiArrowDownSLine, RiArrowUpSLine, RiInputField } from 'react-icons/ri'; import { type ControlsMetadata } from '@novu/shared'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/primitives/collapsible'; import { JsonForm } from './json-form'; +import { WorkflowOriginEnum } from '@/utils/enums'; -export function CustomStepControls({ dataSchema }: { dataSchema: ControlsMetadata['dataSchema'] }) { +export function CustomStepControls({ + dataSchema, + origin, +}: { + dataSchema: ControlsMetadata['dataSchema']; + origin: WorkflowOriginEnum; +}) { const [isEditorOpen, setIsEditorOpen] = useState(true); - if (!dataSchema) { + if (!dataSchema || origin !== WorkflowOriginEnum.EXTERNAL) { return null; } diff --git a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx index 9eff060ef0f..d8584f31c01 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx @@ -42,6 +42,7 @@ export const InAppBody = () => { ]} value={field.value} onChange={(val) => field.onChange(val)} + height="100%" /> 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 6537dc11c6e..e4e2712a883 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 @@ -156,7 +156,7 @@ export const InAppTabs = ({ workflow, step }: { workflow: WorkflowResponseDto; s - + { + const { stepSlug = '' } = useParams<{ workflowSlug: string; stepSlug: string }>(); + const { dataSchema, uiSchema } = step.controls; + const navigate = useNavigate(); + const schema = buildDynamicZodSchema(dataSchema ?? {}); + const form = useForm({ + mode: 'onSubmit', + resolver: zodResolver(schema), + resetOptions: { keepDirtyValues: true }, + defaultValues: buildDefaultValues(uiSchema ?? {}), + values: step.controls.values, + }); + + const { reset, formState } = form; + + const { updateWorkflow } = useUpdateWorkflow({ + onSuccess: () => { + showToast({ + children: () => ( + <> + + Saved + + ), + options: { + position: 'bottom-right', + classNames: { + toast: 'ml-10 mb-4', + }, + }, + }); + }, + onError: () => { + showToast({ + children: () => ( + <> + + Failed to save + + ), + options: { + position: 'bottom-right', + classNames: { + toast: 'ml-10 mb-4', + }, + }, + }); + }, + }); + + const onSubmit = async (data: any) => { + await updateWorkflow({ + id: workflow._id, + workflow: { + ...workflow, + steps: workflow.steps.map((step) => (step.slug === stepSlug ? { ...step, controlValues: { ...data } } : step)), + }, + }); + reset({ ...data }); + }; + + return ( +
+ { + event.preventDefault(); + event.stopPropagation(); + form.handleSubmit(onSubmit)(event); + }} + > + +
+
+ + Configure Template +
+ + + + Editor + + + + Preview + + + + +
+ + + + + +
+ +
+
+
+ + ); +}; diff --git a/apps/dashboard/src/components/workflow-editor/steps/step-editor.tsx b/apps/dashboard/src/components/workflow-editor/steps/step-editor.tsx index b6dda55c497..cfd892c729f 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/step-editor.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/step-editor.tsx @@ -1,15 +1,16 @@ import { type StepDataDto, StepTypeEnum, type WorkflowResponseDto } from '@novu/shared'; import { InAppTabs } from '@/components/workflow-editor/steps/in-app/in-app-tabs'; +import { OtherStepTabs } from './other-steps-tabs'; const STEP_TYPE_TO_EDITOR: Record< StepTypeEnum, (args: { workflow: WorkflowResponseDto; step: StepDataDto }) => React.JSX.Element > = { - [StepTypeEnum.EMAIL]: () =>
EMAIL Editor
, - [StepTypeEnum.CHAT]: () =>
CHAT Editor
, + [StepTypeEnum.EMAIL]: OtherStepTabs, + [StepTypeEnum.CHAT]: OtherStepTabs, [StepTypeEnum.IN_APP]: InAppTabs, - [StepTypeEnum.SMS]: () =>
SMS Editor
, - [StepTypeEnum.PUSH]: () =>
PUSH Editor
, + [StepTypeEnum.SMS]: OtherStepTabs, + [StepTypeEnum.PUSH]: OtherStepTabs, [StepTypeEnum.DIGEST]: () =>
DIGEST Editor
, [StepTypeEnum.DELAY]: () =>
DELAY Editor
, [StepTypeEnum.TRIGGER]: () =>
TRIGGER Editor
,