Commit Graph
47 Commits
Author SHA1 Message Date
Brenda WallaceandClaude Sonnet 5 9dae899693 Quick planting addition form (#4808)
* Add rake tasks to pull crops and a member's data from production

crops:pull_from_production copies approved crops, and
members:pull_from_production copies one member's gardens, plantings and
planting photos, from growstuff.org into a local database. Both refuse to
run in a production environment.

They are built to be gentle on a server that has been struggling: one
request at a time with a pause between requests, exponential backoff
(honouring Retry-After) on timeouts, 429s and 5xx, and a hard stop rather
than endless retries. The client also stops if a server keeps sending the
same page, and will not follow a next link to another host.

Gardens and plantings come from the site's own JSON, which carries every
column; the JSON:API only exposes a garden's name, and its related
resource endpoints return the wrong records. Only the crops that a
member's plantings use are fetched. Flickr photos are stored as records
pointing at Flickr's image URLs, as on production, so nothing is
downloaded from Flickr.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Add ACTIVE_ONLY option to the member import

ACTIVE_ONLY=true leaves out the site's all=1 flag, so production lists only
the member's active gardens and current (not finished, not failed)
plantings. The server does the filtering, so it also means fewer requests,
and no crops or photos are fetched for plantings that are skipped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Bundle JavaScript with esbuild via jsbundling-rails

Adds React and an esbuild build (yarn build, yarn build:watch) that writes
app/assets/builds/react_islands.js, which Sprockets serves. jsbundling-rails
runs the build as part of assets:precompile, which CI and the Dockerfile
already run after yarn install.

react_islands.jsx is the entry point: it mounts a React component into every
element with data-react-component. No components are registered yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Add react_component view helper

Renders an empty element carrying the component name and its props as JSON,
for react_islands.jsx to mount a React component into. Props go through
Rails' attribute escaping.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Show a React GardenSummary island on the gardens index

The first React island, to prove the pipeline end to end: server-rendered
props (owner and garden count) drive a small component with client-side
state. Loaded only on this page, deferred, via the scripts content block.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Add GardenCardSerializer for the React garden cards

Turns a page of gardens into plain hashes: the garden, its owner, its
actions menu, and its active plantings split into perennials and annuals,
with progress, badges and quick actions. It mirrors what gardens/_card,
gardens/_actions and the planting partials render today, so the server
still decides labels, links and permissions.

Active plantings for the whole page are loaded in one query (with harvests
and the crop's parents), instead of several queries per garden.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Render the garden cards on the index with React

The gardens index now hands GardenCardSerializer's output to a GardenCards
island instead of rendering gardens/_card per garden. The actions menu moves
to the top right of the card header. Everything else on the card is the same:
image, perennials, and each annual planting with badges, progress bar and its
own quick actions menu. The server still chooses labels, links and
permissions.

The dropdowns keep the existing Bootstrap and jquery_ujs markup
(data-bs-toggle, data-method, data-confirm), so mark inactive, delete and the
confirmations work unchanged. Menu button ids are now unique per card, so the
edit-from-index spec finds the menu by its Actions text instead of the old
repeated id.

gardens/_card stays for the activity page. This replaces the GardenSummary
island, which only proved the pipeline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Don't raise when validating a planting that has no garden

owner_must_match_garden_owner called garden.owner unconditionally, so a
planting with a missing garden raised NoMethodError (a 500) instead of
failing the garden presence validation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Create plantings as JSON for the React garden cards

POST /plantings.json returns the garden's updated card (201), or the
validation errors (422), so the cards can show a new planting without a page
reload. It is for our own pages only: session cookie plus CSRF token, and no
CORS headers, unlike the public /api/v1.

Creating now also checks the garden is yours. Before, can :create Planting
had no garden condition and create never authorized the garden, so a crafted
request could plant into someone else's garden. This applies to the HTML form
as well. A missing garden is still left to the presence validation.

CanCan::AccessDenied now answers JSON requests with a 403 and an error,
instead of redirecting them.

The request spec logs in through a real cookie session to exercise CSRF
protection: PlantingsController uses the null_session strategy, so a request
without the token is treated as signed out (401), and the sign_in test helper
would bypass that.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Don't ask which garden when planting from a garden

Arriving at the planting form with a garden_id of one of your own gardens
(from 'Plant something here') now shows 'Planting in <garden>' with a hidden
garden field and a link to choose a different one, instead of the garden
radio buttons. Any other arrival, including a garden_id that isn't yours,
still asks as before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Open 'Plant something here' as a dialog over the garden cards

Clicking 'Plant something here' on a card opens a dialog to plant into that
garden, without leaving the list or asking which garden. Saving posts to
POST /plantings.json, swaps in the garden's updated card so the new planting
shows straight away (briefly highlighted, with a confirmation), and closes the
dialog. Errors show inside it and it stays open. A ctrl/cmd/shift-click still
opens the normal form.

The dialog is a React-controlled Bootstrap modal: focus moves in and is
trapped, Escape and the backdrop close it, and focus returns afterwards. The
crop picker searches /crops/search.json like the existing autosuggest, and the
planted-from and sun choices come from Planting's constants via props.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Use the Bootstrap 4 class names the app's CSS defines in the card components

The stylesheet is MDB 4 (Bootstrap 4), with Bootstrap 5's JavaScript on top,
so Bootstrap 5-only classes had no styling: the dialog's close button was
btn-close (a small grey box), and visually-hidden, form-select and text-start
did nothing. Use close with a times sign, sr-only, form-control and text-left,
as the app's existing modals do.

dropdown-menu-end stays, because Bootstrap 5's dropdown JavaScript reads it to
place the menu; dropdown-menu-right is added for the CSS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Make the plant-something form read as a sentence

'I planted [N] [crop](s) on [date] from [seed] in [sun].' Each blank is an
underlined inline field: quantity, the crop search, the planted date, and
planted-from and sun/shade selects. The (s) drops away when the quantity is 1.
The blanks are labelled for screen readers (Quantity, Crop, Planted date,
Planted from, Sun or shade). Notes and 'Mark as finished' stay below.

The browser spec now fills in quantity, planted from and sun as well, and
checks they are saved.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Hint at typing a crop name, and offer to request one that isn't found

The crop blank says 'type a crop name'. When a search finds nothing it says
so and links to the new-crop form (in a new tab, so the dialog stays open),
as the old planting form did.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Say 'number' in the quantity blank

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Choose a crop with the cursor keys

The crop picker is now an accessible combobox. As you type, matches appear in
a listbox: Up and Down move through them, Enter chooses the highlighted one
(or the top match if none is highlighted), and Escape closes the list before a
second Escape closes the dialog. Focus moves to 'Change' once a crop is chosen.
Matches can still be clicked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Trim the plant-something form to just the sentence

Remove 'Mark as finished' and the 'Tell us more about it' notes from the
dialog, along with the never-shown rating field it carried in its state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Make the highlighted crop match obvious

The theme's list-group 'active' shade was barely darker than the other
matches. The one Enter would choose now has a solid blue background, white
bold text and a darker left edge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Tidy the plant-something sentence layout

Group the sentence into phrases (I planted..., on..., from..., in...) that stay
together when it wraps, so a word is never stranded away from its blank. Hide
the quantity spinner that clipped 'number', widen the date, and give the two
selects a minimum width, with a grey 'optional' when they are still empty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Don't let a second Enter undo a chosen crop

After choosing a crop the input is replaced by the crop's name and a Change
button. Focus went to that button, so pressing Enter again pressed it and
cleared the choice. Focus now moves on to the date blank, so a second Enter
submits the form as it would from any field.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Plant with one search and a confirmation, instead of the sentence form

The dialog asks one big question, 'What did you plant?', with the same
crop search as before (arrow keys, Enter, and a way to request a crop that
isn't found). Choosing a crop shows it back ('You're planting lettuce in
Orchard, today.') with Change and Plant it buttons, and focus lands on Plant
it so Enter confirms. The planting gets today's date and no other details;
those can be added later, which keeps adding one quick.

Drops the sentence blanks, their styles, and the planted-from/sun option lists
the page passed down.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Fix unreadable text on a just-planted crop

The highlight for a newly added planting set an inline pale green background
on the crop chip. Chips are brown with white text, so the name became white on
light green. The highlight is now a CSS class that leaves the chip's colours
alone: a soft green row with a left bar for annual plantings, and a green ring
around the chip for perennials. The browser spec checks the chip carries no
inline style.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Tidy the plant dialog and show what is happening at each stage

Standard patterns so the dialog always says where you are and what it is doing:
- a two-step indicator (Choose a crop, Confirm) with a tick on the finished step
- a spinner in the search box while searching, plus a 'Searching...' status, a
  count of matches, and an info icon on the 'no crops match' note
- the choice shown back as a Crop / Garden / When summary with a Change button
- Plant it as a filled green primary button that stays green while focused (the
  theme greys a focused button) and shows a spinner while planting, with Cancel
  and Change disabled meanwhile
- an error panel with a warning icon that says the choice is kept for retrying
- the matches listed in the dialog rather than floating over its buttons
- a tick on the 'Planted ...' notice on the cards
- the shovel (spade-marker.svg, the map marker) in the dialog title

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Build the React bundle in CI and for fresh checkouts

app/assets/builds/react_islands.js is built by esbuild and not committed, so on
a fresh checkout pages that load it (the gardens index) fail with 'The asset
react_islands.js is not present in the asset pipeline'. The rspec CI job never
built it: only the browser-feature jobs did, through assets:precompile. Add a
yarn build step there, and have the specs build the bundle once if it is
missing so a local checkout doesn't fail either.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Add the missing 'View' translation

The planting quick-actions menu used t('view'), which has no translation. In a
view Rails hides that by showing a humanised 'View'; the card serializer uses
I18n.t, which showed 'Translation missing: en.view' in the menu. Add
buttons.view, use it in both places, and add a serializer spec that fails if
any card label is a missing translation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Fix two crop_jsonld_data specs that could not pass

The helper builds its hash with symbol keys, but the spec read 'data["@context"]'
with a string key. And 'caps posts and photos at 50' relied on
create(:post, crops: [crop]), which creates no crop_posts rows here, and on
crop.photo_associations.create!, which the Crop model's own association leaves
without a photographable. Link posts with CropPost and photos with
PhotoAssociation explicitly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Run the helper and serializer specs in CI

The rspec job ran lib, services, models, controllers, views, routing and
requests, but not spec/helpers or spec/serializers, so those specs (including
the react_component helper and the card serializer) never ran there, and two
broken crops_helper specs went unnoticed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Tidy the garden cards

The cards were cluttered and misaligned. Now:
- the header has a smaller title with the owner under it, and the menu top right
- a smaller picture, and 'Perennials' and 'Annuals' headings instead of a
  divider and a gap; 'Nothing planted here yet.' for an empty garden
- each annual planting is a row of three aligned columns: the crop with when it
  was planted (which tells duplicates apart), then its badges and progress bar,
  then a labelled three-dots menu in place of a bare chevron
- the note when there is nothing to predict from is one short muted line that
  no longer repeats the crop name
- on a phone the picture and rows stack

Also restores the just-planted highlight styles, which a stylesheet edit had
dropped, and adds a spec that the highlight has a background, since the class
existing was all that was checked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Make the planting menu button compact

The theme gives every .btn a 5.5rem minimum width, so the three-dots button's
hover background was a wide grey box. It is now a 2.25rem round button.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 16:04:15 +09:30
google-labs-jules[bot]andCloCkWeRX e423e6ac79 Add weekly harvest reminder emails and scheduled task
- Added `send_harvest_reminder` preference to Member model and settings UI.
- Implemented `harvest_in_next_week?` in PredictHarvest concern.
- Created `harvest_reminder` email with localized templates.
- Added `growstuff:send_harvest_reminders` Rake task to run weekly.
- Refactored existing and new reminder tasks to use `deliver_later` for scalability.
- Added unit tests for prediction logic and mailer.
- Fixed a bug in the existing planting reminder task where it was using an uninitialized constant `Notifier`.

Co-authored-by: CloCkWeRX <365751+CloCkWeRX@users.noreply.github.com>
2026-04-30 04:09:26 +00:00
google-labs-jules[bot] 76a6c1d849 Merge pull request #4131 from Growstuff/feature/add-social-media-links
feat: Add social media links to user profiles
2025-08-24 16:21:05 +09:30
Daniel O'Connor 81060cccf7 Ruby 3.2: Rubocop - Lint/* and Style/* (#3786)
* Rubocop - Layout/*
* Rubocop - Lint and Style
2024-07-13 15:38:37 +09:30
Daniel O'Connor 26189855cc Reduce visual impact of hover, and make snappier (#3712)
* Reduce visual impact of hover, and make snappier

* Better seed

* Improve seeding

* Fix codespaces login
2024-04-07 22:17:16 +09:30
Brenda 17f5b1c562 Routes using slugs (instead of ids)
this seems to be intended by the models having slugs (added about 7 years ago) but the routes weren't using them
2020-01-10 16:50:52 +13:00
Brenda 7257b2ada1 Update with frozen literals 2020-01-10 16:50:52 +13:00
Brenda Wallace 04fd1a343a Rubocop update (#2334)
* Update rubocop config

* Rubocop update - for rubocop 0.77

* [CodeFactor] Apply fixes

* rubocop fixes

* [CodeFactor] Apply fixes to commit 2826c2e
2019-12-16 22:35:33 +13:00
Brenda Wallace 5072353293 Only save location (used for post-signin redirect) if this is a format html page 2019-09-26 21:50:42 +12:00
Brenda Wallace f4df42d136 Fixes not being able to find the 404 page file 2019-01-01 13:42:53 +13:00
Brenda Wallace 05bfbda5f5 Rubocop style fix ups 2018-06-04 16:18:49 +12:00
Brenda Wallace cc838e19b5 Show a 404 page on record not found 2018-06-04 10:59:34 +12:00
pozorvlak 25a1f0da9a Merge branch 'dev' into gardens-controller 2017-02-20 13:11:18 +00:00
Brenda Wallace 1a3e976ba5 DRY the gardens controller 2017-02-20 02:36:51 +00:00
Brenda Wallace 550848a513 ParenthesesAroundCondition fixup 2017-02-19 16:20:45 +13:00
Brenda Wallace e184503caa Fix up to match rubocop's MultilineMethodCallBraceLayout recommendation 2017-02-19 16:17:03 +13:00
Brenda Wallace 1217ba5b48 Reduce complexity of ApplicationController.store_location 2017-02-04 11:19:11 +13:00
Brenda Wallace d60dcfbddd Use request.referer instead of request.referrer 2016-11-26 17:26:44 +13:00
Brenda Wallace 07c135eeb5 before_filter is now before_action 2016-11-26 17:20:13 +13:00
Brenda Wallace 723ebff923 Spacing fixes 2016-11-26 12:13:17 +13:00
Brenda Wallace 62e7c716dd Removed extra empty blank lines 2016-11-26 12:06:02 +13:00
Brenda Wallace 83392ddda1 Indentation clean ups 2016-11-26 10:51:54 +13:00
pozorvlak f3dc2ff7e5 Replace deprecated Devise for method with permit
This should eliminate the screens-ful of deprecation warnings when
running the test suite.
2016-06-08 15:00:05 +00:00
Daniel O'Connor 4b4e0cf69a $ rubocop --only HashSyntax --auto-correct
483 files inspected, 2018 offenses detected, 2018 offenses corrected
2016-05-19 15:53:11 -04:00
Jym Paul A. Carandang 9f3d3b2b8f Signout redirects to last page visited 2015-07-10 12:51:36 +08:00
Taylor Griffin 86cea2ad9f add current_password to permitted params 2015-01-11 14:55:12 +11:00
Taylor Griffin ccef46a5cf add more permitted parameters to member update 2015-01-11 14:32:19 +11:00
Taylor Griffin c6cc8ee6bb configure devise account update for strong parameters 2014-12-31 18:32:37 +11:00
Taylor Griffin acbd3f9a9e implement strong parameters instead of protected attributes 2014-11-18 20:08:54 +11:00
Taylor Griffin 0ffe9adc86 tests are green 2014-11-16 21:40:43 +11:00
Taylor Griffin 6a7c935d43 try setting locale from query parameter first 2014-10-16 06:37:02 +11:00
Taylor Griffin a7da4b9ce4 no longer necessary to pass locale quuery param in every url 2014-09-28 21:38:28 +10:00
Taylor Griffin d10a62d336 set locale from subdomain 2014-09-28 21:32:15 +10:00
Taylor Griffin 984503480f remove necessity to add locale to url if current locale is English 2014-09-25 07:00:11 +10:00
Taylor Griffin 16f4d2f80e fix spec to check href with newly added locale query param 2014-09-19 07:17:08 +10:00
Taylor Griffin f355c393f0 basic setup 2014-09-18 06:15:59 +10:00
Skud 965a3bf2a1 Modernise redirection after signup to use devise's stored_location_for 2014-08-31 18:28:26 +10:00
Skud 238fd0b3ff don't redirect to homepage after signin 2013-08-21 22:51:40 +10:00
Skud a3b7822268 Review and cleanup of JSON API
Removed JSON from classes that shouldn't have it.
(PT: https://www.pivotaltracker.com/story/show/54570954)

Also found a couple of controllers with broken authorization (i.e. not
checking CanCan). Incidentally, this also fixes the comment form bug at
https://www.pivotaltracker.com/story/show/54328716.
2013-08-05 13:29:07 +10:00
Skud 2ecbd8315d bugfix: don't say 'not yet set' in planting form
PT: https://www.pivotaltracker.com/story/show/51457917

Maco found this. The problem was that if you had a blank planting date,
and then re-edited the planting, it would say "not yet set" in the form
field, then die when it later tried to convert that to a date.

I replaced Miles's planted_at_string stuff in the model with a simpler
parse_date method in the application helper.
2013-06-11 16:08:02 +10:00
Miles Gould 5866cc5bbc Document current_user vs current_member usage. 2013-03-29 15:19:01 +00:00
Skud 21960a5494 Tried to get controller tests passing.
All are fine except for plantings.  I'm pretty sure hte problem is to do
with these two lines in app/model/ability.rb

  can :update, Planting, :garden => { :owner_id => member.id }
  can :destroy, Planting, :garden => { :owner_id => member.id }

I'm pretty sure that's the right syntax (according to
https://github.com/ryanb/cancan/wiki/Defining-Abilities) but since it's
the update and delete actions that are failing, I guess I must have it
wrong somehow.  Any hints?
2013-02-01 13:25:06 +11:00
Skud 2d6f1ae210 redirect to previous page if available 2013-02-01 11:29:14 +11:00
Skud cd469264bd this seems to mostly work 2013-02-01 11:21:13 +11:00
Skud ed2e73832d sorta kinda works, but login/homepage are b0rked 2013-02-01 11:10:12 +11:00
Skud ea8f30bd8d tweaked cancan defaults (we don't have current_user) 2013-02-01 10:25:54 +11:00
Cesy Avon 95145f2869 Moving development to top level directory 2012-08-23 10:41:00 -07:00