-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(dashboard): in-app editor form driven by BE schema #6877
Changes from 2 commits
f684317
71f33b1
e38f38a
fb62976
ab0c195
8cb5115
10e5199
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,7 @@ | |
"devDependencies": { | ||
"@clerk/types": "^4.6.1", | ||
"@eslint/js": "^9.9.0", | ||
"@hookform/devtools": "^4.3.0", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A really useful tool to debug the |
||
"@playwright/test": "^1.44.0", | ||
"@sentry/vite-plugin": "^2.22.6", | ||
"@types/lodash.debounce": "^4.0.9", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { getV2 } from './api.client'; | ||
import type { StepDataDto } from '@novu/shared'; | ||
|
||
export const fetchStep = async ({ | ||
workflowSlug, | ||
stepSlug, | ||
}: { | ||
workflowSlug: string; | ||
stepSlug: string; | ||
}): Promise<StepDataDto> => { | ||
const { data } = await getV2<{ data: StepDataDto }>(`/workflows/${workflowSlug}/steps/${stepSlug}`); | ||
|
||
return data; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,14 @@ | ||
'use client'; | ||
|
||
import { Avatar, AvatarImage } from '@/components/primitives/avatar'; | ||
import { Button } from '@/components/primitives/button'; | ||
import { FormControl, FormMessage } from '@/components/primitives/form/form'; | ||
import { FormMessage } from '@/components/primitives/form/form'; | ||
import { Input, InputField } from '@/components/primitives/input'; | ||
import { Label } from '@/components/primitives/label'; | ||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/primitives/popover'; | ||
import { Separator } from '@/components/primitives/separator'; | ||
import TextSeparator from '@/components/primitives/text-separator'; | ||
import { useState, forwardRef } from 'react'; | ||
import { RiEdit2Line, RiImageEditFill } from 'react-icons/ri'; | ||
import { RiEdit2Line, RiErrorWarningFill, RiImageEditFill } from 'react-icons/ri'; | ||
import { useFormField } from './form-context'; | ||
|
||
const predefinedAvatars = [ | ||
`${window.location.origin}/images/avatar.svg`, | ||
|
@@ -30,6 +29,7 @@ type AvatarPickerProps = React.InputHTMLAttributes<HTMLInputElement>; | |
|
||
export const AvatarPicker = forwardRef<HTMLInputElement, AvatarPickerProps>(({ id, ...props }, ref) => { | ||
const [isOpen, setIsOpen] = useState(false); | ||
const { error } = useFormField(); | ||
|
||
const handlePredefinedAvatarClick = (url: string) => { | ||
props.onChange?.({ target: { value: url } } as React.ChangeEvent<HTMLInputElement>); | ||
|
@@ -40,14 +40,17 @@ export const AvatarPicker = forwardRef<HTMLInputElement, AvatarPickerProps>(({ i | |
<div className="space-y-2"> | ||
<Popover modal={true} open={isOpen} onOpenChange={setIsOpen}> | ||
<PopoverTrigger asChild> | ||
<Button variant="outline" size="icon" className="text-foreground-600 size-10"> | ||
<Button variant="outline" size="icon" className="text-foreground-600 relative size-10"> | ||
{props.value ? ( | ||
<Avatar> | ||
<Avatar className="p-[1px]"> | ||
<AvatarImage src={props.value as string} /> | ||
LetItRock marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</Avatar> | ||
) : ( | ||
<RiImageEditFill className="size-5" /> | ||
)} | ||
{error && ( | ||
<RiErrorWarningFill className="text-destructive outline-destructive absolute right-0 top-0 size-3 -translate-y-1/2 translate-x-1/2 rounded-full outline outline-1 outline-offset-1" /> | ||
)} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I get it, we want to show the error icon in the top right corner of the avatar. Can you please clarify? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah i thought you were centering. I think that you can do |
||
</Button> | ||
Comment on lines
+51
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it was missing the error icon |
||
</PopoverTrigger> | ||
<PopoverContent className="w-[300px] p-4"> | ||
|
@@ -59,11 +62,9 @@ export const AvatarPicker = forwardRef<HTMLInputElement, AvatarPickerProps>(({ i | |
<Separator /> | ||
<div className="space-y-1"> | ||
<Label>Avatar URL</Label> | ||
<FormControl> | ||
<InputField> | ||
<Input type="url" id={id} placeholder="Enter avatar URL" ref={ref} {...props} /> | ||
</InputField> | ||
</FormControl> | ||
<InputField> | ||
<Input type="url" id={id} placeholder="Enter avatar URL" ref={ref} {...props} /> | ||
</InputField> | ||
<FormMessage /> | ||
</div> | ||
</div> | ||
|
@@ -80,7 +81,6 @@ export const AvatarPicker = forwardRef<HTMLInputElement, AvatarPickerProps>(({ i | |
</div> | ||
</PopoverContent> | ||
</Popover> | ||
<FormMessage /> | ||
</div> | ||
); | ||
}); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,28 +101,35 @@ const formMessageVariants = cva('flex items-center gap-1', { | |
}, | ||
}); | ||
|
||
const FormMessage = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>( | ||
({ className, children, ...props }, ref) => { | ||
const { error, formMessageId } = useFormField(); | ||
const body = error ? String(error?.message) : children; | ||
const FormMessagePure = React.forwardRef< | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
HTMLParagraphElement, | ||
React.HTMLAttributes<HTMLParagraphElement> & { error?: string } | ||
>(({ className, children, error, id, ...props }, ref) => { | ||
const body = error ? error : children; | ||
|
||
if (!body) { | ||
return null; | ||
} | ||
|
||
if (!body) { | ||
return null; | ||
} | ||
return ( | ||
<p | ||
ref={ref} | ||
id={id} | ||
className={formMessageVariants({ variant: error ? 'error' : 'default', className })} | ||
{...props} | ||
> | ||
{error ? <RiErrorWarningFill className="size-4" /> : <RiInformationFill className="size-4" />} | ||
<span className="mt-[1px] text-xs leading-3">{body}</span> | ||
</p> | ||
); | ||
}); | ||
FormMessagePure.displayName = 'FormMessagePure'; | ||
|
||
return ( | ||
<p | ||
ref={ref} | ||
id={formMessageId} | ||
className={formMessageVariants({ variant: error ? 'error' : 'default', className })} | ||
{...props} | ||
> | ||
{error ? <RiErrorWarningFill className="size-4" /> : <RiInformationFill className="size-4" />} | ||
<span className="mt-[1px] text-xs leading-3">{body}</span> | ||
</p> | ||
); | ||
} | ||
); | ||
const FormMessage = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>((props, ref) => { | ||
const { error, formMessageId } = useFormField(); | ||
|
||
return <FormMessagePure ref={ref} id={formMessageId} error={error ? String(error?.message) : undefined} {...props} />; | ||
}); | ||
FormMessage.displayName = 'FormMessage'; | ||
|
||
export { Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField }; | ||
export { Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormMessagePure, FormField }; |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the default values to the
dataSchema
, these are used to generate the default values for the form. The default values are required for all visible text fields by thereact-hook-form
, ref.