diff --git a/.cspell.json b/.cspell.json
index 667a8bbb5da..16c402de548 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -634,6 +634,7 @@
"prepush",
"xcodebuild",
"liquidjs",
+ "vstack"
"nimma",
"jpath",
"Nimma",
diff --git a/apps/web/panda.config.ts b/apps/web/panda.config.ts
index 753a4204076..d79b37ca47a 100644
--- a/apps/web/panda.config.ts
+++ b/apps/web/panda.config.ts
@@ -38,7 +38,50 @@ export default defineConfig({
* theme-oriented that is unique to `web`, include it below
*/
theme: {
- extend: {},
+ extend: {
+ semanticTokens: {
+ colors: {
+ workflow: {
+ node: {
+ surface: {
+ value: { base: '{colors.legacy.white}', _dark: '{colors.legacy.B17}' },
+ type: 'color',
+ },
+ connector: {
+ value: { base: '{colors.legacy.B40}', _dark: '{colors.legacy.B40}' },
+ type: 'color',
+ },
+ },
+ background: {
+ dots: {
+ value: { base: '{colors.legacy.BGLight}', _dark: '{colors.legacy.B20}' },
+ type: 'color',
+ },
+ },
+ },
+ },
+ spacing: {
+ workflow: {
+ nodes: {
+ gap: {
+ value: '{spacing.250}',
+ type: 'spacing',
+ },
+ },
+ },
+ },
+ sizes: {
+ workflow: {
+ nodes: {
+ gap: {
+ value: '{spacing.250}',
+ type: 'sizes',
+ },
+ },
+ },
+ },
+ },
+ },
},
outExtension: 'js',
diff --git a/apps/web/src/AppRoutes.tsx b/apps/web/src/AppRoutes.tsx
index bfd13afbfba..fce51337f77 100644
--- a/apps/web/src/AppRoutes.tsx
+++ b/apps/web/src/AppRoutes.tsx
@@ -143,7 +143,7 @@ export const AppRoutes = () => {
} />
} />
- } />
+ } />
} />
diff --git a/apps/web/src/studio/components/workflows/StepNode.tsx b/apps/web/src/studio/components/workflows/StepNode.tsx
new file mode 100644
index 00000000000..651158eb005
--- /dev/null
+++ b/apps/web/src/studio/components/workflows/StepNode.tsx
@@ -0,0 +1,46 @@
+import { Title } from '@novu/novui';
+import { css, cx } from '@novu/novui/css';
+import { hstack } from '@novu/novui/patterns';
+import { FC } from 'react';
+import { LocalizedMessage } from '../../../types/LocalizedMessage';
+interface IStepNodeProps {
+ icon: React.ReactNode;
+ title: LocalizedMessage;
+}
+
+export const StepNode: FC = ({ icon, title }) => {
+ return (
+
+ );
+};
diff --git a/apps/web/src/studio/components/workflows/WorkflowFloatingMenu.tsx b/apps/web/src/studio/components/workflows/WorkflowFloatingMenu.tsx
new file mode 100644
index 00000000000..2e5046f072d
--- /dev/null
+++ b/apps/web/src/studio/components/workflows/WorkflowFloatingMenu.tsx
@@ -0,0 +1,99 @@
+import { Tooltip } from '@novu/design-system';
+import { IconButton, Text, type CoreProps, type IconButtonProps } from '@novu/novui';
+import { css, cx } from '@novu/novui/css';
+import {
+ IconOutlineAutoAwesomeMotion,
+ IconOutlineAvTimer,
+ IconOutlineEmail,
+ IconOutlineForum,
+ IconOutlineMobileFriendly,
+ IconOutlineNotifications,
+ IconOutlineSms,
+ IconType,
+} from '@novu/novui/icons';
+import { VStack } from '@novu/novui/jsx';
+import { vstack } from '@novu/novui/patterns';
+import { FC, PropsWithChildren } from 'react';
+import { LocalizedMessage } from '../../../types/LocalizedMessage';
+
+type IWorkflowFloatingMenuProps = CoreProps;
+
+export const WorkflowFloatingMenu: FC = ({ className }) => {
+ return (
+
+ );
+};
+
+interface IWorkflowFloatingMenuSectionProps extends PropsWithChildren {
+ title: LocalizedMessage;
+}
+
+function WorkflowFloatingMenuSection({ title, children }: IWorkflowFloatingMenuSectionProps) {
+ return (
+
+
+ {title}
+
+ {children}
+
+ );
+}
+
+interface IWorkflowFloatingMenuButtonProps extends IconButtonProps {
+ Icon: IconType;
+ tooltipLabel?: LocalizedMessage;
+}
+
+function WorkflowFloatingMenuButton({ Icon, tooltipLabel }: IWorkflowFloatingMenuButtonProps) {
+ return (
+
+
+
+ );
+}
diff --git a/apps/web/src/studio/components/workflows/WorkflowsDetailPage.tsx b/apps/web/src/studio/components/workflows/WorkflowsDetailPage.tsx
index 16217116f66..c91d774e563 100644
--- a/apps/web/src/studio/components/workflows/WorkflowsDetailPage.tsx
+++ b/apps/web/src/studio/components/workflows/WorkflowsDetailPage.tsx
@@ -1,23 +1,67 @@
-import { SearchInput } from '@novu/design-system';
-import { Button } from '@novu/novui';
+import { Button, IconButton, Title } from '@novu/novui';
import { css } from '@novu/novui/css';
-import { IconAddBox } from '@novu/novui/icons';
-import { Flex } from '@novu/novui/jsx';
+import {
+ IconCable,
+ IconOutlineBolt,
+ IconOutlineEmail,
+ IconOutlineNotifications,
+ IconPlayArrow,
+ IconSettings,
+} from '@novu/novui/icons';
+import { Flex, HStack, VStack } from '@novu/novui/jsx';
import { PageContainer } from '../../layout/PageContainer';
import { PageMeta } from '../../layout/PageMeta';
+import { StepNode } from './StepNode';
+import { WorkflowFloatingMenu } from './WorkflowFloatingMenu';
export const WorkflowsDetailPage = () => {
- const title = 'Example';
+ const title = 'Workflow name';
+
+ const handleSettingsClick = () => {};
+ const handleTestClick = () => {};
return (
-
+
-
-
-
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+ } title={'Workflow trigger'} />
+ } title={'Workflow trigger'} />
+ } title={'Workflow trigger'} />
+
+
);
};
diff --git a/libs/novui/src/components/button/IconButton.stories.tsx b/libs/novui/src/components/button/IconButton.stories.tsx
new file mode 100644
index 00000000000..68036f3bc42
--- /dev/null
+++ b/libs/novui/src/components/button/IconButton.stories.tsx
@@ -0,0 +1,36 @@
+import { Meta } from '@storybook/react';
+import React from 'react';
+import { Grid } from '../../../styled-system/jsx';
+import { IconSettings } from '../../icons';
+import { Title } from '../title';
+import { IconButton } from './IconButton';
+
+export default {
+ title: 'Components/IconButton',
+ component: IconButton,
+ argTypes: {},
+} as Meta;
+
+export const all = () => (
+
+ Default
+
+
+
+
+ Transparent
+
+
+
+
+ Filled
+
+
+
+
+ Outline
+
+
+
+
+);
diff --git a/libs/novui/src/components/button/IconButton.tsx b/libs/novui/src/components/button/IconButton.tsx
new file mode 100644
index 00000000000..d3051e4421b
--- /dev/null
+++ b/libs/novui/src/components/button/IconButton.tsx
@@ -0,0 +1,58 @@
+import { ActionIcon, ButtonVariant as ExternalButtonVariant } from '@mantine/core';
+import React from 'react';
+import { PolymorphicComponentPropWithRef, PolymorphicRef } from '../../types/props-helpers';
+import { JsxStyleProps } from 'styled-system/types';
+import { css, cx } from '../../../styled-system/css';
+import { button, type ButtonVariant } from '../../../styled-system/recipes';
+import { token } from '../../../styled-system/tokens';
+import { IconType } from '../../icons';
+import { CoreProps } from '../../types';
+import { splitCssProps } from '../../../styled-system/jsx';
+
+interface IIconButtonProps {
+ Icon: IconType;
+ loading?: boolean;
+}
+
+type IconButtonDefaultElement = 'button';
+
+export type IconButtonProps = PolymorphicComponentPropWithRef<
+ C,
+ JsxStyleProps & ButtonVariant & CoreProps & IIconButtonProps
+>;
+
+const DEFAULT_VARIANT: ButtonVariant['variant'] = 'transparent';
+
+type PolymorphicComponent = (
+ props: IconButtonProps
+) => JSX.Element | null;
+
+/**
+ * A button with only an Icon.
+ *
+ * TODO: there are not specifications for these in the Design System, so this just follows the Button recipe.
+ */
+export const IconButton: PolymorphicComponent = React.forwardRef(
+ (
+ { variant = DEFAULT_VARIANT, ...props }: IconButtonProps,
+ ref?: PolymorphicRef
+ ) => {
+ const [variantProps, buttonProps] = button.splitVariantProps({ ...props, variant });
+ const [cssProps, localProps] = splitCssProps(buttonProps);
+ const { className, as, loading, Icon, ...otherProps } = localProps;
+ const styles = button(variantProps);
+
+ return (
+
+
+
+ );
+ }
+);
diff --git a/libs/novui/src/components/button/index.ts b/libs/novui/src/components/button/index.ts
index 8b166a86e4d..66f0cf90a7d 100644
--- a/libs/novui/src/components/button/index.ts
+++ b/libs/novui/src/components/button/index.ts
@@ -1 +1,2 @@
export * from './Button';
+export * from './IconButton';
diff --git a/libs/novui/src/tokens/semanticColors.tokens.ts b/libs/novui/src/tokens/semanticColors.tokens.ts
index 69c6c95a7f1..d8602b044d7 100644
--- a/libs/novui/src/tokens/semanticColors.tokens.ts
+++ b/libs/novui/src/tokens/semanticColors.tokens.ts
@@ -66,6 +66,12 @@ export const LEGACY_COLOR_SEMANTIC_TOKENS = defineSemanticTokens.colors({
type: 'color',
},
},
+ icon: {
+ filled: {
+ value: { base: '{colors.legacy.white}', _dark: '{colors.legacy.white}' },
+ type: 'color',
+ },
+ },
secondary: {
background: {
value: { base: '{colors.legacy.white}', _dark: '{colors.legacy.B17}' },