import { useEffect, useRef, useState } from 'react' import { Button } from '@/components/ui/Button' import { TextField } from '@/components/ui/TextField' import { useRemovePlanting, useUpdatePlanting } from '@/lib/objects' import type { Plant } from '@/lib/plants' import { computeDerivedCount, type EditorPlanting } from '@/lib/plantings' import { cmFromSpacing, spacingFromCm, spacingUnitLabel, type UnitPref } from '@/lib/units' import { PlantIcon } from '@/components/plants/PlantIcon' const MIN_RADIUS_CM = 1 /** * Property panel for the selected plop. Radius/label/date/count commit a PATCH on * blur (carrying the version); the count field shows the live derived value as a * placeholder and takes an override when typed. "Remove" soft-removes (keeps the * row with removed_at); "Change plant" opens the picker via onChangePlant. */ export function PlopInspector({ plop, plant, gardenId, unit, onChangePlant, onClose, }: { plop: EditorPlanting plant?: Plant gardenId: number unit: UnitPref onChangePlant: () => void onClose: () => void }) { const update = useUpdatePlanting(gardenId) const remove = useRemovePlanting(gardenId) const rootRef = useRef(null) const [radius, setRadius] = useState(String(spacingFromCm(plop.radiusCm, unit))) const [count, setCount] = useState(plop.count != null ? String(plop.count) : '') const [label, setLabel] = useState(plop.label ?? '') const [planted, setPlanted] = useState(plop.plantedAt ?? '') // Re-sync when the plop changes underneath us (a drag/resize or a server row), // unless the user is editing a field here. useEffect(() => { if (rootRef.current?.contains(document.activeElement)) return setRadius(String(spacingFromCm(plop.radiusCm, unit))) setCount(plop.count != null ? String(plop.count) : '') setLabel(plop.label ?? '') setPlanted(plop.plantedAt ?? '') }, [plop.version, plop.radiusCm, plop.count, plop.label, plop.plantedAt, unit]) const patch = (fields: Omit[0], 'id' | 'version'>) => update.mutate({ id: plop.id, version: plop.version, ...fields }) const u = spacingUnitLabel(unit) const derived = plant ? computeDerivedCount(plop.radiusCm, plant.spacingCm) : plop.derivedCount function commitRadius() { const v = parseFloat(radius) if (!Number.isFinite(v)) return const cm = Math.max(MIN_RADIUS_CM, cmFromSpacing(v, unit)) if (cm !== plop.radiusCm) patch({ radiusCm: cm }) } function commitCount() { if (count.trim() === '') { if (plop.count != null) patch({ count: null }) // restore derived return } const n = Number(count) if (!Number.isInteger(n) || n < 1) return if (n !== plop.count) patch({ count: n }) } return (

Plant

{plant ? ( ) : ( ? )} {plant?.name ?? 'Unknown plant'}
setRadius(e.target.value)} onBlur={commitRadius} /> setCount(e.target.value)} onBlur={commitCount} hint={plop.count != null ? 'Override — clear for auto' : 'Auto from area ÷ spacing²'} />
setLabel(e.target.value)} onBlur={() => label !== (plop.label ?? '') && patch({ label: label.trim() || null })} /> setPlanted(e.target.value)} onBlur={() => planted !== (plop.plantedAt ?? '') && planted !== '' && patch({ plantedAt: planted })} />
) }