import React, {useEffect, useState} from 'react'; import {getJson, postJson} from '../api'; import Modal from './Modal'; import Steps from './Steps'; const STEPS = ['How many?', 'Trade?', 'Any notes?']; const QUICK_AMOUNTS = [10, 50, 100, 500, 1000]; const TRADE_LABELS = {nowhere: 'Not for trade', locally: 'Locally', nationally: 'Nationally', internationally: 'Internationally'}; const tradeLabel = (value) => TRADE_LABELS[value] || value; function humanize(field) { return field.replace(/_/g, ' ').replace(/^./, (c) => c.toUpperCase()); } function errorMessages(status, data) { if (status === 422 && data && data.errors) { const messages = Object.entries(data.errors).flatMap(([field, list]) => list.map((m) => `${humanize(field)} ${m}`)); if (messages.length > 0) return messages; } if (status === 401) return ['Please sign in again to save these seeds.']; return ['Something went wrong saving that. Please try again.']; } // Saving seeds from a planting, as a dialog over the garden list, in steps like // the record harvest dialog: about how many, whether to offer them for trade, // then any notes. The crop is the planting's and the date is today; the rest // (organic, source and so on) can be filled in later on the seed itself. // It loads those, and the choices, from /plantings/:slug/seeds/new.json, saves with // POST /seeds.json, and hands the new seed's link to onSaved, so the page is never left. export default function SaveSeedsModal({planting, iconUrl, onClose, onSaved}) { const [form, setForm] = useState(null); // {crop, parent_planting_id, saved_at, tradable_to_values, location, settings_url} const [values, setValues] = useState(null); const [step, setStep] = useState(1); const [loadError, setLoadError] = useState(false); const [saving, setSaving] = useState(false); const [errors, setErrors] = useState([]); useEffect(() => { const controller = new AbortController(); (async () => { try { const {ok, data} = await getJson(`${planting.url}/seeds/new.json`, {signal: controller.signal}); if (!ok) throw new Error('load failed'); setForm(data); setValues({quantity: '', tradable_to: 'nowhere', description: ''}); } catch (error) { if (error.name !== 'AbortError') setLoadError(true); } })(); return () => controller.abort(); }, [planting.url]); const set = (field) => (event) => setValues((current) => ({...current, [field]: event.target.value})); const amount = () => (values.quantity === '' ? 'Not sure' : `About ${Number(values.quantity).toLocaleString()}`); // Each step's button goes on to the next; the last one saves. async function save(event) { event.preventDefault(); if (step < STEPS.length) { setStep(step + 1); return; } setSaving(true); setErrors([]); try { const {ok, status, data} = await postJson('/seeds.json', { seed: {parent_planting_id: form.parent_planting_id, saved_at: form.saved_at, ...values}, }); if (ok) { onSaved(data.seed); // closes this dialog return; } setErrors(errorMessages(status, data)); } catch (error) { setErrors(['Couldn\'t reach the server. Please try again.']); } setSaving(false); } const title = ( <> {iconUrl && } Save {planting.crop.name} seeds ); const change = (toStep, what) => (