Update documentation rules for various contexts and functionalities (#235)
Update cusor rules for various contexts and functionalities
This commit is contained in:
committed by
GitHub
parent
53b09fcb8e
commit
1030c84eee
@@ -3,16 +3,18 @@ description: Writing Forms with Shadcn UI, Server Actions, Zod
|
||||
globs: apps/**/*.tsx,packages/**/*.tsx
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Forms
|
||||
|
||||
- Use React Hook Form for form validation and submission.
|
||||
- Use Zod for form validation.
|
||||
- Use the `zodResolver` function to resolve the Zod schema to the form.
|
||||
- Use Server Actions [server-actions.mdc](mdc:.cursor/rules/server-actions.mdc) for server-side code handling
|
||||
- Use Sonner for writing toasters for UI feedback
|
||||
|
||||
Follow the example below to create all forms:
|
||||
|
||||
## Define the schema
|
||||
|
||||
Zod schemas should be defined in the `schema` folder and exported, so we can reuse them across a Server Action and the client-side form:
|
||||
|
||||
```tsx
|
||||
@@ -27,19 +29,15 @@ export const CreateNoteSchema = z.object({
|
||||
|
||||
## Create the Server Action
|
||||
|
||||
Server Actions [server-actions.mdc](mdc:.cursor/rules/server-actions.mdc) can help us create endpoints for our forms.
|
||||
|
||||
```tsx
|
||||
// _lib/server/server-actions.ts
|
||||
'use server';
|
||||
|
||||
import { z } from 'zod';
|
||||
import { enhanceAction } from '@kit/next/actions';
|
||||
import { CreateNoteSchema } from '../schema/create-note.schema';
|
||||
|
||||
const CreateNoteSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
});
|
||||
|
||||
export const createNoteAction = enhanceAction(
|
||||
async function (data, user) {
|
||||
// 1. "data" has been validated against the Zod schema, and it's safe to use
|
||||
@@ -62,18 +60,22 @@ export const createNoteAction = enhanceAction(
|
||||
Then create a client component to handle the form submission:
|
||||
|
||||
```tsx
|
||||
// _components/create-note-form.tsx
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { Textarea } from '@kit/ui/textarea';
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@kit/ui/form';
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { CreateNoteSchema } from '../_lib/schema/create-note.schema';
|
||||
|
||||
export function CreateNoteForm() {
|
||||
const [pending, startTransition] = useTransition();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(CreateNoteSchema),
|
||||
@@ -85,58 +87,58 @@ export function CreateNoteForm() {
|
||||
|
||||
const onSubmit = (data) => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
await createNoteAction(data);
|
||||
} catch {
|
||||
// handle error
|
||||
}
|
||||
await toast.promise(createNoteAction(data), {
|
||||
loading: t('notes:creatingNote`),
|
||||
success: t('notes:createNoteSuccess`),
|
||||
error: t('notes:createNoteError`)
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<Form {...form}>
|
||||
<FormField name={'title'} render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<span className={'text-sm font-medium'}>Title</span>
|
||||
</FormLabel>
|
||||
<Form {...form}>
|
||||
<FormField name={'title'} render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<span className={'text-sm font-medium'}>Title</span>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<input
|
||||
type={'text'}
|
||||
className={'w-full'}
|
||||
placeholder={'Title'}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<Input
|
||||
type={'text'}
|
||||
className={'w-full'}
|
||||
placeholder={'Title'}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)} />
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)} />
|
||||
|
||||
<FormField name={'content'} render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<span className={'text-sm font-medium'}>Content</span>
|
||||
</FormLabel>
|
||||
<FormField name={'content'} render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<span className={'text-sm font-medium'}>Content</span>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<textarea
|
||||
className={'w-full'}
|
||||
placeholder={'Content'}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
className={'w-full'}
|
||||
placeholder={'Content'}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)} />
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)} />
|
||||
|
||||
<button disabled={pending} type={'submit'} className={'w-full'}>
|
||||
Submit
|
||||
</button>
|
||||
</Form>
|
||||
<button disabled={pending} type={'submit'} className={'w-full'}>
|
||||
Submit
|
||||
</button>
|
||||
</Form>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user