From f27838745cdb23977010fa638c3480e441bcd17e Mon Sep 17 00:00:00 2001 From: Bonna Moon Date: Mon, 15 Jun 2026 17:41:06 -0500 Subject: [PATCH] =?UTF-8?q?v0.2.2=20=E2=80=94=20Fix=20add=20plant=20form?= =?UTF-8?q?=20(simplify,=20remove=20react-hook-form)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/garden/add-plant-button.tsx | 177 +++++++++++++-------- src/lib/version.ts | 2 +- 2 files changed, 111 insertions(+), 68 deletions(-) diff --git a/src/components/garden/add-plant-button.tsx b/src/components/garden/add-plant-button.tsx index 1cec26c..a370126 100644 --- a/src/components/garden/add-plant-button.tsx +++ b/src/components/garden/add-plant-button.tsx @@ -2,9 +2,6 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; import { PlantCategory } from "@prisma/client"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -20,91 +17,108 @@ import { useToast } from "@/hooks/use-toast"; import { Plus, Leaf } from "lucide-react"; import { CATEGORY_LABELS, CATEGORY_ORDER } from "@/lib/garden/categories"; import { searchPlants, type PlantSuggestion } from "@/lib/garden/plant-db"; -import { cn } from "@/lib/utils"; -const schema = z.object({ - commonName: z.string().min(1, "Name is required"), - species: z.string().optional(), - variety: z.string().optional(), - category: z.nativeEnum(PlantCategory), - zone: z.string().optional(), - plantedAt: z.string().optional(), - source: z.string().optional(), - notes: z.string().optional(), -}); -type FormValues = z.infer; +const BLANK = { + commonName: "", + species: "", + variety: "", + category: "CANOPY_TREE" as PlantCategory, + zone: "", + plantedAt: "", + source: "", + notes: "", +}; export function AddPlantButton() { const [open, setOpen] = useState(false); + const [fields, setFields] = useState(BLANK); + const [error, setError] = useState(""); + const [busy, setBusy] = useState(false); const [suggestions, setSuggestions] = useState([]); const [showSuggestions, setShowSuggestions] = useState(false); - const [category, setCategory] = useState("CANOPY_TREE"); const router = useRouter(); const { toast } = useToast(); - const form = useForm({ - resolver: zodResolver(schema), - defaultValues: { category: "CANOPY_TREE" }, - }); + function set(key: keyof typeof BLANK, value: string) { + setFields((f) => ({ ...f, [key]: value })); + } function onNameChange(value: string) { - form.setValue("commonName", value); + set("commonName", value); const results = searchPlants(value); setSuggestions(results); setShowSuggestions(results.length > 0); } - function applySuggestion(suggestion: PlantSuggestion) { - form.setValue("commonName", suggestion.commonName); - if (suggestion.species) form.setValue("species", suggestion.species); - if (suggestion.notes && !form.getValues("notes")) form.setValue("notes", suggestion.notes); - form.setValue("category", suggestion.category); - setCategory(suggestion.category); + function applySuggestion(s: PlantSuggestion) { + setFields((f) => ({ + ...f, + commonName: s.commonName, + species: s.species ?? f.species, + category: s.category, + notes: f.notes || s.notes || "", + })); setSuggestions([]); setShowSuggestions(false); } - async function onSubmit(values: FormValues) { + function handleClose() { + setOpen(false); + setFields(BLANK); + setError(""); + setSuggestions([]); + setShowSuggestions(false); + } + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + if (!fields.commonName.trim()) { + setError("Please enter the plant name."); + return; + } + setError(""); + setBusy(true); const res = await fetch("/api/plants", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify(values), + body: JSON.stringify({ + commonName: fields.commonName.trim(), + species: fields.species.trim() || undefined, + variety: fields.variety.trim() || undefined, + category: fields.category, + zone: fields.zone.trim() || undefined, + plantedAt: fields.plantedAt || undefined, + source: fields.source.trim() || undefined, + notes: fields.notes.trim() || undefined, + }), }); + setBusy(false); if (!res.ok) { const err = await res.json().catch(() => ({})); toast({ title: "Error", description: err.error ?? "Could not save plant.", variant: "destructive" }); return; } const plant = await res.json(); - toast({ title: "Plant added!", description: `${values.commonName} is now in your food forest.` }); - setOpen(false); - form.reset(); - setCategory("CANOPY_TREE"); - setSuggestions([]); + toast({ title: "Plant added!", description: `${fields.commonName} is now in your garden.` }); + handleClose(); router.push(`/garden/${plant.id}`); router.refresh(); } - function handleOpen() { - setOpen(true); - setSuggestions([]); - setShowSuggestions(false); - } - return ( <> - - + { if (!v) handleClose(); }}> Add a plant -
+
{/* Name with autocomplete */} @@ -114,17 +128,13 @@ export function AddPlantButton() { id="commonName" placeholder="Start typing — apple, comfrey, zinnia…" autoComplete="off" - {...form.register("commonName", { - onChange: (e) => onNameChange(e.target.value), - })} + value={fields.commonName} + onChange={(e) => onNameChange(e.target.value)} onBlur={() => setTimeout(() => setShowSuggestions(false), 150)} onFocus={() => suggestions.length > 0 && setShowSuggestions(true)} /> - {form.formState.errors.commonName && ( -

{form.formState.errors.commonName.message}

- )} + {error &&

{error}

} - {/* Suggestion dropdown */} {showSuggestions && (
{suggestions.map((s) => ( @@ -138,7 +148,9 @@ export function AddPlantButton() {

{s.commonName}

{s.species && ( -

{s.species} · {CATEGORY_LABELS[s.category]}

+

+ {s.species} · {CATEGORY_LABELS[s.category]} +

)}
@@ -152,18 +164,19 @@ export function AddPlantButton() {
- + set("variety", e.target.value)} + />
+ + set("species", e.target.value)} + />
- + set("zone", e.target.value)} + />
- + set("plantedAt", e.target.value)} + />
- + set("source", e.target.value)} + />
-