# Form System This document covers the generic form infrastructure used by all resource forms in the dashboard. --- ## Layer Overview ``` ← Feature-specific form component └─ useResourceForm(...) ← State: RHF form + optional fetch for edit mode └─ useMutation(...) ← Create or update mutation with toast └─ ← FormProvider +
wrapper └─ ← RHF-connected text input └─ ← RHF-connected static select └─ ← RHF-connected async combobox (fetches options) └─ ) } ``` --- ## Zod Schema Conventions **File:** `modules//.schema.ts` ### Relation Fields Relation fields (foreign-key selects) use a shared `relationFieldSchema`: ```ts import { z } from "zod" const relationFieldSchema = z .object({ value: z.string(), label: z.string() }) .nullable() // In the schema: const mySchema = z.object({ category: relationFieldSchema, // → { value: "3", label: "Electronics" } | null name: z.string().min(1, "Name is required"), }) ``` ### Email Validation Pattern Use union to allow empty strings: ```ts email: z.union([ z.string().email("Enter a valid email address"), z.literal(""), ]).optional(), ``` --- ## `extractItems` — Response Unwrapper Used internally by `RhfAsyncSelectField` to normalize different API response shapes: ```ts // Handles all of: extractItems([{ id: 1, name: "A" }]) // → same array extractItems({ data: [{ id: 1, name: "A" }] }) // → data array extractItems({ data: { data: [{ id: 1, name: "A" }] } }) // → nested data ``` This handles both plain arrays, standard Laravel list responses, and nested pagination wrappers.