Skip to content

Commit

Permalink
feat(dashboard): Open step template drawer on Add Step
Browse files Browse the repository at this point in the history
  • Loading branch information
desiprisg committed Dec 31, 2024
1 parent d7f5cf2 commit 5635caa
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 70 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { useMemo } from 'react';
import { Link } from 'react-router-dom';
import { zodResolver } from '@hookform/resolvers/zod';
import { ChannelTypeEnum, WorkflowPreferences, WorkflowResponseDto } from '@novu/shared';
import { motion } from 'motion/react';
import { useMemo } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import { RiArrowLeftSLine, RiCloseFill, RiInformationFill } from 'react-icons/ri';
import { ChannelTypeEnum, UpdateWorkflowDto, WorkflowPreferences, WorkflowResponseDto } from '@novu/shared';
import { Link } from 'react-router-dom';
import { z } from 'zod';

import { SidebarContent, SidebarHeader } from '@/components/side-navigation/sidebar';
import { UserPreferencesFormSchema } from '@/components/workflow-editor/schema';
import { UpdateWorkflowFn } from '@/components/workflow-editor/workflow-provider';
import { useTelemetry } from '@/hooks/use-telemetry';
import { STEP_TYPE_TO_COLOR } from '@/utils/color';
import { StepTypeEnum, WorkflowOriginEnum } from '@/utils/enums';
import { capitalize } from '@/utils/string';
import { TelemetryEvent } from '@/utils/telemetry';
import { cn } from '@/utils/ui';
import { STEP_TYPE_TO_ICON } from '../icons/utils';
import { PageMeta } from '../page-meta';
Expand All @@ -22,13 +25,11 @@ import { Form, FormControl, FormField, FormItem, FormLabel } from '../primitives
import { Separator } from '../primitives/separator';
import { Step } from '../primitives/step';
import { Switch } from '../primitives/switch';
import { useTelemetry } from '@/hooks/use-telemetry';
import { TelemetryEvent } from '@/utils/telemetry';
import { Tooltip, TooltipContent, TooltipTrigger } from '../primitives/tooltip';

type ConfigureWorkflowFormProps = {
workflow: WorkflowResponseDto;
update: (data: UpdateWorkflowDto) => void;
update: UpdateWorkflowFn;
};

