import React, {useEffect, useState} from 'react';
import {getJson, postJson} from '../api';
import Modal from './Modal';
import Steps from './Steps';
const SOURCES = {
saved: 'my own seed saving',
bought: 'retail outlet',
catalogue: 'seed catalogue',
seedbank: 'seed bank or similar institution',
traded: 'traded from another person',
other: 'other/unknown',
};
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.
//
// Without a planting, give it a crop instead, and where the seeds came from as
// source (a key of SOURCES, saved unless said). The form comes from /seeds/new.json?crop_id=.
export default function SaveSeedsModal({planting, crop: chosenCrop, iconUrl, lead = [], source: knownSource, onBack, onClose, onSaved}) {
const crop = chosenCrop || planting.crop;
const [form, setForm] = useState(null); // {crop, parent_planting_id, saved_at, tradable_to_values, location, settings_url}
const [values, setValues] = useState(null);
const formUrl = planting ? `${planting.url}/seeds/new.json` : `/seeds/new.json?crop_id=${crop.id}`;
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(formUrl, {signal: controller.signal});
if (!ok) throw new Error('load failed');
setForm(data);
setValues({quantity: '', tradable_to: 'nowhere', description: '', source: SOURCES[knownSource || 'saved']});
} catch (error) {
if (error.name !== 'AbortError') setLoadError(true);
}
})();
return () => controller.abort();
}, [formUrl]);
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: {crop_id: crop.id, 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 {crop.name} seeds
>
);
const change = (toStep, what) => (
);
return (
{loadError && (
Couldn’t load the seed form. Please close this and try again.
)}
{!loadError && !values && (
Loading…
)}
{values && (
)}
);
}
// Shown with the steps before it, in the picker that opens it.
SaveSeedsModal.STEPS = STEPS;