Skip to content

Commit

Permalink
chore(dashboard): in-app editor base form (#6801)
Browse files Browse the repository at this point in the history
  • Loading branch information
LetItRock authored Oct 30, 2024
1 parent bb70f2c commit 031881a
Show file tree
Hide file tree
Showing 15 changed files with 316 additions and 129 deletions.
3 changes: 2 additions & 1 deletion apps/dashboard/src/components/primitives/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { cva, VariantProps } from 'class-variance-authority';
import { inputVariants } from '@/components/primitives/variants';

const inputFieldVariants = cva(
'text-foreground-950 flex w-full flex-nowrap items-center gap-1.5 rounded-md border bg-transparent shadow-sm transition-colors focus-within:outline-none focus-visible:outline-none hover:bg-neutral-50 has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50 has-[input[value=""]]:text-foreground-400 has-[input:disabled]:bg-neutral-alpha-100 has-[input:disabled]:text-foreground-300',
'text-foreground-950 flex w-full flex-nowrap items-center gap-1.5 rounded-md border bg-transparent transition-colors focus-within:outline-none focus-visible:outline-none hover:bg-neutral-50 has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50 has-[input[value=""]]:text-foreground-400 has-[input:disabled]:bg-neutral-alpha-100 has-[input:disabled]:text-foreground-300',
{
variants: {
size: {
default: 'h-8 px-2 [&>input]:py-1.5',
md: 'h-10 px-3 [&>input]:py-2.5',
},
state: {
default:
Expand Down
97 changes: 65 additions & 32 deletions apps/dashboard/src/components/primitives/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,76 @@ import * as React from 'react';
import * as TabsPrimitive from '@radix-ui/react-tabs';

import { cn } from '@/utils/ui';
import { cva, VariantProps } from 'class-variance-authority';

const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
'border-neutral-alpha-200 inline-flex w-full items-center justify-start gap-6 border-b border-t px-3.5',
className
)}
{...props}
/>
));
const tabsListVariants = cva('inline-flex items-center', {
variants: {
variant: {
default: 'h-9 justify-center rounded-[10px] bg-neutral-alpha-100 p-1 text-muted-foreground',
regular: 'border-neutral-alpha-200 w-full justify-start gap-6 border-b border-t px-3.5',
},
},
defaultVariants: {
variant: 'default',
},
});

type TabsListProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & VariantProps<typeof tabsListVariants>;

const TabsList = React.forwardRef<React.ElementRef<typeof TabsPrimitive.List>, TabsListProps>(
({ className, variant, ...props }, ref) => (
<TabsPrimitive.List ref={ref} className={tabsListVariants({ variant, className })} {...props} />
)
);
TabsList.displayName = TabsPrimitive.List.displayName;

const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"text-foreground-600 ring-offset-background focus-visible:ring-ring data-[state=active]:text-foreground data-[state=active]:after:border-primary data-[state=active]:text-foreground-950 relative inline-flex items-center justify-center whitespace-nowrap rounded-md py-3.5 text-sm font-medium transition-all duration-300 ease-out after:absolute after:bottom-0 after:left-0 after:h-[2px] after:w-full after:border-b-2 after:border-b-transparent after:content-[''] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
className
)}
{...props}
/>
));
const tabsTriggerVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md font-medium ring-offset-background transition-all text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none data-[state=active]:text-foreground disabled:opacity-50',
{
variants: {
variant: {
default:
'px-3 py-1 data-[state=active]:bg-background data-[state=active]:shadow data-[state=inactive]:text-foreground-400',
regular:
"text-foreground-600 data-[state=active]:after:border-primary data-[state=active]:text-foreground-950 relative py-3.5 duration-300 ease-out after:absolute after:bottom-0 after:left-0 after:h-[2px] after:w-full after:border-b-2 after:border-b-transparent after:content-['']",
},
},
defaultVariants: {
variant: 'default',
},
}
);

type TabsTriggerProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> &
VariantProps<typeof tabsTriggerVariants>;

const TabsTrigger = React.forwardRef<React.ElementRef<typeof TabsPrimitive.Trigger>, TabsTriggerProps>(
({ className, variant, ...props }, ref) => (
<TabsPrimitive.Trigger ref={ref} className={tabsTriggerVariants({ variant, className })} {...props} />
)
);
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;

const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content ref={ref} className={cn('mt-2 focus-visible:outline-none', className)} {...props} />
));
const tabsContentVariants = cva('focus-visible:outline-none', {
variants: {
variant: {
default: 'ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
regular: 'mt-2',
},
},
defaultVariants: {
variant: 'default',
},
});

type TabsContentProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> &
VariantProps<typeof tabsContentVariants>;

const TabsContent = React.forwardRef<React.ElementRef<typeof TabsPrimitive.Content>, TabsContentProps>(
({ className, variant, ...props }, ref) => (
<TabsPrimitive.Content ref={ref} className={tabsContentVariants({ variant, className })} {...props} />
)
);
TabsContent.displayName = TabsPrimitive.Content.displayName;

