Correctness:
- PlantFormModal: validate days-to-maturity with Number() so "1.5" is rejected
rather than silently truncated to 1 (parseInt). Accurate "at least 1 <unit>"
spacing message; spacing input min now matches validation. Drop the icon
maxLength that could split a multi-codepoint emoji (server caps at 32 bytes).
- PlantPicker: namespace row keys (recent- / all-) so a plant shown in both the
Recent and All sections no longer collides on a duplicate React key.
Dedupe (the review's dominant, multi-model theme) — also sets up #15's reuse:
- lib/plants.ts: shared CategoryFilter type + filterPlants() helper.
- components/plants/CategoryChips.tsx and PlantIcon.tsx: extracted the chip row
and the color-mix icon tile, now used by both the /plants page and PlantPicker
(and PlantCard for the tile).
Skipped (cosmetic/cross-file, low agreement): conflictPlant→generic helper,
PlantCard vs GardenCard button-class dup, form component length, and localStorage
user-scoping (recent custom-plant ids don't resolve cross-user anyway).
tsc --noEmit clean; 20/20 vitest; production build green.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
- web/src/lib/plants.ts: zod-validated Plant schema + react-query hooks
(list/create/update/delete) over the #12 endpoints, with the category enum,
labels, isBuiltin helper, and a 409 conflict extractor.
- /plants page: searchable, category-chip-filtered grid of PlantCard; built-ins
badged and offer only Duplicate; own plants add Edit/Delete. A local
metric/imperial spacing toggle (no per-user unit pref exists server-side).
- PlantFormModal: create/edit/duplicate a custom plant (name, category, spacing,
color, emoji icon, days-to-maturity, notes), unit-aware spacing, 409 rebase.
- DeletePlantModal: confirm + surface the server's PLANT_IN_USE refusal inline.
- editor/PlantPicker.tsx: reusable search-first chooser with recently-used
(localStorage) and category filter, bottom-sheet on mobile / panel on desktop,
onSelect callback. Demoed via the /plants "Try the picker" button until #15
consumes it.
- units.ts: small-scale spacing helpers (cm/inches) + tests.
tsc --noEmit clean; 20/20 vitest; production build green.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi