diff --git a/app/javascript/components/EditGardenModal.jsx b/app/javascript/components/EditGardenModal.jsx new file mode 100644 index 000000000..0d719bef5 --- /dev/null +++ b/app/javascript/components/EditGardenModal.jsx @@ -0,0 +1,156 @@ +import React, {useEffect, useState} from 'react'; + +import {getJson, patchJson} from '../api'; +import Modal from './Modal'; + +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 this garden.']; + if (status === 403) return ['You can\'t change this garden.']; + return ['Something went wrong saving that. Please try again.']; +} + +// The same fields as the garden edit page, in a dialog over the garden list. It +// loads the garden's current values from /gardens/:slug/edit.json, saves with +// PATCH, and hands the refreshed garden card to onSaved, so the list updates in +// place and the page is never left. +export default function EditGardenModal({garden, onClose, onSaved}) { + const [form, setForm] = useState(null); // {garden, area_units, garden_types, location_help, ...} + const [values, setValues] = useState(null); + 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(`${garden.url}/edit.json`, {signal: controller.signal}); + if (!ok) throw new Error('load failed'); + setForm(data); + setValues(data.garden); + } catch (error) { + if (error.name !== 'AbortError') setLoadError(true); + } + })(); + return () => controller.abort(); + }, [garden.url]); + + const set = (field) => (event) => { + const {type, checked, value} = event.target; + setValues((current) => ({...current, [field]: type === 'checkbox' ? checked : value})); + }; + + async function save(event) { + event.preventDefault(); + setSaving(true); + setErrors([]); + const {name, description, location, area, area_unit: areaUnit, garden_type_id: gardenTypeId, active} = values; + try { + const {ok, status, data} = await patchJson(garden.url, { + garden: {name, description, location, area, area_unit: areaUnit, garden_type_id: gardenTypeId, active}, + }); + if (ok) { + onSaved(data.garden); // closes this dialog + return; + } + setErrors(errorMessages(status, data)); + } catch (error) { + setErrors(['Couldn\'t reach the server. Please try again.']); + } + setSaving(false); + } + + return ( + + {loadError && ( +
+
Couldn’t load this garden. Please close this and try again.
+
+ )} + {!loadError && !values && ( +