const Tabs = React.forwardRef<
Expand Down
1 change: 1 addition & 0 deletions apps/dashboard/src/components/primitives/textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const textareaVariants = cva(
variants: {
size: {
default: 'h-8 px-2 py-1.5',
md: 'h-10 px-3 py-2.5',
},
state: {
default:
Expand Down
12 changes: 6 additions & 6 deletions apps/dashboard/src/components/primitives/url-input.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { Input, InputField, InputProps } from '@/components/primitives/input';
import { Input, InputField, InputFieldProps, InputProps } from '@/components/primitives/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select';
import { RedirectTargetEnum } from '@novu/shared';
import { forwardRef } from 'react';
Expand All @@ -10,19 +10,19 @@ type URLValue = {
url: string;
};

type URLInputProps = Omit<InputProps, 'value' | 'onChange'> & {
type URLInputProps = Omit<InputProps, 'value' | 'onChange' | 'size'> & {
options: string[];
value: URLValue;
onChange: (value: URLValue) => void;
};
} & Pick<InputFieldProps, 'size'>;

export const URLInput = forwardRef<HTMLInputElement, URLInputProps>((props, ref) => {
const { options, value, onChange, ...rest } = props;
const { options, value, onChange, size, ...rest } = props;

return (
<div className="flex items-center justify-between space-x-2">
<div className="relative flex-grow">
<InputField className="pr-0">
<InputField className="pr-0" size={size}>
<Input
ref={ref}
type="text"
Expand All @@ -32,7 +32,7 @@ export const URLInput = forwardRef<HTMLInputElement, URLInputProps>((props, ref)
{...rest}
/>
<Select value={value.type} onValueChange={(val: RedirectTargetEnum) => onChange({ ...value, type: val })}>
<SelectTrigger className="h-full rounded-l-none border-0 border-l">
<SelectTrigger className="h-full max-w-24 rounded-l-none border-0 border-l">
<SelectValue />
</SelectTrigger>
<SelectContent>
Expand Down
6 changes: 3 additions & 3 deletions apps/dashboard/src/components/primitives/variants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ export const buttonVariants = cva(
variants: {
variant: {
default:
'bg-gradient-to-b from-neutral-alpha-900 to-neutral-900 text-neutral-foreground shadow-[inset_0_-4px_2px_-2px_hsl(var(--neutral-900)),inset_0_0_0_1px_rgba(255,255,255,0.16),0_0_0_1px_hsl(var(--neutral-900)),0px_1px_2px_0px_#0E121B3D] after:content-[""] after:absolute after:w-full after:h-full after:bg-gradient-to-b after:from-background/10 after:opacity-0 hover:after:opacity-100 after:rounded-lg after:transition-opacity after:duration-300',
'bg-gradient-to-b from-neutral-alpha-900 to-neutral-900 text-neutral-foreground [clip-path:border-box] shadow-[inset_0_-4px_2px_-2px_hsl(var(--neutral-900)),inset_0_0_0_1px_rgba(255,255,255,0.16),0_0_0_1px_hsl(var(--neutral-900)),0px_1px_2px_0px_#0E121B3D] after:content-[""] after:absolute after:w-full after:h-full after:bg-gradient-to-b after:from-background/10 after:opacity-0 hover:after:opacity-100 after:rounded-lg after:transition-opacity after:duration-300',
primary:
'bg-gradient-to-b from-primary/90 to-primary text-primary-foreground shadow-[inset_0_-4px_2px_-2px_hsl(var(--primary)),inset_0_0_0_1px_rgba(255,255,255,0.16),0_0_0_1px_hsl(var(--primary)),0px_1px_2px_0px_#0E121B3D] after:content-[""] after:absolute after:w-full after:h-full after:bg-gradient-to-b after:from-background/10 after:opacity-0 hover:after:opacity-100 after:rounded-lg after:transition-opacity after:duration-300',
'bg-gradient-to-b from-primary/90 to-primary text-primary-foreground [clip-path:border-box] shadow-[inset_0_-4px_2px_-2px_hsl(var(--primary)),inset_0_0_0_1px_rgba(255,255,255,0.16),0_0_0_1px_hsl(var(--primary)),0px_1px_2px_0px_#0E121B3D] after:content-[""] after:absolute after:w-full after:h-full after:bg-gradient-to-b after:from-background/10 after:opacity-0 hover:after:opacity-100 after:rounded-lg after:transition-opacity after:duration-300',
destructive:
'bg-gradient-to-b from-destructive/90 to-destructive text-destructive-foreground shadow-[inset_0_-4px_2px_-2px_hsl(var(--destructive)),inset_0_0_0_1px_rgba(255,255,255,0.16),0_0_0_1px_hsl(var(--destructive)),0px_1px_2px_0px_#0E121B3D] after:content-[""] after:absolute after:w-full after:h-full after:bg-gradient-to-b after:from-background/10 after:opacity-0 hover:after:opacity-100 after:rounded-lg after:transition-opacity after:duration-300',
'bg-gradient-to-b from-destructive/90 to-destructive text-destructive-foreground [clip-path:border-box] shadow-[inset_0_-4px_2px_-2px_hsl(var(--destructive)),inset_0_0_0_1px_rgba(255,255,255,0.16),0_0_0_1px_hsl(var(--destructive)),0px_1px_2px_0px_#0E121B3D] after:content-[""] after:absolute after:w-full after:h-full after:bg-gradient-to-b after:from-background/10 after:opacity-0 hover:after:opacity-100 after:rounded-lg after:transition-opacity after:duration-300',
outline: 'border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground',
dashed: 'border border-dashed border-input bg-background hover:bg-accent text-foreground-600',
ghost: 'hover:bg-accent',
Expand Down
30 changes: 12 additions & 18 deletions apps/dashboard/src/components/workflow-editor/action-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Separator } from '@/components/primitives/separator';
import { URLInput } from '@/components/primitives/url-input';
import { buttonVariants } from '@/components/primitives/variants';
import { cn } from '@/utils/ui';
import { urlTargetTypes } from '@/utils/url';
import { zodResolver } from '@hookform/resolvers/zod';
import { RedirectTargetEnum } from '@novu/shared';
import { ComponentProps } from 'react';
Expand All @@ -33,18 +34,19 @@ type Actions = {
};

type ActionPickerProps = {
className?: string;
value: Actions | undefined;
onChange: (value: Actions) => void;
};

export const ActionPicker = (props: ActionPickerProps) => {
const { value, onChange } = props;
const { className, value, onChange } = props;
const primaryAction = value?.primaryAction;
const secondaryAction = value?.secondaryAction;

return (
<div className="flex items-center gap-1">
<div className="border-neutral-alpha-200 flex min-h-10 w-full flex-wrap justify-end gap-1 rounded-md border p-1 shadow-sm">
<div className={cn('flex items-center gap-1', className)}>
<div className="border-neutral-alpha-200 flex min-h-10 w-full flex-wrap items-center justify-end gap-1 rounded-md border p-1 shadow-sm">
{!primaryAction && !secondaryAction && (
<div className={buttonVariants({ variant: 'dashed', size: 'sm' })}>
<RiForbid2Line className="size-4" />
Expand All @@ -59,7 +61,7 @@ export const ActionPicker = (props: ActionPickerProps) => {
onChange({ primaryAction, secondaryAction });
}}
>
<Button variant="primary" size="sm">
<Button variant="primary" size="xs">
{primaryAction.label}
</Button>
</ConfigureActionPopover>
Expand All @@ -72,7 +74,7 @@ export const ActionPicker = (props: ActionPickerProps) => {
onChange({ primaryAction, secondaryAction });
}}
>
<Button variant="outline" size="sm">
<Button variant="outline" size="xs">
{secondaryAction.label}
</Button>
</ConfigureActionPopover>
Expand All @@ -90,7 +92,7 @@ export const ActionPicker = (props: ActionPickerProps) => {
onChange({});
}}
>
<div className={cn(buttonVariants({ variant: 'dashed', size: 'sm' }), 'pointer-events-none gap-2')}>
<div className={cn(buttonVariants({ variant: 'dashed', size: 'xs' }), 'pointer-events-none gap-2')}>
<RiForbid2Line className="size-4" />
No action
</div>
Expand All @@ -106,7 +108,7 @@ export const ActionPicker = (props: ActionPickerProps) => {
});
}}
>
<div className={cn(buttonVariants({ variant: 'primary', size: 'sm' }), 'pointer-events-none')}>
<div className={cn(buttonVariants({ variant: 'primary', size: 'xs' }), 'pointer-events-none')}>
Primary action
</div>
</DropdownMenuItem>
Expand All @@ -124,10 +126,10 @@ export const ActionPicker = (props: ActionPickerProps) => {
});
}}
>
<div className={cn(buttonVariants({ variant: 'primary', size: 'sm' }), 'pointer-events-none')}>
<div className={cn(buttonVariants({ variant: 'primary', size: 'xs' }), 'pointer-events-none')}>
Primary action
</div>
<div className={cn(buttonVariants({ variant: 'outline', size: 'sm' }), 'pointer-events-none')}>
<div className={cn(buttonVariants({ variant: 'outline', size: 'xs' }), 'pointer-events-none')}>
Secondary action
</div>
</DropdownMenuItem>
Expand All @@ -151,14 +153,6 @@ const formSchema = z.object({
}),
});

const urlTypes = [
RedirectTargetEnum.SELF,
RedirectTargetEnum.BLANK,
RedirectTargetEnum.PARENT,
RedirectTargetEnum.TOP,
RedirectTargetEnum.UNFENCED_TOP,
];

const ConfigureActionPopover = (
props: ComponentProps<typeof PopoverTrigger> & { action: Action; setAction: (action: Action) => void }
) => {
Expand Down Expand Up @@ -221,7 +215,7 @@ const ConfigureActionPopover = (
<FormControl>
<URLInput
{...field}
options={urlTypes}
options={urlTargetTypes}
value={field.value}
onChange={(val) => field.onChange(val)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function ConfigureStep() {
}

const Step = () => {
const { channel } = useStep();
const { stepType: channel } = useStep();
switch (channel) {
case StepTypeEnum.IN_APP:
return <InApp />;
Expand Down
Loading

0 comments on commit 031881a

Please sign in to comment.