Skip to content

Commit

Permalink
feat(dashboard): Nv 4885 push step editor (#7306)
Browse files Browse the repository at this point in the history
  • Loading branch information
BiswaViraj authored Dec 17, 2024
1 parent c27185e commit 75af9d3
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const AddStepMenu = ({
}) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const areNewStepsEnabled = useFeatureFlag(FeatureFlagsKeysEnum.IS_ND_DELAY_DIGEST_EMAIL_ENABLED);
const arePushChatSMSEnabled = useFeatureFlag(FeatureFlagsKeysEnum.IS_ND_SMS_CHAT_PUSH_ENABLED);

const handleMenuItemClick = (stepType: StepTypeEnum) => {
onMenuItemClick(stepType);
Expand Down Expand Up @@ -123,7 +124,13 @@ export const AddStepMenu = ({
>
In-App
</MenuItem>
<MenuItem stepType={StepTypeEnum.PUSH}>Push</MenuItem>
<MenuItem
stepType={StepTypeEnum.PUSH}
disabled={!arePushChatSMSEnabled}
onClick={() => handleMenuItemClick(StepTypeEnum.PUSH)}
>
Push
</MenuItem>
<MenuItem stepType={StepTypeEnum.CHAT}>Chat</MenuItem>
<MenuItem stepType={StepTypeEnum.SMS}>SMS</MenuItem>
</MenuItemsGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { EditorView } from '@uiw/react-codemirror';
import { useMemo } from 'react';
import { useFormContext } from 'react-hook-form';

import { Editor } from '@/components/primitives/editor';
import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form';
import { InputField } from '@/components/primitives/input';
import { completions } from '@/utils/liquid-autocomplete';
import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables';
import { capitalize } from '@/utils/string';
import { autocompletion } from '@codemirror/autocomplete';
import { useWorkflow } from '@/components/workflow-editor/workflow-provider';

const bodyKey = 'body';

const basicSetup = {
defaultKeymap: true,
};

export const BaseBody = () => {
const { control } = useFormContext();
const { step } = useWorkflow();
const variables = useMemo(() => (step ? parseStepVariablesToLiquidVariables(step.variables) : []), [step]);
const extensions = useMemo(
() => [autocompletion({ override: [completions(variables)] }), EditorView.lineWrapping],
[variables]
);

return (
<FormField
control={control}
name={bodyKey}
render={({ field }) => (
<FormItem className="w-full">
<FormControl>
<InputField className="h-36 px-1">
<Editor
fontFamily="inherit"
placeholder={capitalize(field.name)}
id={field.name}
extensions={extensions}
basicSetup={basicSetup}
ref={field.ref}
value={field.value}
onChange={field.onChange}
height="100%"
/>
</InputField>
</FormControl>
<FormMessage>{`This supports markdown and variables, type { for more.`}</FormMessage>
</FormItem>
)}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useMemo } from 'react';
import { EditorView } from '@uiw/react-codemirror';
import { useFormContext } from 'react-hook-form';

import { Editor } from '@/components/primitives/editor';
import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form';
import { InputField } from '@/components/primitives/input';
import { completions } from '@/utils/liquid-autocomplete';
import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables';
import { capitalize } from '@/utils/string';
import { autocompletion } from '@codemirror/autocomplete';
import { useWorkflow } from '@/components/workflow-editor/workflow-provider';

const subjectKey = 'subject';

export const BaseSubject = () => {
const { control } = useFormContext();
const { step } = useWorkflow();
const variables = useMemo(() => (step ? parseStepVariablesToLiquidVariables(step.variables) : []), [step]);
const extensions = useMemo(
() => [autocompletion({ override: [completions(variables)] }), EditorView.lineWrapping],
[variables]
);

return (
<FormField
control={control}
name={subjectKey}
render={({ field }) => (
<FormItem className="w-full">
<FormControl>
<InputField size="fit" className="px-1">
<Editor
fontFamily="inherit"
placeholder={capitalize(field.name)}
id={field.name}
extensions={extensions}
value={field.value}
onChange={field.onChange}
/>
</InputField>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { Maily } from '@/components/workflow-editor/steps/email/maily';
import { EmailSubject } from '@/components/workflow-editor/steps/email/email-subject';
import { DigestKey } from '@/components/workflow-editor/steps/digest/digest-key';
import { DigestWindow } from '@/components/workflow-editor/steps/digest/digest-window';
import { BaseBody } from './base/base-body';
import { BaseSubject } from './base/base-subject';

export const getComponentByType = ({ component }: { component?: UiComponentEnum }) => {
switch (component) {
Expand Down Expand Up @@ -47,6 +49,13 @@ export const getComponentByType = ({ component }: { component?: UiComponentEnum
case UiComponentEnum.DIGEST_CRON: {
return <DigestWindow />;
}
case UiComponentEnum.PUSH_BODY: {
return <BaseBody />;
}
case UiComponentEnum.PUSH_SUBJECT: {
return <BaseSubject />;
}

default: {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import { useFormAutosave } from '@/hooks/use-form-autosave';
import { SaveFormContext } from '@/components/workflow-editor/steps/save-form-context';
import { EmailTabs } from '@/components/workflow-editor/steps/email/email-tabs';
import { CommonCustomControlValues } from './common/common-custom-control-values';
import { PushTabs } from '@/components/workflow-editor/steps/push/push-tabs';

const STEP_TYPE_TO_TEMPLATE_FORM: Record<StepTypeEnum, (args: StepEditorProps) => React.JSX.Element | null> = {
[StepTypeEnum.EMAIL]: EmailTabs,
[StepTypeEnum.CHAT]: OtherStepTabs,
[StepTypeEnum.IN_APP]: InAppTabs,
[StepTypeEnum.SMS]: OtherStepTabs,
[StepTypeEnum.PUSH]: OtherStepTabs,
[StepTypeEnum.PUSH]: PushTabs,
[StepTypeEnum.DIGEST]: CommonCustomControlValues,
[StepTypeEnum.DELAY]: CommonCustomControlValues,
[StepTypeEnum.TRIGGER]: () => null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { getComponentByType } from '@/components/workflow-editor/steps/component-utils';
import { PushTabsSection } from '@/components/workflow-editor/steps/push/push-tabs-section';
import { type UiSchema } from '@novu/shared';
import { RiCellphoneFill } from 'react-icons/ri';

const subjectKey = 'subject';
const bodyKey = 'body';

type PushEditorProps = { uiSchema: UiSchema };
export const PushEditor = (props: PushEditorProps) => {
const { uiSchema } = props;
const { [bodyKey]: body, [subjectKey]: subject } = uiSchema?.properties ?? {};

return (
<div className="flex h-full flex-col">
<PushTabsSection className="py-5">
<div className="flex items-center gap-2.5 pb-2 text-sm font-medium">
<RiCellphoneFill className="size-3" />
<span>Push template editor</span>
</div>
<div className="flex flex-col gap-1 rounded-lg border border-neutral-100 p-1">
{getComponentByType({ component: subject.component })}
{getComponentByType({ component: body.component })}
</div>
</PushTabsSection>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { cn } from '@/utils/ui';
import { HTMLAttributes } from 'react';

type PushTabsSectionProps = HTMLAttributes<HTMLDivElement>;
export const PushTabsSection = (props: PushTabsSectionProps) => {
const { className, ...rest } = props;
return <div className={cn('px-4 py-3', className)} {...rest} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useState } from 'react';
import { WorkflowOriginEnum } from '@novu/shared';
import { StepEditorProps } from '@/components/workflow-editor/steps/configure-step-template-form';
import { PushEditor } from '@/components/workflow-editor/steps/push/push-editor';
import { CustomStepControls } from '../controls/custom-step-controls';
import { TemplateTabs } from '../template-tabs';

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

const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema;
const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL;

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

const previewContent = <>TODO</>;

return (
<TemplateTabs
editorContent={editorContent}
previewContent={previewContent}
tabsValue={tabsValue}
onTabChange={setTabsValue}
/>
);
};
1 change: 1 addition & 0 deletions packages/shared/src/types/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ export enum FeatureFlagsKeysEnum {
IS_NEW_DASHBOARD_GETTING_STARTED_ENABLED = 'IS_NEW_DASHBOARD_GETTING_STARTED_ENABLED',
IS_ND_DELAY_DIGEST_EMAIL_ENABLED = 'IS_ND_DELAY_DIGEST_EMAIL_ENABLED',
IS_NEW_DASHBOARD_ACTIVITY_FEED_ENABLED = 'IS_NEW_DASHBOARD_ACTIVITY_FEED_ENABLED',
IS_ND_SMS_CHAT_PUSH_ENABLED = 'IS_ND_SMS_CHAT_PUSH_ENABLED',
}

0 comments on commit 75af9d3

Please sign in to comment.