--- name: "form-building" description: "Building create/edit forms with FormField components, validation integration, and save patterns" --- # Form Building Skill ## Core Principles - **MobX state management** - NO state hooks or other libraries unless specified - **Group related fields** using `DetailFieldContainer` - **Ask first** if unclear about field importance - Forms are for CREATE and EDIT operations ## Available Components Located in: `ui/src/common/components/form/fields/` ### Basic Fields - `FormFieldText` - Short text fields - `FormFieldTextArea` - Long text fields - `FormFieldCheckbox` - Boolean 0/1 fields - `FormFieldReadOnly` - Display-only fields - `FormFieldDate` - Date picker - `FormFieldColor` - Color picker - `FormFieldCodeEdit` - Code editor ### Single Select (Constants) ```typescript import { constants } from "@/models/constants"; ``` ### Multi Select (Constants) ```typescript ``` ### Model Relationships **Small lists** (< 50 options): ```typescript record={props.asset} field="organization_id" label="Organization" placeholder="Select Organization" modelName="organization" modelSearchField="q" modelDisplayField="label" modelSearchFilters={{ disabled: "0" }} /> ``` **Large lists** (use search): ```typescript record={props.asset} field="organization_id" label="Organization" placeholder="Search Organization" modelName="organization" modelSearchField="q" modelDisplayField="label" modelSearchFilters={{ disabled: "0" }} /> ``` **Multi-select (small lists)**: ```typescript record={props.asset} field="tag_ids" label="Tags" placeholder="Select Tags" modelName="tag" modelSearchField="q" modelDisplayField="label" /> ``` **Multi-select (large lists with search)**: ```typescript record={props.asset} field="tag_ids" label="Tags" placeholder="Search Tags" modelName="tag" modelSearchField="q" modelDisplayField="label" /> ``` ## Model Relationship Best Practices - **Always try to use `label` as modelDisplayField** - **Always try to use `q` as modelSearchField** - If model doesn't have a good label, modify it in `XXModel.ts` - Assume all models exist - don't create new ones ## JSONB Fields Pattern For nested JSONB objects: ```typescript // JSONB class must extend ValidationClass ``` **Note**: Unlike DetailFields, FormFields don't need `parentRecord` or `SafeBaseModel` wrapper ## Grouping Fields with Container ```typescript record={props.asset} field="organization_id" label="Organization" modelName="organization" modelSearchField="q" modelDisplayField="label" /> ``` ## Complete Form Example ```typescript import { observer } from "mobx-react-lite"; import { AssetModel } from "@/models/asset"; import { OrganizationModel } from "@/models/organization"; import { constants } from "@/models/constants"; import { DetailFieldContainer, FormFieldText, FormFieldTextArea, FormFieldSelect, FormFieldModelSearchSelect, FormFieldCheckbox, FormFieldDate, } from "@/ui/common/components/form/fields"; import { Button } from "@/ui/shadcn/ui/button"; interface AssetFormProps { asset: AssetModel; onSave: () => void; onCancel: () => void; } export const AssetForm = observer((props: AssetFormProps) => { const handleSave = async () => { await props.asset.save(); props.onSave(); }; return (
record={props.asset} field="organization_id" label="Organization" modelName="organization" modelSearchField="q" modelDisplayField="label" modelSearchFilters={{ disabled: "0" }} />
); }); ``` ## Validation Integration Forms automatically connect to model validation rules: ```typescript // Model already has validation rules defined get validationRules() { return assetValidationRules; } // Form fields automatically show errors ``` ## Save Pattern ```typescript const handleSave = async () => { try { await props.asset.save(); // Success - redirect or close modal props.onSave(); } catch (error) { // Error handling - validation errors show on fields automatically console.error("Save failed:", error); } }; ``` ## Key Differences from Details View | Feature | Form Fields | Detail Fields | | ------------ | ------------- | -------------------- | | Use case | Create/Edit | View/Edit | | JSONB | Direct record | Needs `parentRecord` | | Type casting | Not needed | `SafeBaseModel` | | Save button | Required | Optional | | Validation | Built-in | Built-in | ## Key Rules 1. Always create components with `#ui_code_tools` 2. Use search variants for large option lists (>50 items) 3. Constants are model-specific: `constants.{model}.{field}` 4. Forms handle validation automatically via model rules 5. Call `model.save()` to persist changes 6. Ask before implementing if field importance is unclear 7. JSONB fields don't need `parentRecord` in forms