Add form implementation standards for Shadcn/UI components with vee-validate

- Introduce critical guidelines for using Shadcn/UI form components in conjunction with vee-validate for form handling.
- Provide detailed examples for required form setup, template structure, and key requirements to ensure proper validation and accessibility.
- Emphasize the importance of type safety using Zod schema for validation and correct form handling practices.

These updates aim to standardize form implementations across the application, enhancing consistency and maintainability.
This commit is contained in:
padreug 2025-09-08 16:58:05 +02:00
parent b0a2d1a6df
commit 3679c719a3

148
CLAUDE.md
View file

@ -186,6 +186,153 @@ export const myModule: ModulePlugin = {
- Module configs in `src/app.config.ts` - Module configs in `src/app.config.ts`
- Centralized config parsing and validation - Centralized config parsing and validation
### **Form Implementation Standards**
**CRITICAL: Always use Shadcn/UI Form Components with vee-validate**
All forms in the application MUST follow the official Shadcn Vue form implementation pattern:
**Required Form Setup:**
```typescript
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod'
import {
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form'
// 1. Define Zod schema for validation
const formSchema = toTypedSchema(z.object({
name: z.string().min(1, "Name is required").max(100, "Name too long"),
email: z.string().email("Invalid email address").optional(),
items: z.array(z.string()).min(1, "Select at least one item"),
}))
// 2. Set up form with vee-validate
const form = useForm({
validationSchema: formSchema,
initialValues: {
name: '',
email: '',
items: []
}
})
// 3. Destructure form methods
const { setFieldValue, resetForm, values, meta } = form
// 4. Create form validation computed
const isFormValid = computed(() => meta.value.valid)
// 5. Create submit handler with form.handleSubmit
const onSubmit = form.handleSubmit(async (values) => {
console.log('Form submitted:', values)
// Handle form submission logic
})
```
**Required Form Template Structure:**
```vue
<template>
<!-- form.handleSubmit automatically prevents default submission -->
<form @submit="onSubmit" class="space-y-6">
<!-- Text Input Field -->
<FormField v-slot="{ componentField }" name="name">
<FormItem>
<FormLabel>Name *</FormLabel>
<FormControl>
<Input
placeholder="Enter name"
v-bind="componentField"
/>
</FormControl>
<FormDescription>Enter your full name</FormDescription>
<FormMessage />
</FormItem>
</FormField>
<!-- Multiple Checkbox Selection -->
<FormField name="items">
<FormItem>
<div class="mb-4">
<FormLabel class="text-base">Items *</FormLabel>
<FormDescription>Select one or more items</FormDescription>
</div>
<div v-for="item in availableItems" :key="item.id">
<FormField
v-slot="{ value, handleChange }"
type="checkbox"
:value="item.id"
:unchecked-value="false"
name="items"
>
<FormItem class="flex flex-row items-start space-x-3 space-y-0">
<FormControl>
<Checkbox
:model-value="value.includes(item.id)"
@update:model-value="handleChange"
/>
</FormControl>
<FormLabel class="font-normal">{{ item.name }}</FormLabel>
</FormItem>
</FormField>
</div>
<FormMessage />
</FormItem>
</FormField>
<!-- Submit Button -->
<Button
type="submit"
:disabled="isLoading || !isFormValid"
>
{{ isLoading ? 'Submitting...' : 'Submit' }}
</Button>
</form>
</template>
```
**Key Form Requirements:**
- ✅ **Form validation**: Use `@submit="onSubmit"` - form.handleSubmit automatically prevents page refresh
- ✅ **Button state**: Disable submit button with `!isFormValid` until all required fields are valid
- ✅ **Error display**: Use `<FormMessage />` for automatic error display
- ✅ **Field binding**: Use `v-bind="componentField"` for proper form field integration
- ✅ **Checkbox arrays**: Use nested FormField pattern for multiple checkbox selection
- ✅ **Type safety**: Zod schema provides full TypeScript type safety
**❌ NEVER do this:**
```vue
<!-- Wrong: Manual form handling without vee-validate -->
<form @submit.prevent="handleSubmit">
<!-- Wrong: Direct v-model bypasses form validation -->
<Input v-model="myValue" />
<!-- Wrong: Manual validation instead of using meta.valid -->
<Button :disabled="!name || !email">Submit</Button>
```
**✅ ALWAYS do this:**
```vue
<!-- Correct: Uses form.handleSubmit for proper form handling -->
<form @submit="onSubmit">
<!-- Correct: Uses FormField with componentField binding -->
<FormField v-slot="{ componentField }" name="fieldName">
<FormControl>
<Input v-bind="componentField" />
</FormControl>
</FormField>
<!-- Correct: Uses form meta for validation state -->
<Button :disabled="!isFormValid">Submit</Button>
```
### **Code Conventions:** ### **Code Conventions:**
- Use TypeScript interfaces over types for extendability - Use TypeScript interfaces over types for extendability
- Prefer functional and declarative patterns over classes - Prefer functional and declarative patterns over classes
@ -195,6 +342,7 @@ export const myModule: ModulePlugin = {
- Implement lazy loading for non-critical components - Implement lazy loading for non-critical components
- Optimize images using WebP format with lazy loading - Optimize images using WebP format with lazy loading
- **ALWAYS use dependency injection for cross-module service access** - **ALWAYS use dependency injection for cross-module service access**
- **ALWAYS use Shadcn Form components for all form implementations**
**Build Configuration:** **Build Configuration:**
- Vite config includes PWA, image optimization, and bundle analysis - Vite config includes PWA, image optimization, and bundle analysis