mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-10-29 10:18:29 -04:00
* Implement AddSeedsPicker component and enhance planting selection UI; update styles and serializers for better seed management * Enhance PlantingPicker component: add hints and titles for harvest and seed actions; update test to reflect changes in seed saving behavior * Enhance harvest recording: allow harvesting from crops without plantings; update forms and modals for better user experience * Refactor code structure for improved readability and maintainability * Remove AddSeedsPicker component and its associated tests * Add planting picker functionality for harvesting and seed saving - Introduced PlantingPickersController to handle JSON responses for member's plantings. - Updated HomeHelper to include crop ID in planting picker properties. - Refactored PlantPicker and PlantSomethingModal components to support multiple gardens. - Created PlantingFlow component to manage planting actions (harvest and save seeds). - Implemented RecordDialog component to handle server-rendered links for harvesting and seed saving. - Updated various views to include data attributes for triggering the record dialog. - Added routes for planting picker and corresponding request specs for testing. - Adjusted styles in _plantings.scss for better layout and spacing. * Add job management features including job creation and wishlist functionality * Remove WishlistJobsController and associated views and tests * Add suggestion links rendering and fix routes formatting * Refactor AddJobModal and JobPicker components to simplify props and enhance user experience * Update seed addition test to check dialog presence and current path * Apply suggestion from @CloCkWeRX --------- Co-authored-by: Daniel O'Connor <daniel.oconnor@gmail.com>
127 lines
4.7 KiB
React
127 lines
4.7 KiB
React
import React, {useState} from 'react';
|
|
|
|
import {postJson} from '../api';
|
|
import {daysBefore, localToday} from '../dates';
|
|
import Modal from './Modal';
|
|
import WhenPicker from './WhenPicker';
|
|
|
|
const WHEN_CHOICES = [['today', 'Today'], ['tomorrow', 'Tomorrow'], ['other', 'Another day']];
|
|
|
|
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 add this job.'];
|
|
return ['Something went wrong saving that. Please try again.'];
|
|
}
|
|
|
|
// Adding a job, as a dialog over the page: what needs doing, when, and a note
|
|
// if they want one. Saves with POST /jobs.json and hands the job to onSaved.
|
|
export default function AddJobModal({onClose, onSaved}) {
|
|
const [name, setName] = useState('');
|
|
const [description, setDescription] = useState('');
|
|
const [noting, setNoting] = useState(false); // the notes box is open
|
|
const [when, setWhen] = useState('today');
|
|
const [customDate, setCustomDate] = useState('');
|
|
const [saving, setSaving] = useState(false);
|
|
const [errors, setErrors] = useState([]);
|
|
|
|
const today = localToday();
|
|
const dueOn = {today, tomorrow: daysBefore(today, -1), other: customDate}[when];
|
|
|
|
function chooseWhen(choice) {
|
|
setWhen(choice);
|
|
if (choice === 'other' && !customDate) setCustomDate(today);
|
|
}
|
|
|
|
async function save(event) {
|
|
event.preventDefault();
|
|
setSaving(true);
|
|
setErrors([]);
|
|
try {
|
|
const {ok, status, data} = await postJson('/jobs.json', {
|
|
job: {name, description, due_date: dueOn},
|
|
});
|
|
if (ok) {
|
|
onSaved(data);
|
|
return;
|
|
}
|
|
setErrors(errorMessages(status, data));
|
|
} catch (error) {
|
|
setErrors(['Couldn\'t reach the server. Please try again.']);
|
|
}
|
|
setSaving(false);
|
|
}
|
|
|
|
return (
|
|
<Modal title="Add a job" titleId="add-job-title" onClose={onClose}>
|
|
<form onSubmit={save}>
|
|
<div className="modal-body plant-dialog-body">
|
|
{errors.length > 0 && (
|
|
<div className="alert alert-danger" role="alert">
|
|
<i className="fas fa-exclamation-triangle" aria-hidden="true" />{' '}
|
|
<strong>That didn’t save.</strong>
|
|
<ul className="mb-0">{errors.map((message) => <li key={message}>{message}</li>)}</ul>
|
|
</div>
|
|
)}
|
|
<div className="mb-3">
|
|
<label htmlFor="add-job-name" className="visually-hidden">What needs doing?</label>
|
|
<input
|
|
id="add-job-name"
|
|
className="form-control form-control-lg"
|
|
placeholder="What needs doing?"
|
|
aria-describedby="add-job-hint"
|
|
value={name}
|
|
onChange={(event) => setName(event.target.value)}
|
|
required
|
|
autoFocus
|
|
/>
|
|
<p id="add-job-hint" className="step-hint mb-0 mt-2">Water the seedlings, build a trellis for the peas…</p>
|
|
</div>
|
|
<WhenPicker
|
|
id="add-job"
|
|
legend="When?"
|
|
hideLegend
|
|
choices={WHEN_CHOICES}
|
|
when={when}
|
|
onWhen={chooseWhen}
|
|
date={customDate}
|
|
onDate={(event) => setCustomDate(event.target.value)}
|
|
hint={dueOn}
|
|
/>
|
|
{noting ? (
|
|
<div className="mt-3">
|
|
<label htmlFor="add-job-notes" className="visually-hidden">Notes</label>
|
|
<textarea
|
|
id="add-job-notes"
|
|
className="form-control"
|
|
rows="3"
|
|
placeholder="Note"
|
|
value={description}
|
|
onChange={(event) => setDescription(event.target.value)}
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
) : (
|
|
<button type="button" className="btn btn-link ps-0 mt-2" onClick={() => setNoting(true)}>
|
|
<i className="fas fa-plus" aria-hidden="true" /> Add a note
|
|
</button>
|
|
)}
|
|
</div>
|
|
<div className="modal-footer">
|
|
<span className="me-auto step-hint"><i className="fas fa-globe" aria-hidden="true" /> Jobs are public</span>
|
|
<button type="button" className="btn btn-link" onClick={onClose} disabled={saving}>Cancel</button>
|
|
<button type="submit" className="btn btn-success btn-plant" disabled={saving} aria-busy={saving}>
|
|
{saving ? <><i className="fas fa-spinner fa-spin" aria-hidden="true" /> Saving…</> : 'Add job'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</Modal>
|
|
);
|
|
}
|