From 6d44a2a780cbb185f9d8aac1423a4e9dd762ac14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 10 Aug 2025 04:49:25 +0000 Subject: [PATCH 01/27] Bump terser from 1.2.5 to 1.2.6 Bumps [terser](https://github.com/ahorek/terser-ruby) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/ahorek/terser-ruby/releases) - [Changelog](https://github.com/ahorek/terser-ruby/blob/master/CHANGELOG.md) - [Commits](https://github.com/ahorek/terser-ruby/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: terser dependency-version: 1.2.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 234508831..93fe9785a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -692,7 +692,7 @@ GEM temple (0.10.4) terminal-table (4.0.0) unicode-display_width (>= 1.1.1, < 4) - terser (1.2.5) + terser (1.2.6) execjs (>= 0.3.0, < 3) thor (1.4.0) thread_safe (0.3.6) From c92b912b2804b29e42806f390cf616ed7194cf74 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Sun, 31 Aug 2025 15:46:18 +0930 Subject: [PATCH 02/27] Fix link --- app/views/home/_stats.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/home/_stats.html.haml b/app/views/home/_stats.html.haml index 5baec4006..7ad0df6ae 100644 --- a/app/views/home/_stats.html.haml +++ b/app/views/home/_stats.html.haml @@ -5,5 +5,5 @@ number_crops: link_to(t('.number_crops_linktext', count: Crop.count.to_i), crops_path), number_plantings: link_to(t('.number_plantings_linktext', count: Planting.count.to_i), plantings_path), number_gardens: link_to(t('.number_gardens_linktext', count: Garden.count.to_i), gardens_path), - contributors: link_to(count_github_contibutors, 'http://github.com/Growstuff/growstuff/CONTRIBUTORS.md', target: '_blank', rel: 'noopener'), + contributors: link_to(count_github_contibutors, 'https://github.com/Growstuff/growstuff/blob/dev/CONTRIBUTORS.md', target: '_blank', rel: 'noopener'), github: link_to('GitHub', 'http://github.com/Growstuff/growstuff', target: '_blank', rel: 'noopener')) From ee2fffd25b0ad74eda0e55e893e4d9ad70a496d1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 31 Aug 2025 22:48:41 +0000 Subject: [PATCH 03/27] Fix: Only show 'add a bio' link on own profile The 'add a bio' link on the member profile page was previously shown based on the `can? :edit, @member` ability check. This caused an issue for admins, who could see the link on other users' profiles, but the link would incorrectly lead to their own settings page. This change modifies the condition to be `member_signed_in? && current_member == @member`. This ensures the link is only displayed when a logged-in user is viewing their own profile, which is the correct and intended behavior. --- app/views/members/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/members/show.html.haml b/app/views/members/show.html.haml index e37d9bd30..d4654b8f8 100644 --- a/app/views/members/show.html.haml +++ b/app/views/members/show.html.haml @@ -28,7 +28,7 @@ %a{href: "#content"} Skip to main content - if @member.bio.blank? - - if can? :edit, @member + - if member_signed_in? && current_member == @member = link_to "Add a bio to complete your profile.", edit_member_registration_path - else #{@member.login_name} hasn't written a bio yet. From 0079513b35b210dcd90f8a6f795daccf7c33b282 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 19:51:24 +0930 Subject: [PATCH 04/27] Merge pull request #4183 from Growstuff/feature/timeline-likes Feature: Display likes on timeline --- app/helpers/event_helper.rb | 2 ++ app/services/timeline_service.rb | 10 ++++++++++ app/views/likes/_description.html.haml | 1 + app/views/timeline/_like.html.haml | 2 ++ app/views/timeline/index.html.haml | 1 + app/views/timeline/likeables/_photo.html.haml | 1 + app/views/timeline/likeables/_post.html.haml | 6 ++++++ spec/features/timeline/index_spec.rb | 6 ++++++ 8 files changed, 29 insertions(+) create mode 100644 app/views/likes/_description.html.haml create mode 100644 app/views/timeline/_like.html.haml create mode 100644 app/views/timeline/likeables/_photo.html.haml create mode 100644 app/views/timeline/likeables/_post.html.haml diff --git a/app/helpers/event_helper.rb b/app/helpers/event_helper.rb index 4bc8033b5..2e127c319 100644 --- a/app/helpers/event_helper.rb +++ b/app/helpers/event_helper.rb @@ -7,6 +7,8 @@ module EventHelper def event_description(event) render "#{event.event_type.pluralize}/description", event_model: resolve_model(event) +rescue ActionView::MissingTemplate + "#{event.event_type.humanize.downcase}d" end def resolve_model(event) diff --git a/app/services/timeline_service.rb b/app/services/timeline_service.rb index 25c5706df..3b813bc5b 100644 --- a/app/services/timeline_service.rb +++ b/app/services/timeline_service.rb @@ -18,10 +18,20 @@ class TimelineService .union_all(photos_query) .union_all(seeds_query) .union_all(activities_query) + .union_all(likes_query) .where.not(event_at: nil) .order(event_at: :desc) end + def self.likes_query + Like + .select("likes.id", + "'like' as event_type", + "likes.created_at as event_at", + "likes.member_id as owner_id", + "null as crop_id") + end + def self.activities_query Activity.select( :id, diff --git a/app/views/likes/_description.html.haml b/app/views/likes/_description.html.haml new file mode 100644 index 000000000..d8427969d --- /dev/null +++ b/app/views/likes/_description.html.haml @@ -0,0 +1 @@ +#{link_to event_model.member, event_model.member} liked #{link_to event_model.likeable.class.name.downcase, event_model.likeable} diff --git a/app/views/timeline/_like.html.haml b/app/views/timeline/_like.html.haml new file mode 100644 index 000000000..fa1f6bb44 --- /dev/null +++ b/app/views/timeline/_like.html.haml @@ -0,0 +1,2 @@ +- likeable = like.likeable += render "timeline/likeables/#{likeable.class.name.downcase}", likeable: likeable diff --git a/app/views/timeline/index.html.haml b/app/views/timeline/index.html.haml index 3285db15b..165f9f977 100644 --- a/app/views/timeline/index.html.haml +++ b/app/views/timeline/index.html.haml @@ -14,6 +14,7 @@ = link_to owner, owner = event_description(event) = render 'timeline/photos', photo: resolve_model(event) if event.event_type == 'photo' + = render 'timeline/like', like: resolve_model(event) if event.event_type == 'like' %small - if event.event_at.present? - if event.event_at.kind_of?(Date) diff --git a/app/views/timeline/likeables/_photo.html.haml b/app/views/timeline/likeables/_photo.html.haml new file mode 100644 index 000000000..1b680a22e --- /dev/null +++ b/app/views/timeline/likeables/_photo.html.haml @@ -0,0 +1 @@ += render 'timeline/photos', photo: likeable diff --git a/app/views/timeline/likeables/_post.html.haml b/app/views/timeline/likeables/_post.html.haml new file mode 100644 index 000000000..5cd54af3d --- /dev/null +++ b/app/views/timeline/likeables/_post.html.haml @@ -0,0 +1,6 @@ +.card.my-2 + .card-body + %blockquote.blockquote.mb-0 + %p= truncate(likeable.body, length: 140) + %footer.blockquote-footer + = link_to "view post", likeable diff --git a/spec/features/timeline/index_spec.rb b/spec/features/timeline/index_spec.rb index 3135bf65a..c80e74305 100644 --- a/spec/features/timeline/index_spec.rb +++ b/spec/features/timeline/index_spec.rb @@ -17,6 +17,10 @@ describe "timeline", :js do let!(:friend_harvest) { FactoryBot.create(:planting, owner: friend2, planted_at: 3.years.ago) } let!(:finished_planting) { FactoryBot.create(:finished_planting, owner: friend1) } let!(:no_planted_at_planting) { FactoryBot.create(:planting, owner: friend2, planted_at: nil) } + let!(:friend_photo) { FactoryBot.create(:photo, owner: friend1) } + let!(:friend_post) { FactoryBot.create(:post, author: friend2) } + let!(:liked_post) { FactoryBot.create(:like, likeable: friend_photo, member: friend2) } + let!(:liked_photo) { FactoryBot.create(:like, likeable: friend_post, member: friend1) } before do login_as(member) @@ -28,6 +32,8 @@ describe "timeline", :js do it { expect(page).to have_link href: planting_path(friend_harvest) } it { expect(page).to have_link href: planting_path(finished_planting) } it { expect(page).to have_no_link href: planting_path(no_planted_at_planting) } + it { expect(page).to have_link href: photo_path(friend_photo) } + it { expect(page).to have_link href: post_path(friend_post) } end describe 'shows the friends you follow' do From a4db05c0f6ff4f78c923a334e653d0619c19ac49 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Mon, 1 Sep 2025 11:25:02 +0000 Subject: [PATCH 05/27] Add a lot of indexes --- .../20250901110545_add_indexes_crops.rb | 53 +++++++++++++ db/schema.rb | 78 ++++++++++++++++++- 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20250901110545_add_indexes_crops.rb diff --git a/db/migrate/20250901110545_add_indexes_crops.rb b/db/migrate/20250901110545_add_indexes_crops.rb new file mode 100644 index 000000000..80d6b522d --- /dev/null +++ b/db/migrate/20250901110545_add_indexes_crops.rb @@ -0,0 +1,53 @@ +class AddIndexesCrops < ActiveRecord::Migration[7.2] + def change + add_index :alternate_names, :crop_id + add_index :alternate_names, :creator_id + add_index :alternate_names, :language + + add_index :comments, %i(commentable_type commentable_id) + add_index :comments, :author_id + + add_index :crop_companions, %i(crop_a_id crop_b_id) + + add_index :crops, :creator_id + add_index :crops, :parent_id + + add_index :follows, %i(follower_id followed_id) + + add_index :forums, :owner_id + + add_index :harvests, :crop_id + add_index :harvests, :owner_id + add_index :harvests, :plant_part_id + + add_index :members_roles, %i(member_id role_id) + + add_index :notifications, :sender_id + add_index :notifications, :recipient_id + + add_index :orders_products, %i(order_id product_id) + + add_index :photo_associations, :crop_id # TODO: Is this still in use? + + add_index :photos, :owner_id + add_index :photos, :source_id + + add_index :photos_plantings, %i(photo_id planting_id) + + add_index :plant_parts, :slug, unique: true + + add_index :plantings, :crop_id + add_index :plantings, :garden_id + add_index :plantings, :owner_id + add_index :plantings, :parent_seed_id + + add_index :posts, :forum_id + + add_index :scientific_names, :crop_id + add_index :scientific_names, :creator_id + + add_index :seeds, :owner_id + add_index :seeds, :crop_id + add_index :seeds, :parent_planting_id + end +end diff --git a/db/schema.rb b/db/schema.rb index f96175d6e..6c279b9a9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do +ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -68,6 +68,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.string "language" + t.index ["creator_id"], name: "index_alternate_names_on_creator_id" + t.index ["crop_id"], name: "index_alternate_names_on_crop_id" + t.index ["language"], name: "index_alternate_names_on_language" end create_table "authentications", id: :serial, force: :cascade do |t| @@ -209,6 +212,8 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.string "commentable_type" + t.index ["author_id"], name: "index_comments_on_author_id" + t.index ["commentable_type", "commentable_id"], name: "index_comments_on_commentable_type_and_commentable_id" end create_table "crop_companions", force: :cascade do |t| @@ -216,6 +221,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.integer "crop_b_id", null: false t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false + t.index ["crop_a_id", "crop_b_id"], name: "index_crop_companions_on_crop_a_id_and_crop_b_id" end create_table "crop_posts", id: false, force: :cascade do |t| @@ -246,7 +252,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.jsonb "openfarm_data" t.integer "harvests_count", default: 0 t.integer "photo_associations_count", default: 0 + t.index ["creator_id"], name: "index_crops_on_creator_id" t.index ["name"], name: "index_crops_on_name" + t.index ["parent_id"], name: "index_crops_on_parent_id" t.index ["requester_id"], name: "index_crops_on_requester_id" t.index ["slug"], name: "index_crops_on_slug", unique: true end @@ -256,6 +264,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.integer "followed_id" t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil + t.index ["follower_id", "followed_id"], name: "index_follows_on_follower_id_and_followed_id" end create_table "forums", id: :serial, force: :cascade do |t| @@ -265,6 +274,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.string "slug" + t.index ["owner_id"], name: "index_forums_on_owner_id" t.index ["slug"], name: "index_forums_on_slug", unique: true end @@ -328,6 +338,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.float "si_weight" t.integer "planting_id" t.integer "likes_count", default: 0 + t.index ["crop_id"], name: "index_harvests_on_crop_id" + t.index ["owner_id"], name: "index_harvests_on_owner_id" + t.index ["plant_part_id"], name: "index_harvests_on_plant_part_id" t.index ["planting_id"], name: "index_harvests_on_planting_id" end @@ -464,6 +477,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do create_table "members_roles", id: false, force: :cascade do |t| t.integer "member_id" t.integer "role_id" + t.index ["member_id", "role_id"], name: "index_members_roles_on_member_id_and_role_id" end create_table "notifications", id: :serial, force: :cascade do |t| @@ -477,11 +491,14 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.datetime "updated_at", precision: nil t.string "notifiable_type" t.index ["notifiable_type", "notifiable_id"], name: "index_notifications_on_notifiable_type_and_notifiable_id" + t.index ["recipient_id"], name: "index_notifications_on_recipient_id" + t.index ["sender_id"], name: "index_notifications_on_sender_id" end create_table "orders_products", id: false, force: :cascade do |t| t.integer "order_id" t.integer "product_id" + t.index ["order_id", "product_id"], name: "index_orders_products_on_order_id_and_product_id" end create_table "photo_associations", id: :serial, force: :cascade do |t| @@ -491,6 +508,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false t.integer "crop_id" + t.index ["crop_id"], name: "index_photo_associations_on_crop_id" t.index ["photographable_id", "photographable_type", "photo_id"], name: "items_to_photos_idx", unique: true t.index ["photographable_id", "photographable_type"], name: "photographable_idx" end @@ -511,12 +529,15 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.string "source" t.integer "comments_count", default: 0 t.index ["fullsize_url"], name: "index_photos_on_fullsize_url", unique: true + t.index ["owner_id"], name: "index_photos_on_owner_id" + t.index ["source_id"], name: "index_photos_on_source_id" t.index ["thumbnail_url"], name: "index_photos_on_thumbnail_url", unique: true end create_table "photos_plantings", id: false, force: :cascade do |t| t.integer "photo_id" t.integer "planting_id" + t.index ["photo_id", "planting_id"], name: "index_photos_plantings_on_photo_id_and_planting_id" end create_table "photos_seeds", id: false, force: :cascade do |t| @@ -531,6 +552,17 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.datetime "updated_at", precision: nil t.string "slug" t.integer "harvests_count", default: 0 + t.index ["slug"], name: "index_plant_parts_on_slug", unique: true + end + + create_table "planting_problems", force: :cascade do |t| + t.bigint "planting_id" + t.bigint "problem_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["planting_id", "problem_id"], name: "index_planting_problems_on_planting_id_and_problem_id", unique: true + t.index ["planting_id"], name: "index_planting_problems_on_planting_id" + t.index ["problem_id"], name: "index_planting_problems_on_problem_id" end create_table "plantings", id: :serial, force: :cascade do |t| @@ -554,6 +586,10 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.integer "harvests_count", default: 0 t.integer "likes_count", default: 0 t.boolean "failed", default: false, null: false + t.index ["crop_id"], name: "index_plantings_on_crop_id" + t.index ["garden_id"], name: "index_plantings_on_garden_id" + t.index ["owner_id"], name: "index_plantings_on_owner_id" + t.index ["parent_seed_id"], name: "index_plantings_on_parent_seed_id" t.index ["slug"], name: "index_plantings_on_slug", unique: true end @@ -568,9 +604,36 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.integer "likes_count", default: 0 t.integer "comments_count", default: 0 t.index ["created_at", "author_id"], name: "index_posts_on_created_at_and_author_id" + t.index ["forum_id"], name: "index_posts_on_forum_id" t.index ["slug"], name: "index_posts_on_slug", unique: true end + create_table "problem_posts", force: :cascade do |t| + t.bigint "problem_id" + t.bigint "post_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["post_id"], name: "index_problem_posts_on_post_id" + t.index ["problem_id", "post_id"], name: "index_problem_posts_on_problem_id_and_post_id", unique: true + t.index ["problem_id"], name: "index_problem_posts_on_problem_id" + end + + create_table "problems", force: :cascade do |t| + t.string "name" + t.string "reason_for_rejection" + t.string "rejection_notes" + t.string "approval_status", default: "pending", null: false + t.bigint "requester_id" + t.bigint "creator_id" + t.string "slug" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["creator_id"], name: "index_problems_on_creator_id" + t.index ["name"], name: "index_problems_on_name" + t.index ["requester_id"], name: "index_problems_on_requester_id" + t.index ["slug"], name: "index_problems_on_slug" + end + create_table "roles", id: :serial, force: :cascade do |t| t.string "name", null: false t.text "description" @@ -590,6 +653,8 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.string "gbif_rank" t.string "gbif_status" t.string "wikidata_id" + t.index ["creator_id"], name: "index_scientific_names_on_creator_id" + t.index ["crop_id"], name: "index_scientific_names_on_crop_id" end create_table "seeds", id: :serial, force: :cascade do |t| @@ -611,7 +676,12 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do t.date "finished_at" t.integer "parent_planting_id" t.date "saved_at" + t.string "source" + t.index ["crop_id"], name: "index_seeds_on_crop_id" + t.index ["owner_id"], name: "index_seeds_on_owner_id" + t.index ["parent_planting_id"], name: "index_seeds_on_parent_planting_id" t.index ["slug"], name: "index_seeds_on_slug", unique: true + t.index ["source"], name: "index_seeds_on_source" end add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" @@ -622,6 +692,12 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do add_foreign_key "mailboxer_receipts", "mailboxer_notifications", column: "notification_id", name: "receipts_on_notification_id" add_foreign_key "photo_associations", "crops" add_foreign_key "photo_associations", "photos" + add_foreign_key "planting_problems", "plantings" + add_foreign_key "planting_problems", "problems" add_foreign_key "plantings", "seeds", column: "parent_seed_id", name: "parent_seed", on_delete: :nullify + add_foreign_key "problem_posts", "posts" + add_foreign_key "problem_posts", "problems" + add_foreign_key "problems", "members", column: "creator_id" + add_foreign_key "problems", "members", column: "requester_id" add_foreign_key "seeds", "plantings", column: "parent_planting_id", name: "parent_planting", on_delete: :nullify end From d185ce495f6c3cd2aa7c5a6911c8ff0456eefc69 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Mon, 1 Sep 2025 12:13:08 +0000 Subject: [PATCH 06/27] Remove haml-lint-extractor --- Gemfile | 1 - Gemfile.lock | 8 -------- 2 files changed, 9 deletions(-) diff --git a/Gemfile b/Gemfile index c7259f926..bd7ef2466 100644 --- a/Gemfile +++ b/Gemfile @@ -178,7 +178,6 @@ group :development, :test do gem 'dotenv-rails' # cli utils - gem 'haml-i18n-extractor', require: false gem 'haml_lint', '>= 0.25.1', require: false # Checks haml files for goodness gem 'i18n-tasks', require: false # adds tests for finding missing and unused translations gem 'rspectre', require: false # finds unused code in specs diff --git a/Gemfile.lock b/Gemfile.lock index f25fd5699..2509c7f34 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -294,12 +294,6 @@ GEM temple (>= 0.8.2) thor tilt - haml-i18n-extractor (0.5.9) - activesupport - haml - highline - tilt - trollop (= 1.16.2) haml-rails (2.1.0) actionpack (>= 5.1) activesupport (>= 5.1) @@ -701,7 +695,6 @@ GEM tilt (2.6.1) timecop (0.9.10) timeout (0.4.3) - trollop (1.16.2) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (3.1.5) @@ -788,7 +781,6 @@ DEPENDENCIES gibbon (~> 1.2.0) gravatar-ultimate haml - haml-i18n-extractor haml-rails haml_lint (>= 0.25.1) hashie (>= 3.5.3) From 0f4803392d7b0ea09ed1428c6bcc35e39da1e661 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 21:47:31 +0930 Subject: [PATCH 07/27] Add seed source to Seed model (#4186) * Add seed source to Seed model * Update _form.html.haml * Add to schema * Default option * Default option * Fix test --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: Daniel O'Connor --- app/controllers/seeds_controller.rb | 4 +++- app/models/seed.rb | 5 +++++ app/views/seeds/_form.html.haml | 16 +++++++++------- db/migrate/20250901105232_add_source_to_seeds.rb | 6 ++++++ spec/features/seeds/misc_seeds_spec.rb | 1 + 5 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 db/migrate/20250901105232_add_source_to_seeds.rb diff --git a/app/controllers/seeds_controller.rb b/app/controllers/seeds_controller.rb index 6a5b33517..6dd2741cd 100644 --- a/app/controllers/seeds_controller.rb +++ b/app/controllers/seeds_controller.rb @@ -43,6 +43,7 @@ class SeedsController < DataController def new @seed = Seed.new + @seed.source = 'my own seed saving' if params[:planting_slug] @planting = Planting.find_by(slug: params[:planting_slug]) @@ -56,6 +57,7 @@ class SeedsController < DataController def create @seed = Seed.new(seed_params) + @seed.source ||= 'my own seed saving' @seed.finished ||= false @seed.owner = current_member @seed.crop = @seed.parent_planting.crop if @seed.parent_planting @@ -84,7 +86,7 @@ class SeedsController < DataController :crop_id, :description, :quantity, :plant_before, :parent_planting_id, :saved_at, :days_until_maturity_min, :days_until_maturity_max, - :organic, :gmo, + :organic, :gmo, :source, :heirloom, :tradable_to, :slug, :finished, :finished_at ) diff --git a/app/models/seed.rb b/app/models/seed.rb index 92050be57..e99429a44 100644 --- a/app/models/seed.rb +++ b/app/models/seed.rb @@ -12,6 +12,8 @@ class Seed < ApplicationRecord ORGANIC_VALUES = ['certified organic', 'non-certified organic', 'conventional/non-organic', 'unknown'].freeze GMO_VALUES = ['certified GMO-free', 'non-certified GMO-free', 'GMO', 'unknown'].freeze HEIRLOOM_VALUES = %w(heirloom hybrid unknown).freeze + SOURCE_VALUES = ['seed catalogue', 'retail outlet', 'seed bank or similar institution', + 'traded from another person', 'my own seed saving', 'other/unknown'].freeze # # Relationships @@ -44,6 +46,9 @@ class Seed < ApplicationRecord validates :heirloom, allow_blank: false, inclusion: { in: HEIRLOOM_VALUES, message: "You must say whether the seeds" \ "are heirloom, hybrid, or unknown" } + validates :source, allow_blank: true, + inclusion: { in: SOURCE_VALUES, message: "You must say where the seeds are from," \ + "or that you don't know" } # # Delegations diff --git a/app/views/seeds/_form.html.haml b/app/views/seeds/_form.html.haml index 02b0f4358..f423c6507 100644 --- a/app/views/seeds/_form.html.haml +++ b/app/views/seeds/_form.html.haml @@ -49,17 +49,19 @@ .col-md-6= f.number_field :days_until_maturity_max, label_as_placeholder: true, label: 'max', prepend: 'to', append: "days", min: 1 .row - .col-md-4 - = f.select(:organic, Seed::ORGANIC_VALUES, {label: 'Organic?', wrapper: { class: 'required'}, required: true}, default: 'unknown') - .col-md-4 - = f.select(:gmo, Seed::GMO_VALUES, {label: 'GMO?', wrapper: { class: 'required'}, required: true}, default: 'unknown') - .col-md-4 - = f.select(:heirloom, Seed::HEIRLOOM_VALUES, {label: 'Heirloom?', wrapper: { class: 'required'}, required: true}, default: 'unknown') + .col-md-3 + = f.select(:organic, Seed::ORGANIC_VALUES, { label: 'Organic?', wrapper: { class: 'required' }, required: true }, default: 'unknown') + .col-md-3 + = f.select(:gmo, Seed::GMO_VALUES, { label: 'GMO?', wrapper: { class: 'required' }, required: true }, default: 'unknown') + .col-md-3 + = f.select(:heirloom, Seed::HEIRLOOM_VALUES, { label: 'Heirloom?', wrapper: { class: 'required' }, required: true }, default: 'unknown') + .col-md-3 + = f.select(:source, Seed::SOURCE_VALUES, { label: 'Source?', wrapper: { class: 'required' }, required: true }, default: 'unknown') = f.text_area :description, rows: 6 %hr/ = t('.trade_help', site_name: ENV['GROWSTUFF_SITE_NAME']) - = f.select(:tradable_to, Seed::TRADABLE_TO_VALUES, {label: 'Will trade', wrapper: { class: 'required'}, required: true}) + = f.select(:tradable_to, Seed::TRADABLE_TO_VALUES, { label: 'Will trade', wrapper: { class: 'required' }, required: true }) %span.help_inline - if current_member.location.blank? Don't forget to diff --git a/db/migrate/20250901105232_add_source_to_seeds.rb b/db/migrate/20250901105232_add_source_to_seeds.rb new file mode 100644 index 000000000..2fca4b203 --- /dev/null +++ b/db/migrate/20250901105232_add_source_to_seeds.rb @@ -0,0 +1,6 @@ +class AddSourceToSeeds < ActiveRecord::Migration[7.2] + def change + add_column :seeds, :source, :string + add_index :seeds, :source + end +end diff --git a/spec/features/seeds/misc_seeds_spec.rb b/spec/features/seeds/misc_seeds_spec.rb index 24a8b3a78..4b52ac539 100644 --- a/spec/features/seeds/misc_seeds_spec.rb +++ b/spec/features/seeds/misc_seeds_spec.rb @@ -49,6 +49,7 @@ describe "seeds", :js do click_link 'Edit' expect(page).to have_current_path edit_seed_path(seed), ignore_query: true fill_in 'Quantity', with: seed.quantity * 2 + select 'traded from another person', from: 'Source' click_button 'Save' expect(page).to have_current_path seed_path(seed), ignore_query: true end From 468e34a551a47ec9457e3888cb704510f855685f Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Mon, 1 Sep 2025 12:56:22 +0000 Subject: [PATCH 08/27] Remove openfarm service --- app/services/openfarm_service.rb | 108 ------------------------------- 1 file changed, 108 deletions(-) delete mode 100644 app/services/openfarm_service.rb diff --git a/app/services/openfarm_service.rb b/app/services/openfarm_service.rb deleted file mode 100644 index 11b3d1e2d..000000000 --- a/app/services/openfarm_service.rb +++ /dev/null @@ -1,108 +0,0 @@ -# frozen_string_literal: true - -BASE = 'https://openfarm.cc/api/v1/' -# BASE = 'http://127.0.0.1:3000/api/v1/' - -class OpenfarmService - def initialize - @cropbot = Member.find_by(login_name: 'cropbot') - end - - def import! - Crop.all.order(updated_at: :desc).each do |crop| - Rails.logger.debug { "#{crop.id}, #{crop.name}" } - update_crop(crop) if crop.valid? - end - end - - def update_crop(crop) - openfarm_record = fetch(crop.name) - if openfarm_record.present? && openfarm_record.is_a?(String) - Rails.logger.info(openfarm_record) - elsif openfarm_record.present? && openfarm_record.fetch('data', false) - crop.update! openfarm_data: openfarm_record.fetch('data', false) - save_companions(crop, openfarm_record) - save_photos(crop) - else - Rails.logger.debug "\tcrop not found on Open Farm" - crop.update!(openfarm_data: false) - end - end - - def save_companions(crop, openfarm_record) - companions = openfarm_record.fetch('data').fetch('relationships').fetch('companions').fetch('data') - crops = openfarm_record.fetch('included', []).select { |rec| rec["type"] == 'crops' } - CropCompanion.transaction do - companions.each do |com| - companion_crop_hash = crops.detect { |c| c.fetch('id') == com.fetch('id') } - companion_crop_name = companion_crop_hash.fetch('attributes').fetch('name').downcase - companion_crop = Crop.where('lower(name) = ?', companion_crop_name).first - companion_crop = Crop.create!(name: companion_crop_name, requester: @cropbot, approval_status: "pending") if companion_crop.nil? - crop.companions << companion_crop unless crop.companions.where(id: companion_crop.id).any? - end - end - end - - def save_photos(crop) - pictures = fetch_pictures(crop.name) - pictures.each do |picture| - data = picture.fetch('attributes') - Rails.logger.debug(data) - next unless data.fetch('image_url').start_with? 'http' - next if Photo.find_by(source_id: picture.fetch('id'), source: 'openfarm') - - photo = Photo.new( - source_id: picture.fetch('id'), - source: 'openfarm', - owner: @cropbot, - thumbnail_url: data.fetch('thumbnail_url'), - fullsize_url: data.fetch('image_url'), - title: 'Open Farm photo', - license_name: 'No rights reserved', - link_url: "https://openfarm.cc/en/crops/#{name_to_slug(crop.name)}" - ) - if photo.valid? - Photo.transaction do - photo.save - PhotoAssociation.find_or_create_by! photo:, photographable: crop - end - Rails.logger.debug { "\t saved photo #{photo.id} #{photo.source_id}" } - else - Rails.logger.warn "Photo not valid" - end - end - end - - def fetch(name) - conn.get("crops/#{name_to_slug(name)}.json").body - rescue NoMethodError - Rails.logger.debug "error fetching crop" - Rails.logger.debug "BODY: " - Rails.logger.debug body - end - - def name_to_slug(name) - CGI.escape(name.gsub(' ', '-').downcase) - end - - def fetch_all(page) - conn.get("crops.json?page=#{page}").body.fetch('data', {}) - end - - def fetch_pictures(name) - body = conn.get("crops/#{name_to_slug(name)}/pictures.json").body - body.fetch('data', false) - rescue StandardError - Rails.logger.debug "Error fetching photos" - Rails.logger.debug [] - end - - private - - def conn - Faraday.new BASE do |conn| - conn.response :json, content_type: /\bjson$/ - conn.adapter Faraday.default_adapter - end - end -end From 70e6c44d822daeda2d03f87eb1822c6b32c3d118 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Mon, 1 Sep 2025 13:20:04 +0000 Subject: [PATCH 09/27] Sign up, sign in don't need JS --- spec/features/signin_spec.rb | 2 +- spec/features/signup_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/signin_spec.rb b/spec/features/signin_spec.rb index e0680b191..f1488ba7b 100644 --- a/spec/features/signin_spec.rb +++ b/spec/features/signin_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe "signin", :js do +describe "signin" do let(:member) { FactoryBot.create(:member) } let(:recipient) { FactoryBot.create(:member) } let(:wrangler) { FactoryBot.create(:crop_wrangling_member) } diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb index 7745370bc..68ba750a4 100644 --- a/spec/features/signup_spec.rb +++ b/spec/features/signup_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe "signup", :js do +describe "signup" do it "sign up for new account from top menubar" do visit crops_path # something other than front page, which has multiple signup links click_link 'Sign up' From c189e3b01a81d870b6e64a30b58a1b36cf274646 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Mon, 1 Sep 2025 23:56:13 +0930 Subject: [PATCH 10/27] Merge pull request #4062 from Growstuff/feature/planting-rating Add overall_rating to Plantings --- app/controllers/plantings_controller.rb | 2 +- app/models/planting.rb | 3 ++ app/views/harvests/_planting.haml | 5 +++ app/views/plantings/_facts.haml | 6 +++ app/views/plantings/_form.html.haml | 9 ++++ app/views/plantings/show.html.haml | 9 ++++ .../20250901110545_add_indexes_crops.rb | 1 - ...0901130830_add_overall_rating_plantings.rb | 5 +++ db/schema.rb | 45 +------------------ .../plantings/planting_a_crop_spec.rb | 2 + 10 files changed, 42 insertions(+), 45 deletions(-) create mode 100644 db/migrate/20250901130830_add_overall_rating_plantings.rb diff --git a/app/controllers/plantings_controller.rb b/app/controllers/plantings_controller.rb index fdf33b709..07a73cbce 100644 --- a/app/controllers/plantings_controller.rb +++ b/app/controllers/plantings_controller.rb @@ -133,7 +133,7 @@ class PlantingsController < DataController :crop_id, :description, :garden_id, :planted_at, :parent_seed_id, :quantity, :sunniness, :planted_from, :finished, - :finished_at, :failed + :finished_at, :failed, :overall_rating ) end diff --git a/app/models/planting.rb b/app/models/planting.rb index 381a739c0..9f05a7732 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -83,6 +83,9 @@ class Planting < ApplicationRecord validates :planted_from, allow_blank: true, inclusion: { in: PLANTED_FROM_VALUES, message: "%s is not a valid planting method" } + validates :overall_rating, allow_blank: true, numericality: { + only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5 + } def planting_slug [ diff --git a/app/views/harvests/_planting.haml b/app/views/harvests/_planting.haml index 5e1eaafe8..cb11a9baa 100644 --- a/app/views/harvests/_planting.haml +++ b/app/views/harvests/_planting.haml @@ -5,3 +5,8 @@ - @matching_plantings.each do |planting| = f.radio_button :planting_id, planting.id, label: planting = f.submit "save", class: 'btn btn-sm' + +- if @harvest.planting.present? && @harvest.planting.overall_rating.blank? + .alert.alert-info{role: "alert"} + This harvest is from a planting that hasn't been rated yet. + = link_to "Rate this planting", edit_planting_path(@harvest.planting), class: 'alert-link' diff --git a/app/views/plantings/_facts.haml b/app/views/plantings/_facts.haml index 5cacccdd0..b4521d5b9 100644 --- a/app/views/plantings/_facts.haml +++ b/app/views/plantings/_facts.haml @@ -89,3 +89,9 @@ - if planting.finished_at.present? %span.plantingfact--finish = planting.finished_at.year + - if planting.overall_rating.present? + .card.fact-card + .card-body + %h5.card-title Overall Rating + %p.card-text + %strong= "#{planting.overall_rating}/5" diff --git a/app/views/plantings/_form.html.haml b/app/views/plantings/_form.html.haml index 0354d484f..f0c40f4e1 100644 --- a/app/views/plantings/_form.html.haml +++ b/app/views/plantings/_form.html.haml @@ -43,6 +43,15 @@ = f.select(:sunniness, Planting::SUNNINESS_VALUES, { include_blank: '', label: 'Sun or shade?' } ) .col-md-4 = f.number_field :quantity, label: 'How many?', min: 1 + .col-md-12 + = f.range_field :overall_rating, min: 1, max: 5, include_blank: 'Leave blank', label: 'Overall Rating', list: "rating-list", title: "How well is the planting going?" + %datalist{"id": "rating-list"} + %option{"value": "1"} Poor + %option{"value": "2"} + %option{"value": "3"} + %option{"value": "4"} + %option{"value": "5"} Great + = f.text_area :description, rows: 6, label: 'Tell us more about it' .row diff --git a/app/views/plantings/show.html.haml b/app/views/plantings/show.html.haml index db5af66c6..9841b8329 100644 --- a/app/views/plantings/show.html.haml +++ b/app/views/plantings/show.html.haml @@ -7,6 +7,15 @@ = tag("meta", property: "og:type", content: "website") = tag("meta", property: "og:url", content: request.original_url) = tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME']) + - if @planting.overall_rating.present? + %script{type: "application/ld+json"} + :plain + { + "@context": "http://schema.org", + "@type": "Rating", + "ratingValue": "#{@planting.overall_rating}", + "bestRating": "5" + } - content_for :breadcrumbs do %li.breadcrumb-item= link_to 'Plantings', plantings_path diff --git a/db/migrate/20250901110545_add_indexes_crops.rb b/db/migrate/20250901110545_add_indexes_crops.rb index 80d6b522d..0a519bb11 100644 --- a/db/migrate/20250901110545_add_indexes_crops.rb +++ b/db/migrate/20250901110545_add_indexes_crops.rb @@ -4,7 +4,6 @@ class AddIndexesCrops < ActiveRecord::Migration[7.2] add_index :alternate_names, :creator_id add_index :alternate_names, :language - add_index :comments, %i(commentable_type commentable_id) add_index :comments, :author_id add_index :crop_companions, %i(crop_a_id crop_b_id) diff --git a/db/migrate/20250901130830_add_overall_rating_plantings.rb b/db/migrate/20250901130830_add_overall_rating_plantings.rb new file mode 100644 index 000000000..92b487327 --- /dev/null +++ b/db/migrate/20250901130830_add_overall_rating_plantings.rb @@ -0,0 +1,5 @@ +class AddOverallRatingPlantings < ActiveRecord::Migration[7.2] + def change + add_column :plantings, :overall_rating, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 6c279b9a9..b09d3956b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) do +ActiveRecord::Schema[7.2].define(version: 2025_09_01_130830) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -555,16 +555,6 @@ ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) do t.index ["slug"], name: "index_plant_parts_on_slug", unique: true end - create_table "planting_problems", force: :cascade do |t| - t.bigint "planting_id" - t.bigint "problem_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["planting_id", "problem_id"], name: "index_planting_problems_on_planting_id_and_problem_id", unique: true - t.index ["planting_id"], name: "index_planting_problems_on_planting_id" - t.index ["problem_id"], name: "index_planting_problems_on_problem_id" - end - create_table "plantings", id: :serial, force: :cascade do |t| t.integer "garden_id", null: false t.integer "crop_id", null: false @@ -586,6 +576,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) do t.integer "harvests_count", default: 0 t.integer "likes_count", default: 0 t.boolean "failed", default: false, null: false + t.integer "overall_rating" t.index ["crop_id"], name: "index_plantings_on_crop_id" t.index ["garden_id"], name: "index_plantings_on_garden_id" t.index ["owner_id"], name: "index_plantings_on_owner_id" @@ -608,32 +599,6 @@ ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) do t.index ["slug"], name: "index_posts_on_slug", unique: true end - create_table "problem_posts", force: :cascade do |t| - t.bigint "problem_id" - t.bigint "post_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["post_id"], name: "index_problem_posts_on_post_id" - t.index ["problem_id", "post_id"], name: "index_problem_posts_on_problem_id_and_post_id", unique: true - t.index ["problem_id"], name: "index_problem_posts_on_problem_id" - end - - create_table "problems", force: :cascade do |t| - t.string "name" - t.string "reason_for_rejection" - t.string "rejection_notes" - t.string "approval_status", default: "pending", null: false - t.bigint "requester_id" - t.bigint "creator_id" - t.string "slug" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["creator_id"], name: "index_problems_on_creator_id" - t.index ["name"], name: "index_problems_on_name" - t.index ["requester_id"], name: "index_problems_on_requester_id" - t.index ["slug"], name: "index_problems_on_slug" - end - create_table "roles", id: :serial, force: :cascade do |t| t.string "name", null: false t.text "description" @@ -692,12 +657,6 @@ ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) do add_foreign_key "mailboxer_receipts", "mailboxer_notifications", column: "notification_id", name: "receipts_on_notification_id" add_foreign_key "photo_associations", "crops" add_foreign_key "photo_associations", "photos" - add_foreign_key "planting_problems", "plantings" - add_foreign_key "planting_problems", "problems" add_foreign_key "plantings", "seeds", column: "parent_seed_id", name: "parent_seed", on_delete: :nullify - add_foreign_key "problem_posts", "posts" - add_foreign_key "problem_posts", "problems" - add_foreign_key "problems", "members", column: "creator_id" - add_foreign_key "problems", "members", column: "requester_id" add_foreign_key "seeds", "plantings", column: "parent_planting_id", name: "parent_planting", on_delete: :nullify end diff --git a/spec/features/plantings/planting_a_crop_spec.rb b/spec/features/plantings/planting_a_crop_spec.rb index 28fc3981d..4aa8a243b 100644 --- a/spec/features/plantings/planting_a_crop_spec.rb +++ b/spec/features/plantings/planting_a_crop_spec.rb @@ -198,6 +198,7 @@ describe "Planting a crop", :js, :search do within "form#new_planting" do fill_in "When?", with: "2014-07-01" check "Mark as finished" + find_by_id('planting_overall_rating').set 4 fill_in "Finished date", with: "2014-08-30" uncheck 'Mark as finished' end @@ -220,6 +221,7 @@ describe "Planting a crop", :js, :search do expect(page).to have_content "planting was successfully created" expect(page).to have_content "Finished" expect(page).to have_content "Aug 2014" + expect(page).to have_content "4/5" # ensure we've indexed in elastic search planting.reindex(refresh: true) From ada567dcab71ea72b3cced980df0991348255862 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Mon, 1 Sep 2025 14:28:24 +0000 Subject: [PATCH 11/27] Remove JS testing from footer --- spec/features/footer_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/footer_spec.rb b/spec/features/footer_spec.rb index 147c68a6b..24960f138 100644 --- a/spec/features/footer_spec.rb +++ b/spec/features/footer_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe "footer", :js do +describe "footer" do before { visit root_path } it "footer is on home page" do From 7106b141d9b846df8ef98c3378e06a6502a3786d Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Tue, 2 Sep 2025 00:02:17 +0930 Subject: [PATCH 12/27] Update _facts.haml --- app/views/plantings/_facts.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/plantings/_facts.haml b/app/views/plantings/_facts.haml index b4521d5b9..004c03ffb 100644 --- a/app/views/plantings/_facts.haml +++ b/app/views/plantings/_facts.haml @@ -92,6 +92,6 @@ - if planting.overall_rating.present? .card.fact-card .card-body - %h5.card-title Overall Rating + %h3 Overall Rating %p.card-text %strong= "#{planting.overall_rating}/5" From 2f290efc5b56213fadb6be67d933b363d5d0a31a Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Tue, 2 Sep 2025 00:03:04 +0930 Subject: [PATCH 13/27] Rename _facts.haml to _facts.html.haml --- app/views/plantings/{_facts.haml => _facts.html.haml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/views/plantings/{_facts.haml => _facts.html.haml} (100%) diff --git a/app/views/plantings/_facts.haml b/app/views/plantings/_facts.html.haml similarity index 100% rename from app/views/plantings/_facts.haml rename to app/views/plantings/_facts.html.haml From 9b9de0614055a8866d0aedbe843c0be1e7476347 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Tue, 2 Sep 2025 00:12:59 +0930 Subject: [PATCH 14/27] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c6aec2da6..f13434473 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,9 @@ encourage participation from people of all backgrounds and skill levels. ## Want to contribute? Don't ask to ask, the best way to get started is to fork the project, start a codespace and get hacking. -Dive on in and submit your PRs. +Dive on in and submit your PRs! + +Vibe Coding is more than okay, just make sure you indicate if you have done so and ensure there are tests. ## Important links From 2b818e9f50023215672b115e11ea2d51195d6405 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Tue, 2 Sep 2025 00:33:12 +0930 Subject: [PATCH 15/27] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c6aec2da6..aa1a3a74b 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ frontend features. We welcome contributions -- see * To set up your development environment, see [Getting started](https://github.com/Growstuff/growstuff/wiki/New-contributor-guide). * You may also be interested in our [API](https://github.com/Growstuff/growstuff/wiki/API). +### For Home Automation enthusiasts + +https://github.com/growstuff/growstuff-homeassistant + ## For designers, writers, researchers, data wranglers, and other contributors There are heaps of ways to get involved and contribute no matter what From ac2d99871109402c4815b7c88147ae455aaa716e Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Tue, 2 Sep 2025 00:36:30 +0930 Subject: [PATCH 16/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa1a3a74b..c88b728f6 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ frontend features. We welcome contributions -- see ### For Home Automation enthusiasts -https://github.com/growstuff/growstuff-homeassistant +https://github.com/Growstuff/homeassistant-growstuff/ ## For designers, writers, researchers, data wranglers, and other contributors From b3ba05d8340162bb8c19e912efba2aa367960d62 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Tue, 2 Sep 2025 02:17:28 +0930 Subject: [PATCH 17/27] Fix crash on adding Flickr photo (#4198) * Update photo.rb * Update photo.rb * Update app/models/photo.rb * Update app/models/photo.rb --- app/models/photo.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/photo.rb b/app/models/photo.rb index 183fadceb..afc65875d 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -46,7 +46,8 @@ class Photo < ApplicationRecord flickr = owner.flickr info = flickr.photos.getInfo(photo_id: source_id) licenses = flickr.photos.licenses.getInfo - license = licenses.find { |l| l.id == info.license } + license = licenses.find { |l| l.id.to_i == info.license.to_i } + Rails.logger.error("Cannot find license: " + [info.license, licenses].inspect) unless license { title: calculate_title(info), license_name: license.name, From 765fab110401299e2f3efa7b1aaec3781a4b4d88 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:30:02 +0000 Subject: [PATCH 18/27] CI: Preserve screenshots as build artifacts Adjust the behaviour of capybara-screenshot / GitHub CI to preserve the screenshots as a build artifact. This change adds a step to the `ci-features.yml` workflow to upload the `tmp/screenshots` directory as a build artifact on failure. This will help with debugging failing feature tests. --- .github/workflows/ci-features.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-features.yml b/.github/workflows/ci-features.yml index 4f0e1ad2f..c2d3cc2ba 100644 --- a/.github/workflows/ci-features.yml +++ b/.github/workflows/ci-features.yml @@ -108,4 +108,11 @@ jobs: run: bundle exec rspec spec/features/photos/ -fd - name: Run rspec (rss/) - run: bundle exec rspec spec/features/rss/ -fd \ No newline at end of file + run: bundle exec rspec spec/features/rss/ -fd + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots \ No newline at end of file From d61227bad0e6dcda3654aa9715c909e6d8df2fe8 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:30:25 +0000 Subject: [PATCH 19/27] CI: Preserve screenshots as build artifacts Adjust the behaviour of capybara-screenshot / GitHub CI to preserve the screenshots as a build artifact. This change adds a step to all `ci-features-*.yml` workflows to upload the `tmp/screenshots` directory as a build artifact on failure. This will help with debugging failing feature tests. --- .github/workflows/ci-features-admin.yml | 7 +++++++ .github/workflows/ci-features-comments.yml | 7 +++++++ .github/workflows/ci-features-conversations.yml | 6 ++++++ .github/workflows/ci-features-crops.yml | 7 +++++++ .github/workflows/ci-features-gardens.yml | 7 +++++++ .github/workflows/ci-features-harvests.yml | 9 ++++++++- .github/workflows/ci-features-home.yml | 9 ++++++++- .github/workflows/ci-features-members.yml | 9 ++++++++- .github/workflows/ci-features-places.yml | 7 +++++++ .github/workflows/ci-features-plantings.yml | 7 +++++++ .github/workflows/ci-features-posts.yml | 7 +++++++ .github/workflows/ci-features-seeds.yml | 7 +++++++ .github/workflows/ci-features-timeline.yml | 9 ++++++++- 13 files changed, 94 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-features-admin.yml b/.github/workflows/ci-features-admin.yml index 870e6942c..ff491a422 100644 --- a/.github/workflows/ci-features-admin.yml +++ b/.github/workflows/ci-features-admin.yml @@ -100,3 +100,10 @@ jobs: - name: Run rspec (admin/) run: bundle exec rspec spec/features/admin/ -fd -t ~@flaky + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots diff --git a/.github/workflows/ci-features-comments.yml b/.github/workflows/ci-features-comments.yml index aa14b2078..398fc30e5 100644 --- a/.github/workflows/ci-features-comments.yml +++ b/.github/workflows/ci-features-comments.yml @@ -100,3 +100,10 @@ jobs: - name: Run rspec (comments/) run: bundle exec rspec spec/features/comments/ -fd -t ~@flaky + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots diff --git a/.github/workflows/ci-features-conversations.yml b/.github/workflows/ci-features-conversations.yml index 1548c4ebb..c1fa33b66 100644 --- a/.github/workflows/ci-features-conversations.yml +++ b/.github/workflows/ci-features-conversations.yml @@ -101,3 +101,9 @@ jobs: - name: Run rspec (conversations/) run: bundle exec rspec spec/features/conversations/ -fd -t ~@flaky + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots diff --git a/.github/workflows/ci-features-crops.yml b/.github/workflows/ci-features-crops.yml index 182cbe9b6..4a999de2a 100644 --- a/.github/workflows/ci-features-crops.yml +++ b/.github/workflows/ci-features-crops.yml @@ -100,3 +100,10 @@ jobs: - name: Run rspec (crops/) run: bundle exec rspec spec/features/crops/ -fd -t ~@flaky + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots diff --git a/.github/workflows/ci-features-gardens.yml b/.github/workflows/ci-features-gardens.yml index cc96d742e..f64a8d36a 100644 --- a/.github/workflows/ci-features-gardens.yml +++ b/.github/workflows/ci-features-gardens.yml @@ -100,3 +100,10 @@ jobs: - name: Run rspec (gardens/) run: bundle exec rspec spec/features/gardens/ -fd -t ~@flaky + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots diff --git a/.github/workflows/ci-features-harvests.yml b/.github/workflows/ci-features-harvests.yml index c3047c533..41a29f859 100644 --- a/.github/workflows/ci-features-harvests.yml +++ b/.github/workflows/ci-features-harvests.yml @@ -99,4 +99,11 @@ jobs: run: bundle exec rails search:reindex - name: Run rspec (harvests/) - run: bundle exec rspec spec/features/harvests/ -fd -t ~@flaky \ No newline at end of file + run: bundle exec rspec spec/features/harvests/ -fd -t ~@flaky + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots \ No newline at end of file diff --git a/.github/workflows/ci-features-home.yml b/.github/workflows/ci-features-home.yml index 43c72a922..1676ca10e 100644 --- a/.github/workflows/ci-features-home.yml +++ b/.github/workflows/ci-features-home.yml @@ -99,4 +99,11 @@ jobs: run: bundle exec rails search:reindex - name: Run rspec (home/) - run: bundle exec rspec spec/features/home/ -fd -t ~@flaky \ No newline at end of file + run: bundle exec rspec spec/features/home/ -fd -t ~@flaky + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots \ No newline at end of file diff --git a/.github/workflows/ci-features-members.yml b/.github/workflows/ci-features-members.yml index c542c93e9..8bce06f19 100644 --- a/.github/workflows/ci-features-members.yml +++ b/.github/workflows/ci-features-members.yml @@ -99,4 +99,11 @@ jobs: run: bundle exec rails search:reindex - name: Run rspec (members/) - run: bundle exec rspec spec/features/members/ -fd -t ~@flaky \ No newline at end of file + run: bundle exec rspec spec/features/members/ -fd -t ~@flaky + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots \ No newline at end of file diff --git a/.github/workflows/ci-features-places.yml b/.github/workflows/ci-features-places.yml index 6a4cd4256..ebb175946 100644 --- a/.github/workflows/ci-features-places.yml +++ b/.github/workflows/ci-features-places.yml @@ -100,3 +100,10 @@ jobs: - name: Run rspec (places/) run: bundle exec rspec spec/features/places/ -fd + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots diff --git a/.github/workflows/ci-features-plantings.yml b/.github/workflows/ci-features-plantings.yml index 6a7e50146..fdfc7b0b9 100644 --- a/.github/workflows/ci-features-plantings.yml +++ b/.github/workflows/ci-features-plantings.yml @@ -100,3 +100,10 @@ jobs: - name: Run rspec (plantings/) run: bundle exec rspec spec/features/plantings/ -fd + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots diff --git a/.github/workflows/ci-features-posts.yml b/.github/workflows/ci-features-posts.yml index 1353c891f..9a7b7e78b 100644 --- a/.github/workflows/ci-features-posts.yml +++ b/.github/workflows/ci-features-posts.yml @@ -100,3 +100,10 @@ jobs: - name: Run rspec (posts/) run: bundle exec rspec spec/features/posts/ -fd + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots diff --git a/.github/workflows/ci-features-seeds.yml b/.github/workflows/ci-features-seeds.yml index 41bbecbf2..d38390c95 100644 --- a/.github/workflows/ci-features-seeds.yml +++ b/.github/workflows/ci-features-seeds.yml @@ -100,3 +100,10 @@ jobs: - name: Run rspec (seeds/) run: bundle exec rspec spec/features/seeds/ -fd -t ~@flaky + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots diff --git a/.github/workflows/ci-features-timeline.yml b/.github/workflows/ci-features-timeline.yml index 3560dbd81..a5c8c830b 100644 --- a/.github/workflows/ci-features-timeline.yml +++ b/.github/workflows/ci-features-timeline.yml @@ -99,4 +99,11 @@ jobs: run: bundle exec rails search:reindex - name: Run rspec (timeline/) - run: bundle exec rspec spec/features/timeline/ -fd -t ~@flaky \ No newline at end of file + run: bundle exec rspec spec/features/timeline/ -fd -t ~@flaky + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: tmp/screenshots \ No newline at end of file From dc1b46c7becbeb1655e8e7d54d6004392a17013a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 05:55:09 +0000 Subject: [PATCH 20/27] Bump rubocop-rspec from 3.6.0 to 3.7.0 Bumps [rubocop-rspec](https://github.com/rubocop/rubocop-rspec) from 3.6.0 to 3.7.0. - [Release notes](https://github.com/rubocop/rubocop-rspec/releases) - [Changelog](https://github.com/rubocop/rubocop-rspec/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop-rspec/compare/v3.6.0...v3.7.0) --- updated-dependencies: - dependency-name: rubocop-rspec dependency-version: 3.7.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2509c7f34..02115c812 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -630,7 +630,7 @@ GEM rubocop-rake (0.7.1) lint_roller (~> 1.1) rubocop (>= 1.72.1) - rubocop-rspec (3.6.0) + rubocop-rspec (3.7.0) lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) rubocop-rspec_rails (2.31.0) From 1dc587d4b5fcbc12cfe87e42814b7b6a7ebb9849 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 00:26:26 +0000 Subject: [PATCH 21/27] Bump pg from 1.6.1 to 1.6.2 Bumps [pg](https://github.com/ged/ruby-pg) from 1.6.1 to 1.6.2. - [Changelog](https://github.com/ged/ruby-pg/blob/master/CHANGELOG.md) - [Commits](https://github.com/ged/ruby-pg/compare/v1.6.1...v1.6.2) --- updated-dependencies: - dependency-name: pg dependency-version: 1.6.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2509c7f34..5ce117c55 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -457,8 +457,8 @@ GEM racc percy-capybara (5.0.0) capybara (>= 3) - pg (1.6.1) - pg (1.6.1-x86_64-linux) + pg (1.6.2) + pg (1.6.2-x86_64-linux) platform-api (3.8.0) heroics (~> 0.1.1) moneta (~> 1.0.0) From b0b759ef60154896979f53499f818564a086935c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 07:59:48 +0000 Subject: [PATCH 22/27] Bump puma from 6.6.1 to 7.0.0 Bumps [puma](https://github.com/puma/puma) from 6.6.1 to 7.0.0. - [Release notes](https://github.com/puma/puma/releases) - [Changelog](https://github.com/puma/puma/blob/master/History.md) - [Commits](https://github.com/puma/puma/compare/v6.6.1...v7.0.0) --- updated-dependencies: - dependency-name: puma dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 544c6c183..b15f5835d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -475,7 +475,7 @@ GEM date stringio public_suffix (6.0.1) - puma (6.6.1) + puma (7.0.0) nio4r (~> 2.0) query_diet (0.7.2) racc (1.8.1) From 30f799c4b9a941d669068ff941417ef571e31002 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 08:10:40 +0000 Subject: [PATCH 23/27] Bump rubocop from 1.80.1 to 1.80.2 Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.80.1 to 1.80.2. - [Release notes](https://github.com/rubocop/rubocop/releases) - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop/compare/v1.80.1...v1.80.2) --- updated-dependencies: - dependency-name: rubocop dependency-version: 1.80.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index b15f5835d..6247f9810 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -601,7 +601,7 @@ GEM rswag-ui (2.16.0) actionpack (>= 5.2, < 8.1) railties (>= 5.2, < 8.1) - rubocop (1.80.1) + rubocop (1.80.2) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) From f650d1b8fa1eed651ca65dde0601bcfb2af045a3 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Sun, 7 Sep 2025 03:54:20 +0000 Subject: [PATCH 24/27] Change garden sort order to be by name and planting by most to lease recent when creating an activity --- app/views/activities/_form.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 879117f8b..8ff9fae2e 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -27,13 +27,13 @@ .row .col-md-4 - = f.collection_radio_buttons(:garden_id, @activity.owner.gardens.active, + = f.collection_radio_buttons(:garden_id, @activity.owner.gardens.active.order_by_name, :id, :name, label: 'Is this for a specific garden?') = link_to "Add a garden.", new_garden_path .col-md-4 - = f.collection_radio_buttons(:planting_id, @activity.owner.plantings.active, + = f.collection_radio_buttons(:planting_id, @activity.owner.plantings.active.recent, :id, :crop_name, label: 'Is this for a specific planting?') = link_to "Add a planting.", new_planting_path From 84da4c0f4fe2a85b49c23a03ee38be115307c2c1 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Sun, 7 Sep 2025 04:24:22 +0000 Subject: [PATCH 25/27] Fix styling of cards to space evenly --- app/assets/stylesheets/overrides.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/overrides.scss b/app/assets/stylesheets/overrides.scss index 670269aa8..c21aad2df 100755 --- a/app/assets/stylesheets/overrides.scss +++ b/app/assets/stylesheets/overrides.scss @@ -132,6 +132,8 @@ section { border-radius: 5%; margin: 0.5em 0.5em 0.5em 0; width: 200px; + align-items: stretch; + justify-content: space-between; .img-card { border-top-left-radius: 5%; From d8b84e611b8d18755b62abbc86cfa40ae9d55f72 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Sun, 7 Sep 2025 14:37:18 +0930 Subject: [PATCH 26/27] Update ci-features-posts.yml --- .github/workflows/ci-features-posts.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-features-posts.yml b/.github/workflows/ci-features-posts.yml index 9a7b7e78b..9b973163a 100644 --- a/.github/workflows/ci-features-posts.yml +++ b/.github/workflows/ci-features-posts.yml @@ -1,4 +1,4 @@ -name: CI Features - Admin +name: CI Features - Posts on: [pull_request] From 9b1699b06121b3b3c1332bff37cce6734cd519d6 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Sun, 7 Sep 2025 14:39:13 +0930 Subject: [PATCH 27/27] Merge pull request #4215 from Growstuff/activities-detail Surface more Activities detail --- app/helpers/application_helper.rb | 22 ++++++++++++++++++ app/models/concerns/search_activities.rb | 6 ++++- app/views/activities/_card.html.haml | 29 +++++++++++++++--------- app/views/crops/_harvests.html.haml | 2 +- app/views/harvests/show.html.haml | 2 +- 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 98fb076be..526beadc1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -21,6 +21,28 @@ module ApplicationHelper classes end + # Similar to Rails' time_ago_in_words, but gives a more standard + # output like "in 3 days" or "5 months ago". + # Also handles the case where from_time is a Date and to_time is a Date + # (in which case it just says "today" if they're the same date). + # + # NOTE: This is similar to distance_of_time_in_words but different enough + # that I think it's worth having a separate helper for it. + # + # from_time - the starting time (Time or Date) + # to_time - the ending time (Time or Date). Default: now (Time.zone.now) + # include_seconds - whether to include seconds in the calculation + # + # Returns a string like "in 3 days" or "5 months ago" + def standard_time_distance(from_time, to_time = 0, include_seconds = false) + return 'today' if from_time.is_a?(Date) && (from_time == to_time) + + return 'now' if from_time == to_time + return distance_of_time_in_words(from_time, to_time, include_seconds:) + ' ago' if from_time > to_time + + 'in ' + distance_of_time_in_words(from_time, to_time, include_seconds:) + end + def count_github_contibutors File.open(Rails.root.join('CONTRIBUTORS.md')).readlines.grep(/^-/).size end diff --git a/app/models/concerns/search_activities.rb b/app/models/concerns/search_activities.rb index 458d0921c..8190b4a7c 100644 --- a/app/models/concerns/search_activities.rb +++ b/app/models/concerns/search_activities.rb @@ -9,7 +9,9 @@ module SearchActivities mappings: { properties: { active: { type: :boolean }, - created_at: { type: :integer } + created_at: { type: :integer }, + updated_at: { type: :integer }, + due_date: { type: :date } } } @@ -23,8 +25,10 @@ module SearchActivities category:, garden_id:, garden_name: garden&.name, + garden_slug: garden&.garden_slug, planting_id:, planting_name: planting&.crop&.name, + planting_slug: planting&.slug, description:, # owner diff --git a/app/views/activities/_card.html.haml b/app/views/activities/_card.html.haml index eafef62f4..ca0a17909 100644 --- a/app/views/activities/_card.html.haml +++ b/app/views/activities/_card.html.haml @@ -20,16 +20,23 @@ - if can? :destroy, activity .dropdown-divider = delete_button(activity, classes: 'dropdown-item text-danger') - = link_to activity_path(slug: activity.slug) do - .card-body.text-center + .card-body + = link_to activity_path(slug: activity.slug) do %h4= activity.name - .text-center= activity.description - - if activity.garden - .text-center= activity.garden - - if activity.planting - .text-center= activity.planting + %small.due-date{title: activity.due_date} + = standard_time_distance(activity.due_date.to_date, Time.zone.now.to_date) + %div + %small.text-justify{title: activity.description}= activity.description.truncate(150) + %p + %ul.list-unstyled + - if activity.garden_name && activity.garden_slug + %li + %small= link_to activity.garden_name, garden_path(slug: activity.garden_slug) + - if activity.planting_name && activity.planting_slug + %li + %small= link_to activity.planting_name, planting_path(slug: activity.planting_slug) + .card-footer - .float-right - %span.chip.member-chip - = link_to member_path(slug: activity.owner_slug) do - = activity.owner_login_name + %small.chip.member-chip + = link_to member_path(slug: activity.owner_slug) do + = activity.owner_login_name diff --git a/app/views/crops/_harvests.html.haml b/app/views/crops/_harvests.html.haml index f2755bbc6..47f43d57a 100644 --- a/app/views/crops/_harvests.html.haml +++ b/app/views/crops/_harvests.html.haml @@ -12,7 +12,7 @@ #{harvest.owner} harvested #{display_quantity(harvest)}. .float-right= render 'members/location', member: harvest.owner .harvest-timeago - %small #{distance_of_time_in_words(harvest.harvested_at, Time.zone.now)} ago. + %small #{standard_time_distance(harvest.harvested_at, Time.zone.now.to_date)} %li.list-group-item= link_to "View all #{crop.name} harvests", crop_harvests_path(crop), class: 'card-link' - if crop.approved? - if current_member diff --git a/app/views/harvests/show.html.haml b/app/views/harvests/show.html.haml index 88d55664a..37404368f 100644 --- a/app/views/harvests/show.html.haml +++ b/app/views/harvests/show.html.haml @@ -46,7 +46,7 @@ %h3 Harvested = editable :date, @harvest, :harvested_at, display_field: '.harvested_at' - %strong.harvested_at #{distance_of_time_in_words @harvest.harvested_at, Time.zone.now.to_date} ago + %strong.harvested_at #{standard_time_distance @harvest.harvested_at, Time.zone.now.to_date} %span.harvested_at= I18n.l @harvest.harvested_at .card{class: @harvest.quantity.present? ? '' : 'text-muted'}