Add plant catalog UI: /plants page + PlantPicker (#13)
- 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
This commit is contained in:
@@ -68,3 +68,29 @@ export function formatDimensions(widthCm: number, heightCm: number, unit: UnitPr
|
||||
export function dimensionUnitLabel(unit: UnitPref): string {
|
||||
return unit === 'imperial' ? 'ft' : 'm'
|
||||
}
|
||||
|
||||
// --- Plant spacing (small scale) -------------------------------------------
|
||||
// Plant spacing is a handful of centimeters, so meters/feet read poorly here;
|
||||
// metric shows cm and imperial shows whole inches.
|
||||
|
||||
/** Centimeters → a spacing string in the given unit (e.g. "15 cm" or "6″"). */
|
||||
export function formatSpacing(cm: number, unit: UnitPref): string {
|
||||
if (unit === 'imperial') return `${Math.round(cm / CM_PER_INCH)}″`
|
||||
return `${Math.round(cm)} cm`
|
||||
}
|
||||
|
||||
/** A spacing value typed in the given unit (cm, or inches) → whole centimeters. */
|
||||
export function cmFromSpacing(value: number, unit: UnitPref): number {
|
||||
return unit === 'imperial' ? Math.round(value * CM_PER_INCH) : Math.round(value)
|
||||
}
|
||||
|
||||
/** Centimeters → a spacing number in the given unit, for prefilling an input.
|
||||
* Inches to 0.1 so cm-quantization doesn't show through. */
|
||||
export function spacingFromCm(cm: number, unit: UnitPref): number {
|
||||
return unit === 'imperial' ? Math.round((cm / CM_PER_INCH) * 10) / 10 : Math.round(cm)
|
||||
}
|
||||
|
||||
/** The spacing input-field unit label. */
|
||||
export function spacingUnitLabel(unit: UnitPref): string {
|
||||
return unit === 'imperial' ? 'in' : 'cm'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user