const CHANNEL_LABELS_LOOKUP: Record<`${ChannelTypeEnum}` | 'all', string> = {
Expand Down Expand Up @@ -87,10 +88,12 @@ export const ChannelPreferencesForm = (props: ConfigureWorkflowFormProps) => {

const updateUserPreference = (userPreferences: WorkflowPreferences | null) => {
update({
...workflow,
preferences: {
...workflow.preferences,
user: userPreferences,
data: {
...workflow,
preferences: {
...workflow.preferences,
user: userPreferences,
},
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ToastIcon } from '@/components/primitives/sonner';
import { showToast } from '@/components/primitives/sonner-helpers';
import { SidebarContent, SidebarHeader } from '@/components/side-navigation/sidebar';
import { MAX_DESCRIPTION_LENGTH, workflowSchema } from '@/components/workflow-editor/schema';
import { UpdateWorkflowFn } from '@/components/workflow-editor/workflow-provider';
import { useEnvironment } from '@/context/environment/hooks';
import { useDeleteWorkflow } from '@/hooks/use-delete-workflow';
import { useFormAutosave } from '@/hooks/use-form-autosave';
Expand All @@ -26,7 +27,7 @@ import { useTags } from '@/hooks/use-tags';
import { ROUTES } from '@/utils/routes';
import { cn } from '@/utils/ui';
import { zodResolver } from '@hookform/resolvers/zod';
import { UpdateWorkflowDto, WorkflowOriginEnum, WorkflowResponseDto } from '@novu/shared';
import { WorkflowOriginEnum, WorkflowResponseDto } from '@novu/shared';
import {
RiArrowRightSLine,
RiCodeSSlashLine,
Expand Down Expand Up @@ -54,7 +55,7 @@ import { usePromotionalBanner } from '../promotional/coming-soon-banner';

type ConfigureWorkflowFormProps = {
workflow: WorkflowResponseDto;
update: (data: UpdateWorkflowDto) => void;
update: UpdateWorkflowFn;
};

const toastOptions: ExternalToast = {
Expand Down Expand Up @@ -132,7 +133,7 @@ export const ConfigureWorkflowForm = (props: ConfigureWorkflowFormProps) => {
});

const { onBlur, saveForm } = useFormAutosave({
previousData: workflow,
previousData: { data: workflow },
form,
isReadOnly,
save: update,
Expand Down
28 changes: 22 additions & 6 deletions apps/dashboard/src/components/workflow-editor/edges.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { BaseEdge, Edge, EdgeLabelRenderer, EdgeProps, getBezierPath } from '@xyflow/react';
import { AddStepMenu } from './add-step-menu';
import { createStep } from '@/components/workflow-editor/step-utils';
import { useWorkflow } from '@/components/workflow-editor/workflow-provider';
import { AUTO_OPEN_DRAWER_AFTER_CREATION_STEP_TYPES } from '@/utils/constants';
import { buildRoute, ROUTES } from '@/utils/routes';
import { WorkflowOriginEnum } from '@novu/shared';
import { createStep } from '@/components/workflow-editor/step-utils';
import { BaseEdge, Edge, EdgeLabelRenderer, EdgeProps, getBezierPath } from '@xyflow/react';
import { useNavigate } from 'react-router-dom';
import { AddStepMenu } from './add-step-menu';

export type AddNodeEdgeType = Edge<{ isLast: boolean; addStepIndex: number }>;

Expand All @@ -18,6 +21,7 @@ export function AddNodeEdge({
markerEnd,
}: EdgeProps<AddNodeEdgeType>) {
const { workflow, update } = useWorkflow();
const navigate = useNavigate();
const isReadOnly = workflow?.origin === WorkflowOriginEnum.EXTERNAL;

const [edgePath, labelX, labelY] = getBezierPath({
Expand Down Expand Up @@ -47,7 +51,7 @@ export function AddNodeEdge({
>
{!isReadOnly && (
<AddStepMenu
onMenuItemClick={(stepType) => {
onMenuItemClick={async (stepType) => {
if (workflow) {
const indexToAdd = data.addStepIndex;

Expand All @@ -60,8 +64,20 @@ export function AddNodeEdge({
];

update({
...workflow,
steps: updatedSteps,
data: {
...workflow,
steps: updatedSteps,
},
onSuccess: (data) => {
if (AUTO_OPEN_DRAWER_AFTER_CREATION_STEP_TYPES.includes(stepType)) {
navigate(
buildRoute(ROUTES.EDIT_STEP_TEMPLATE, {
workflowSlug: workflow.slug,
stepSlug: data.steps[indexToAdd].slug,
})
);
}
},
});
}
}}
Expand Down
39 changes: 27 additions & 12 deletions apps/dashboard/src/components/workflow-editor/nodes.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { Handle, Node as FlowNode, NodeProps, Position } from '@xyflow/react';
import { RiPlayCircleLine } from 'react-icons/ri';
import { Link, useParams } from 'react-router-dom';
import { STEP_TYPE_TO_ICON } from '../icons/utils';
import { AddStepMenu } from './add-step-menu';
import { Node, NodeBody, NodeError, NodeHeader, NodeIcon, NodeName } from './base-node';
import { createStep } from '@/components/workflow-editor/step-utils';
import { useWorkflow } from '@/components/workflow-editor/workflow-provider';
import { STEP_TYPE_TO_COLOR } from '@/utils/color';
import { AUTO_OPEN_DRAWER_AFTER_CREATION_STEP_TYPES } from '@/utils/constants';
import { StepTypeEnum } from '@/utils/enums';
import { buildRoute, ROUTES } from '@/utils/routes';
import { ComponentProps } from 'react';
import { getWorkflowIdFromSlug, STEP_DIVIDER } from '@/utils/step';
import { cn } from '@/utils/ui';
import { STEP_TYPE_TO_COLOR } from '@/utils/color';
import { useWorkflow } from '@/components/workflow-editor/workflow-provider';
import { WorkflowOriginEnum } from '@novu/shared';
import { createStep } from '@/components/workflow-editor/step-utils';
import { getWorkflowIdFromSlug, STEP_DIVIDER } from '@/utils/step';
import { Node as FlowNode, Handle, NodeProps, Position } from '@xyflow/react';
import { steps } from 'motion/react';
import { ComponentProps } from 'react';
import { RiPlayCircleLine } from 'react-icons/ri';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { STEP_TYPE_TO_ICON } from '../icons/utils';
import { AddStepMenu } from './add-step-menu';
import { Node, NodeBody, NodeError, NodeHeader, NodeIcon, NodeName } from './base-node';

export type NodeData = {
addStepIndex?: number;
Expand Down Expand Up @@ -244,6 +246,7 @@ export const CustomNode = (props: NodeProps<NodeType>) => {

export const AddNode = (_props: NodeProps<NodeType>) => {
const { workflow, update } = useWorkflow();
const navigate = useNavigate();
if (!workflow) {
return null;
}
Expand All @@ -259,7 +262,19 @@ export const AddNode = (_props: NodeProps<NodeType>) => {
<AddStepMenu
visible
onMenuItemClick={(stepType) => {
update({ ...workflow, steps: [...workflow.steps, createStep(stepType)] });
update({
data: { ...workflow, steps: [...workflow.steps, createStep(stepType)] },
onSuccess: (data) => {
if (AUTO_OPEN_DRAWER_AFTER_CREATION_STEP_TYPES.includes(stepType)) {
navigate(
buildRoute(ROUTES.EDIT_STEP_TEMPLATE, {
workflowSlug: workflow.slug,
stepSlug: data.steps[steps.length - 1].slug,
})
);
}
},
});
}}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
StepDataDto,
StepTypeEnum,
StepUpdateDto,
UpdateWorkflowDto,
WorkflowOriginEnum,
WorkflowResponseDto,
} from '@novu/shared';
Expand All @@ -22,31 +21,32 @@ import { Input, InputField } from '@/components/primitives/input';
import { Separator } from '@/components/primitives/separator';
import { SidebarContent, SidebarFooter, SidebarHeader } from '@/components/side-navigation/sidebar';
import TruncatedText from '@/components/truncated-text';
import { getStepDefaultValues } from '@/components/workflow-editor/step-default-values';
import {
flattenIssues,
getFirstBodyErrorMessage,
getFirstControlsErrorMessage,
updateStepInWorkflow,
} from '@/components/workflow-editor/step-utils';
import { ConfigureChatStepPreview } from '@/components/workflow-editor/steps/chat/configure-chat-step-preview';
import { ConfigureStepTemplateIssueCta } from '@/components/workflow-editor/steps/configure-step-template-issue-cta';
import { DelayControlValues } from '@/components/workflow-editor/steps/delay/delay-control-values';
import { DigestControlValues } from '@/components/workflow-editor/steps/digest/digest-control-values';
import { ConfigureEmailStepPreview } from '@/components/workflow-editor/steps/email/configure-email-step-preview';
import { ConfigureInAppStepPreview } from '@/components/workflow-editor/steps/in-app/configure-in-app-step-preview';
import { ConfigurePushStepPreview } from '@/components/workflow-editor/steps/push/configure-push-step-preview';
import { SaveFormContext } from '@/components/workflow-editor/steps/save-form-context';
import { SdkBanner } from '@/components/workflow-editor/steps/sdk-banner';
import { ConfigureSmsStepPreview } from '@/components/workflow-editor/steps/sms/configure-sms-step-preview';
import { UpdateWorkflowFn } from '@/components/workflow-editor/workflow-provider';
import { useFormAutosave } from '@/hooks/use-form-autosave';
import {
AUTOCOMPLETE_PASSWORD_MANAGERS_OFF,
INLINE_CONFIGURABLE_STEP_TYPES,
STEP_TYPE_LABELS,
TEMPLATE_CONFIGURABLE_STEP_TYPES,
} from '@/utils/constants';
import { getStepDefaultValues } from '@/components/workflow-editor/step-default-values';
import { buildRoute, ROUTES } from '@/utils/routes';
import { ConfigurePushStepPreview } from '@/components/workflow-editor/steps/push/configure-push-step-preview';
import { ConfigureChatStepPreview } from '@/components/workflow-editor/steps/chat/configure-chat-step-preview';

const STEP_TYPE_TO_INLINE_CONTROL_VALUES: Record<StepTypeEnum, () => React.JSX.Element | null> = {
[StepTypeEnum.DELAY]: DelayControlValues,
Expand Down Expand Up @@ -76,7 +76,7 @@ type ConfigureStepFormProps = {
workflow: WorkflowResponseDto;
environment: IEnvironment;
step: StepDataDto;
update: (data: UpdateWorkflowDto) => void;
update: UpdateWorkflowFn;
};

export const ConfigureStepForm = (props: ConfigureStepFormProps) => {
Expand All @@ -103,7 +103,7 @@ export const ConfigureStepForm = (props: ConfigureStepFormProps) => {
const isInlineConfigurableStepWithCustomControls = isInlineConfigurableStep && hasCustomControls;

const onDeleteStep = () => {
update({ ...workflow, steps: workflow.steps.filter((s) => s._id !== step._id) });
update({ data: { ...workflow, steps: workflow.steps.filter((s) => s._id !== step._id) } });
navigate(buildRoute(ROUTES.EDIT_WORKFLOW, { environmentSlug: environment.slug!, workflowSlug: workflow.slug }));
};

Expand Down Expand Up @@ -143,7 +143,7 @@ export const ConfigureStepForm = (props: ConfigureStepFormProps) => {
name: data.name,
...(data.controlValues ? { controlValues: data.controlValues } : {}),
};
update(updateStepInWorkflow(workflow, step.stepId, updateStepData));
update({ data: updateStepInWorkflow(workflow, step.stepId, updateStepData) });
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import { type StepDataDto, StepTypeEnum, StepUpdateDto, type WorkflowResponseDto } from '@novu/shared';
import { useCallback, useEffect, useMemo } from 'react';
import { useForm } from 'react-hook-form';
import {
type StepDataDto,
StepTypeEnum,
StepUpdateDto,
UpdateWorkflowDto,
type WorkflowResponseDto,
} from '@novu/shared';

import { flattenIssues, updateStepInWorkflow } from '@/components/workflow-editor/step-utils';
import { Form } from '@/components/primitives/form/form';
import { EmailTabs } from '@/components/workflow-editor/steps/email/email-tabs';
import { getStepDefaultValues } from '@/components/workflow-editor/step-default-values';
import { flattenIssues, updateStepInWorkflow } from '@/components/workflow-editor/step-utils';
import { ChatTabs } from '@/components/workflow-editor/steps/chat/chat-tabs';
import { CommonCustomControlValues } from '@/components/workflow-editor/steps/common/common-custom-control-values';
import { EmailTabs } from '@/components/workflow-editor/steps/email/email-tabs';
import { InAppTabs } from '@/components/workflow-editor/steps/in-app/in-app-tabs';
import { PushTabs } from '@/components/workflow-editor/steps/push/push-tabs';
import { SaveFormContext } from '@/components/workflow-editor/steps/save-form-context';
import { SmsTabs } from '@/components/workflow-editor/steps/sms/sms-tabs';
import { ChatTabs } from '@/components/workflow-editor/steps/chat/chat-tabs';
import { UpdateWorkflowFn } from '@/components/workflow-editor/workflow-provider';
import { useFormAutosave } from '@/hooks/use-form-autosave';
import { CommonCustomControlValues } from '@/components/workflow-editor/steps/common/common-custom-control-values';

const STEP_TYPE_TO_TEMPLATE_FORM: Record<StepTypeEnum, (args: StepEditorProps) => React.JSX.Element | null> = {
[StepTypeEnum.EMAIL]: EmailTabs,
Expand All @@ -38,7 +33,7 @@ export type StepEditorProps = {
};

type ConfigureStepTemplateFormProps = StepEditorProps & {
update: (data: UpdateWorkflowDto) => void;
update: UpdateWorkflowFn;
};

export const ConfigureStepTemplateForm = (props: ConfigureStepTemplateFormProps) => {
Expand All @@ -58,7 +53,7 @@ export const ConfigureStepTemplateForm = (props: ConfigureStepTemplateFormProps)
const updateStepData: Partial<StepUpdateDto> = {
controlValues: data,
};
update(updateStepInWorkflow(workflow, step.stepId, updateStepData));
update({ data: updateStepInWorkflow(workflow, step.stepId, updateStepData) });
},
});

Expand Down
44 changes: 27 additions & 17 deletions apps/dashboard/src/components/workflow-editor/workflow-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,39 @@ import { PatchWorkflowDto, StepDataDto, UpdateWorkflowDto, WorkflowResponseDto }
import { createContext, ReactNode, useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react';
import { useBlocker, useNavigate, useParams } from 'react-router-dom';

import { useEnvironment } from '@/context/environment/hooks';
import { useFetchWorkflow } from '@/hooks/use-fetch-workflow';
import { useUpdateWorkflow } from '@/hooks/use-update-workflow';
import { usePatchWorkflow } from '@/hooks/use-patch-workflow';
import { createContextHook } from '@/utils/context';
import { buildRoute, ROUTES } from '@/utils/routes';
import { toast } from 'sonner';
import { RiCloseFill } from 'react-icons/ri';
import {
AlertDialog,
AlertDialogHeader,
AlertDialogContent,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/primitives/alert-dialog';
import { RiAlertFill } from 'react-icons/ri';
import { CheckCircleIcon } from 'lucide-react';
import { useEnvironment } from '@/context/environment/hooks';
import { useFetchWorkflow } from '@/hooks/use-fetch-workflow';
import { useInvocationQueue } from '@/hooks/use-invocation-queue';
import { usePatchWorkflow } from '@/hooks/use-patch-workflow';
import { useUpdateWorkflow } from '@/hooks/use-update-workflow';
import { createContextHook } from '@/utils/context';
import { buildRoute, ROUTES } from '@/utils/routes';
import { getWorkflowIdFromSlug, STEP_DIVIDER } from '@/utils/step';
import { CheckCircleIcon } from 'lucide-react';
import { RiAlertFill, RiCloseFill } from 'react-icons/ri';
import { toast } from 'sonner';
import { showErrorToast, showSavingToast, showSuccessToast } from './toasts';
import { STEP_DIVIDER } from '@/utils/step';
import { getWorkflowIdFromSlug } from '@/utils/step';

export type UpdateWorkflowFn = ({
data,
onSuccess,
}: {
data: UpdateWorkflowDto;
onSuccess?: (workflow: WorkflowResponseDto) => void;
}) => void;

export type WorkflowContextType = {
isPending: boolean;
workflow?: WorkflowResponseDto;
step?: StepDataDto;
update: (data: UpdateWorkflowDto) => void;
update: UpdateWorkflowFn;
patch: (data: PatchWorkflowDto) => void;
};

Expand Down Expand Up @@ -105,9 +111,13 @@ export const WorkflowProvider = ({ children }: { children: ReactNode }) => {
const isUpdatePatchPending = isPatchPending || isUpdatePending || hasPendingItems;

const update = useCallback(
(data: UpdateWorkflowDto) => {
({ data, onSuccess }: { data: UpdateWorkflowDto; onSuccess?: (workflow: WorkflowResponseDto) => void }) => {
if (workflow) {
enqueue(() => updateWorkflow({ workflowSlug: workflow.slug, workflow: { ...data } }));
enqueue(async () => {
const res = await updateWorkflow({ workflowSlug: workflow.slug, workflow: { ...data } });
onSuccess?.(res);
return res;
});
}
},
[enqueue, updateWorkflow, workflow]
Expand Down
Loading

0 comments on commit 5635caa

Please sign in to comment.