generate-form
Generate form component with validation, state management, and submit handling. Use when creating new data entry forms.
Installation
Suivez les instructions du dépôt pour votre agent. Consultez les fichiers et dépendances avant l’installation.
Voir la source ↗GuidesInstructions
Generate Form
Generate a form component with validation and store integration.
Usage
When user requests to create a form component, ask for:
- Form name (e.g., "WaterIntakeForm", "SleepLogForm")
- Fields needed (name, type, validation rules)
- Store action to call on submit (e.g., "addWaterLog")
- Whether it includes a list (add/remove items like MealLogForm)
Implementation Pattern
Based on src/components/forms/MealLogForm.tsx pattern.
File Structure
Create file: src/components/forms/{FormName}.tsx
'use client';
import { useState } from 'react';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Trash2, Plus, Loader2 } from 'lucide-react';
import { useHealthStore } from '@/lib/store/healthStore';
import { toast } from 'sonner';
export function FormName() {
const [field1, setField1] = useState('');
const [field2, setField2] = useState('');
const { addItem, isLoading } = useHealthStore();
const [errors, setErrors] = useState<Record<string, string>>({});
const validate = () => {
const newErrors: Record<string, string> = {};
if (!field1.trim()) {
newErrors.field1 = 'Field 1 is required';
}
if (!field2.trim()) {
newErrors.field2 = 'Field 2 is required';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async () => {
if (!validate()) return;
const today = new Date().toISOString().split('T')[0];
try {
await addItem({
date: today,
field1,
field2,
});
setField1('');
setField2('');
setErrors({});
} catch (error: any) {
toast.error(error.message || 'Failed to submit form');
}
};
return (
<Card>
<CardHeader>
<CardTitle>Form Title</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="field1">Field 1 Label</Label>
<Input
id="field1"
type="text"
placeholder="Enter value"
value={field1}
onChange={(e) => setField1(e.target.value)}
disabled={isLoading}
/>
{errors.field1 && <p className="text-xs text-red-500">{errors.field1}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="field2">Field 2 Label</Label>
<Input
id="field2"
type="number"
placeholder="0"
value={field2}
onChange={(e) => setField2(e.target.value)}
disabled={isLoading}
/>
{errors.field2 && <p className="text-xs text-red-500">{errors.field2}</p>}
</div>
</CardContent>
<CardFooter>
<Button onClick={handleSubmit} disabled={isLoading} className="w-full">
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Submit
</Button>
</CardFooter>
</Card>
);
}
With List Management (Optional)
For forms that add/remove items from a list:
'use client';
import { useState } from 'react';
import { Trash2, Plus } from 'lucide-react';
export function FormWithList() {
const [items, setItems] = useState<any[]>([]);
const [currentItem, setCurrentItem] = useState('');
const addItem = () => {
if (!currentItem.trim()) {
toast.error('Item cannot be empty');
return;
}
setItems([...items, { id: Date.now(), name: currentItem }]);
setCurrentItem('');
toast.success(`Added ${currentItem}`);
};
const removeItem = (index: number) => {
const itemName = items[index].name;
setItems(items.filter((_, i) => i !== index));
toast.info(`Removed ${itemName}`);
};
return (
<>
<div className="flex gap-2">
<Input
value={currentItem}
onChange={(e) => setCurrentItem(e.target.value)}
placeholder="Enter item"
/>
<Button onClick={addItem} variant="outline" size="sm">
<Plus className="h-4 w-4" />
</Button>
</div>
<div className="space-y-2 mt-4">
{items.map((item, index) => (
<div key={item.id} className="flex items-center justify-between bg-muted p-2 rounded">
<span>{item.name}</span>
<Button
onClick={() => removeItem(index)}
variant="ghost"
size="sm"
>
<Trash2 className="h-4 w-4 text-destructive" />
</Button>
</div>
))}
</div>
</>
);
}
Key Conventions
- Use
'use client'directive at top - useState for all form fields
- useHealthStore hook for store actions
- Validation function before submit
- Try-catch-toast error handling
- Set isLoading state on button during submission
- Clear form fields after successful submit
- Use shadcn/ui components (Input, Button, Label, etc.)
- Show validation errors below each field
- Disable inputs while loading
- Use Loader2 icon for loading state
- Toast notifications for all user actions
Steps
- Ask user for form name, fields, store action, and list management
- Create file:
src/components/forms/{FormName}.tsx - Generate component with Card wrapper
- Add useState for each field
- Add useHealthStore hook integration
- Add validation function
- Add handleSubmit with try-catch-toast
- Add form fields with shadcn/ui components
- Add list management if requested
- Format with Prettier
Implementation Checklist
- Component exports correctly
- 'use client' directive present
- useState for all fields
- useHealthStore hook imported
- Validation function implemented
- Error state management
- handleSubmit with try-catch
- Toast notifications added
- isLoading state used
- shadcn/ui components used
- Error messages displayed
- List management (if applicable)
Exemples
Consultez les exemples du dépôt source.
Voir la source ↗Historique source
- Date d’indexation
- 2026-01-20
- Mise à jour de l’index
- 2026-01-26
- Branch
- main
Les dates concernent ce répertoire. Consultez le dépôt pour l’historique du code.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!