diff --git a/src/client/routes/threads/$threadId/index.tsx b/src/client/routes/threads/$threadId/index.tsx index 0fc9da9..20ffec3 100644 --- a/src/client/routes/threads/$threadId/index.tsx +++ b/src/client/routes/threads/$threadId/index.tsx @@ -3,9 +3,12 @@ import { Reorder } from "framer-motion"; import { useEffect, useState } from "react"; import { CandidateCard } from "../../../components/CandidateCard"; import { CandidateListItem } from "../../../components/CandidateListItem"; +import { CategoryPicker } from "../../../components/CategoryPicker"; import { ComparisonTable } from "../../../components/ComparisonTable"; +import { ImageUpload } from "../../../components/ImageUpload"; import { SetupImpactSelector } from "../../../components/SetupImpactSelector"; import { + useCreateCandidate, useReorderCandidates, useUpdateCandidate, } from "../../../hooks/useCandidates"; @@ -23,7 +26,6 @@ function ThreadDetailPage() { const { threadId: threadIdParam } = Route.useParams(); const threadId = Number(threadIdParam); const { data: thread, isLoading, isError } = useThread(threadId); - const openCandidateAddPanel = useUIStore((s) => s.openCandidateAddPanel); const candidateViewMode = useUIStore((s) => s.candidateViewMode); const setCandidateViewMode = useUIStore((s) => s.setCandidateViewMode); const selectedSetupId = useUIStore((s) => s.selectedSetupId); @@ -36,6 +38,8 @@ function ThreadDetailPage() { thread?.categoryId ?? 0, ); + const [addCandidateOpen, setAddCandidateOpen] = useState(false); + const [tempItems, setTempItems] = useState< NonNullable["candidates"] | null >(null); @@ -132,7 +136,7 @@ function ThreadDetailPage() { {isActive && ( + + +
+ {/* Image */} + + setForm((f) => ({ ...f, imageFilename: filename })) + } + /> + + {/* Name */} +
+ + setForm((f) => ({ ...f, name: e.target.value }))} + className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent" + placeholder="e.g. Osprey Talon 22" + autoFocus + /> + {errors.name && ( +

{errors.name}

+ )} +
+ + {/* Weight & Price row */} +
+
+ + + setForm((f) => ({ + ...f, + weightGrams: e.target.value, + })) + } + className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent" + placeholder="e.g. 680" + /> + {errors.weightGrams && ( +

+ {errors.weightGrams} +

+ )} +
+
+ + + setForm((f) => ({ + ...f, + priceDollars: e.target.value, + })) + } + className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent" + placeholder="e.g. 129.99" + /> + {errors.priceDollars && ( +

+ {errors.priceDollars} +

+ )} +
+
+ + {/* Category */} +
+ + setForm((f) => ({ ...f, categoryId: id }))} + /> +
+ + {/* Notes */} +
+ +