import { useState, type FormEvent } from 'react' import { Alert } from '@/components/ui/Alert' import { Button } from '@/components/ui/Button' import { Modal } from '@/components/ui/Modal' import { Select } from '@/components/ui/Select' import { TextArea } from '@/components/ui/TextArea' import { TextField } from '@/components/ui/TextField' import { errorMessage } from '@/lib/api' import { conflictSeedLot, LOT_UNITS, safeExternalUrl, useCreateSeedLot, useUpdateSeedLot, type LotUnit, type SeedLot, } from '@/lib/seedLots' import type { Plant } from '@/lib/plants' /** * Record a purchase, or correct one. Everything except quantity and unit is * optional — the point is to make writing it down cheap enough to bother with, * and a form that demands a SKU and a lot code gets skipped. */ export function SeedLotModal({ plant, lot, onClose, }: { plant: Plant /** Editing an existing lot, or undefined to record a new one. */ lot?: SeedLot onClose: () => void }) { const isEdit = !!lot const create = useCreateSeedLot() const update = useUpdateSeedLot() const pending = create.isPending || update.isPending // A new lot inherits the plant's vendor and source link, since the usual case // is buying the variety you already recorded from the place you recorded it. const [vendor, setVendor] = useState(lot?.vendor ?? plant.vendor ?? '') const [sourceUrl, setSourceUrl] = useState(lot?.sourceUrl ?? plant.sourceUrl ?? '') const [quantity, setQuantity] = useState(lot ? String(lot.quantity) : '') const [unit, setUnit] = useState(lot?.unit ?? 'seeds') const [purchasedAt, setPurchasedAt] = useState(lot?.purchasedAt ?? '') const [packedForYear, setPackedForYear] = useState(lot?.packedForYear != null ? String(lot.packedForYear) : '') const [cost, setCost] = useState(lot?.costCents != null ? (lot.costCents / 100).toFixed(2) : '') const [germination, setGermination] = useState(lot?.germinationPct != null ? String(lot.germinationPct) : '') const [sku, setSku] = useState(lot?.sku ?? '') const [lotCode, setLotCode] = useState(lot?.lotCode ?? '') const [notes, setNotes] = useState(lot?.notes ?? '') const [version, setVersion] = useState(lot?.version ?? 0) const [conflict, setConflict] = useState(null) const [error, setError] = useState(null) async function onSubmit(e: FormEvent) { e.preventDefault() setError(null) setConflict(null) const qty = quantity.trim() === '' ? 0 : Number(quantity) if (!Number.isFinite(qty) || qty < 0) { setError('Quantity must be a number, or left blank.') return } if (sourceUrl.trim() && !safeExternalUrl(sourceUrl.trim())) { setError('The source link needs to be a full http:// or https:// address.') return } let year: number | null = null if (packedForYear.trim()) { const y = Number(packedForYear) if (!Number.isInteger(y) || y < 1900 || y > 2200) { setError('Packed-for year should be a four-digit year.') return } year = y } let costCents: number | null = null if (cost.trim()) { const c = Number(cost) if (!Number.isFinite(c) || c < 0) { setError('Cost must be an amount, or left blank.') return } costCents = Math.round(c * 100) } let germinationPct: number | null = null if (germination.trim()) { const g = Number(germination) if (!Number.isFinite(g) || g < 0 || g > 100) { setError('Germination is a percentage between 0 and 100.') return } germinationPct = g } const input = { plantId: plant.id, vendor: vendor.trim(), sourceUrl: sourceUrl.trim(), sku: sku.trim(), lotCode: lotCode.trim(), purchasedAt: purchasedAt.trim() === '' ? null : purchasedAt.trim(), packedForYear: year, quantity: qty, unit, costCents, germinationPct, notes: notes.trim(), } try { if (isEdit) { await update.mutateAsync({ id: lot.id, version, ...input }) } else { await create.mutateAsync(input) } onClose() } catch (err) { const current = conflictSeedLot(err) if (current) { // Someone edited this lot elsewhere. Rebase onto the fresh row so a // re-save applies, rather than making them retype everything — the same // contract every other version-guarded form here honours. setVersion(current.version) setVendor(current.vendor) setSourceUrl(current.sourceUrl) setQuantity(String(current.quantity)) setUnit(current.unit) setPurchasedAt(current.purchasedAt ?? '') setPackedForYear(current.packedForYear != null ? String(current.packedForYear) : '') setCost(current.costCents != null ? (current.costCents / 100).toFixed(2) : '') setGermination(current.germinationPct != null ? String(current.germinationPct) : '') setSku(current.sku) setLotCode(current.lotCode) setNotes(current.notes) setConflict('This lot changed elsewhere. The latest values are shown — review and save again.') return } setError(errorMessage(err, isEdit ? 'Could not save the lot.' : 'Could not record the lot.')) } } return (
{conflict && {conflict}}
setQuantity(e.target.value)} />