Files
growstuff/app/javascript/components/PlantedDate.jsx
T
Brenda WallaceandClaude Sonnet 5 64c88c23fe Let owners edit the planted date on garden cards
The date on an annual planting row is a button with a pencil hint for
plantings the viewer can edit. It opens the browser's date picker, saves
with the tick or Enter, and swaps in the refreshed card so predictions
follow the new date.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 17:42:26 +12:00

103 lines
3.3 KiB
React

import React, {useEffect, useRef, useState} from 'react';
import {patchJson} from '../api';
import {formatDate} from '../dates';
function errorMessage(status, data) {
if (status === 422 && data && data.errors) {
const [field, messages] = Object.entries(data.errors)[0] || [];
if (field) return `${field.replace(/_/g, ' ').replace(/^./, (c) => c.toUpperCase())} ${messages[0]}`;
}
return 'Couldn\'t save the date. Please try again.';
}
// "Planted 12 Sep". When the viewer owns the planting it is a button with a
// pencil that opens the browser's own date picker, saved with the tick (or
// Enter) and dropped with the cross (or Escape). `onUpdated` gets the garden's
// refreshed card, so the row's predictions follow the new date.
export default function PlantedDate({planting, onUpdated}) {
const {url, planted_at: plantedAt, can_edit: canEdit} = planting;
const [editing, setEditing] = useState(false);
const [value, setValue] = useState(plantedAt || '');
const [saving, setSaving] = useState(false);
const [error, setError] = useState(null);
const input = useRef(null);
useEffect(() => {
if (editing && input.current) input.current.focus();
}, [editing]);
if (!canEdit) return plantedAt ? <span className="planting-row-planted">Planted {formatDate(plantedAt)}</span> : null;
function open() {
setValue(plantedAt || '');
setError(null);
setEditing(true);
}
function cancel() {
setEditing(false);
setError(null);
}
async function save() {
if (!value || value === plantedAt) return cancel();
setSaving(true);
setError(null);
try {
const {ok, status, data} = await patchJson(url, {planting: {planted_at: value}});
if (ok) {
setSaving(false);
setEditing(false);
onUpdated(data.garden);
return;
}
setError(errorMessage(status, data));
} catch (failure) {
setError('Couldn\'t reach the server. Please try again.');
}
setSaving(false);
}
function onKeyDown(event) {
if (event.key === 'Enter') {
event.preventDefault();
save();
} else if (event.key === 'Escape') {
cancel();
}
}
if (!editing) {
return (
<button type="button" className="planting-row-planted planting-date-button" onClick={open} title="Change the planted date">
{plantedAt ? `Planted ${formatDate(plantedAt)}` : 'Set planted date'}
<i className="fa fa-pencil" aria-hidden="true" />
</button>
);
}
return (
<span className="planting-date-editor">
<input
ref={input}
type="date"
className="form-control form-control-sm"
aria-label="Planted date"
value={value}
disabled={saving}
onChange={(event) => setValue(event.target.value)}
onKeyDown={onKeyDown}
/>
<button type="button" className="btn btn-sm btn-link" onClick={save} disabled={saving || !value} aria-label="Save planted date">
<i className="fa fa-check" aria-hidden="true" />
</button>
<button type="button" className="btn btn-sm btn-link" onClick={cancel} disabled={saving} aria-label="Cancel">
<i className="fa fa-times" aria-hidden="true" />
</button>
{error && <small className="planting-date-error" role="alert">{error}</small>}
</span>
);
}