diff --git a/app/controllers/job_ideas_controller.rb b/app/controllers/job_ideas_controller.rb index a39cf20a0..2901ef781 100644 --- a/app/controllers/job_ideas_controller.rb +++ b/app/controllers/job_ideas_controller.rb @@ -27,7 +27,7 @@ class JobIdeasController < ApplicationController key = params.require(:key) length = params[:length].to_s snoozed = JobIdeaDismissal::SNOOZES.key?(length) - snoozed ? JobIdeaDismissal.snooze!(current_member, key, length) : JobIdeaDismissal.dismiss!(current_member, key) + snoozed ? JobIdeaDismissal.snooze!(current_member, key, length) : JobIdeaDismissal.hide!(current_member, key) changed([], t("job_ideas.hidden.#{snoozed ? length : 'dismissed'}")) end @@ -57,17 +57,23 @@ class JobIdeasController < ApplicationController def changed(added, notice) return redirect_to(job_ideas_path(skip: skipped), notice:) unless request.xhr? + JobIdeas.forget_count(current_member) @job_ideas = nil load_ideas render json: overlay_json(added) end + # The member's jobs, as they now are, to show those that were added. + def jobs_groups + groups = JobsNotepad.new(current_member).groups + render_to_string(partial: 'jobs/notepad_groups', formats: [:html], locals: { groups:, in_garden: false }) + end + def overlay_json(added) - notepad = JobsNotepad.new(current_member) count = @ideas.size + @more { list: render_to_string(partial: 'job_ideas/list', formats: [:html]), - groups: render_to_string(partial: 'jobs/notepad_groups', formats: [:html], locals: { groups: notepad.groups, in_garden: false }), + groups: (jobs_groups if added.any?), count:, banner: t('job_ideas.banner', count:), added_label: t('job_ideas.added_to_jobs', count: added.size), added: added.map { |job| { id: job.id, name: job.name, due: (l(job.due_date, format: '%-d %b') if job.due_date) } } } diff --git a/app/helpers/job_ideas_helper.rb b/app/helpers/job_ideas_helper.rb index 1935f8179..a03213829 100644 --- a/app/helpers/job_ideas_helper.rb +++ b/app/helpers/job_ideas_helper.rb @@ -1,15 +1,6 @@ # frozen_string_literal: true module JobIdeasHelper - # How many ideas a member has, for the count beside the link to them. It looks - # at other people's jobs, so it is kept for a few minutes, and changes at once - # when they hide an idea. - def job_ideas_count(member) - hidden = member.job_idea_dismissals.maximum(:updated_at) - changed = member.jobs.maximum(:updated_at) - Rails.cache.fetch(['job_ideas_count', member.id, hidden, changed], expires_in: 10.minutes) { JobIdeas.new(member).ideas.size } - end - # What an idea would add, as the member would read it. def job_idea_title(idea) case idea.kind @@ -23,7 +14,7 @@ module JobIdeasHelper def job_idea_reason(idea) day = idea.on && l(idea.on, format: '%-d %b') case idea.kind - when :template then t('job_ideas.reason.template', count: idea.job_count, steps: job_idea_steps(idea), crop: idea.crop.name, day:) + when :template then t('job_ideas.reason.template', count: idea.schedule.size, steps: job_idea_steps(idea), crop: idea.crop.name, day:) when :past_job, :past_planting then t("job_ideas.reason.#{idea.kind}", count: idea.years, day:) when :harvest then t("job_ideas.reason.harvest_#{idea.period}", day:) else community_reason(idea, day) diff --git a/app/javascript/site/job_ideas.js b/app/javascript/site/job_ideas.js index 5d971ffe9..7ead41cec 100644 --- a/app/javascript/site/job_ideas.js +++ b/app/javascript/site/job_ideas.js @@ -84,7 +84,7 @@ document.addEventListener('DOMContentLoaded', function () { // arrive. const showJobs = function (data) { const groups = document.getElementById('notepad-groups'); - if (!groups) return; + if (!groups || !data.groups) return; const added = new Set(data.added.map(function (job) { return String(job.id); })); groups.innerHTML = data.groups; groups.querySelectorAll('.notepad-job').forEach(function (row) { @@ -92,25 +92,39 @@ document.addEventListener('DOMContentLoaded', function () { }); }; - const choose = function (form) { - const row = form.closest('.idea'); - if (row) row.classList.add('idea-working'); + // Sends the choice. Rejects only if it could not be sent, or the answer was no + // good: then it is safe to send it again the ordinary way. + const send = function (form) { return request(form.action, {method: 'POST', body: new FormData(form), headers: {Accept: 'application/json'}}) .then(ok) - .then(function (response) { return response.json(); }) - .then(function (data) { + .then(function (response) { return response.json(); }); + }; + + // Shows what the server did. If that goes wrong the choice was made all the + // same, so the page is loaded again to show how things stand. + const show = function (form, data) { + const row = form.closest('.idea'); + return Promise.resolve() + .then(function () { showCount(data); showJobs(data); if (data.added.length && row) { row.replaceChildren(addedBlock(data)); row.classList.remove('idea-working'); row.classList.add('idea-confirmed'); - return wait(Math.min(2600, 1100 + data.added.length * 140)).then(function () { return data; }); + return wait(Math.min(2600, 1100 + data.added.length * 140)); } if (row) row.classList.add('idea-leaving'); - return wait(350).then(function () { return data; }); + return wait(350); }) - .then(function (data) { showList(data.list); }); + .then(function () { showList(data.list); }) + .catch(function () { window.location.reload(); }); + }; + + const choose = function (form) { + const row = form.closest('.idea'); + if (row) row.classList.add('idea-working'); + return send(form).then(function (data) { return show(form, data); }, function () { form.submit(); }); }; document.querySelectorAll('[data-job-ideas-open]').forEach(function (link) { @@ -132,6 +146,6 @@ document.addEventListener('DOMContentLoaded', function () { const form = event.target.closest('.idea-form'); if (!form) return; event.preventDefault(); - choose(form).catch(function () { form.submit(); }); + choose(form); }); }); diff --git a/app/javascript/site/job_ideas.test.js b/app/javascript/site/job_ideas.test.js index 7afbfb1e5..864c3edf1 100644 --- a/app/javascript/site/job_ideas.test.js +++ b/app/javascript/site/job_ideas.test.js @@ -146,6 +146,12 @@ describe('choosing an idea', () => { expect(document.querySelector('.notepad-ideas').hidden).toBe(true); }); + it('does not send the choice again once the server has answered', async () => { + await choose(); + const form = body().querySelector('.idea-form'); + expect(form === null || form.submit === HTMLFormElement.prototype.submit).toBe(true); + }); + it('sends the choice the ordinary way if it could not be sent', async () => { fetchMock.mockResolvedValueOnce(html(idea('a', 'Snip'))).mockRejectedValueOnce(new Error('offline')); await start(); diff --git a/app/models/job_idea_dismissal.rb b/app/models/job_idea_dismissal.rb index 7f9542585..687e879fe 100644 --- a/app/models/job_idea_dismissal.rb +++ b/app/models/job_idea_dismissal.rb @@ -20,10 +20,6 @@ class JobIdeaDismissal < ApplicationRecord dismissal end - def self.dismiss!(member, key) - hide!(member, key) - end - # For one of SNOOZES, named. def self.snooze!(member, key, length) hide!(member, key, SNOOZES.fetch(length)) diff --git a/app/services/job_ideas.rb b/app/services/job_ideas.rb index 7c89e419e..d3bca05ad 100644 --- a/app/services/job_ideas.rb +++ b/app/services/job_ideas.rb @@ -8,19 +8,32 @@ class JobIdeas # How many ideas are suggested at a time. SHOWN = 5 - # kind is where the idea came from. job_count is how many jobs a template would - # add, and schedule those jobs, with their days; on is the latest day it was done or planted at this time of year, and - # years how many years it was. From other gardeners, gardeners is how many did - # it, of how many grow the crop, now or in earlier years, as period says. For - # a harvest, on is the day it was last harvested from, or if never, the day it - # was expected to be ready, as period says. - Idea = Struct.new(:kind, :key, :name, :crop, :planting, :template, :schedule, :job_count, :on, :years, :gardeners, :of, :period, + # Where an idea came from is its kind. The rest is what the kind has to say: + # - planting and template: the planting it is for; a template idea also has the + # template and its schedule, the jobs it would add with their days + # - on: the latest day it was done or planted at this time of year, and years how + # many years; for a harvest, the day it was last harvested from or, if never, + # expected to be ready, as period says + # - gardeners: from other gardeners, how many did it, of how many grow the crop, + # now or in earlier years, as period says + Idea = Struct.new(:kind, :key, :name, :crop, :planting, :template, :schedule, :on, :years, :gardeners, :of, :period, keyword_init: true) # Sources a member can switch off, by name, and the ones each name stands for. # Templates and harvests are the member's own garden, so they are always on. SWITCHES = { 'mine' => %i(past_jobs past_plantings), 'community' => %i(community), 'nearby' => %i(nearby) }.freeze + # How many ideas a member has, for the box over their jobs. It looks at other + # people's jobs, so it is kept for a few minutes, until the member adds or hides + # an idea. + def self.count_for(member) + Rails.cache.fetch(['job_ideas_count', member.id], expires_in: 10.minutes) { new(member).ideas.size } + end + + def self.forget_count(member) + Rails.cache.delete(['job_ideas_count', member.id]) + end + def initialize(member, today: Time.zone.today, skip: []) @member = member @today = today @@ -56,6 +69,6 @@ class JobIdeas end def hidden_keys - @hidden_keys ||= @member.job_idea_dismissals.hiding.pluck(:key) + @hidden_keys ||= @member.job_idea_dismissals.hiding.pluck(:key).to_set end end diff --git a/app/services/job_ideas/add.rb b/app/services/job_ideas/add.rb index 42af4f610..4d7f1e830 100644 --- a/app/services/job_ideas/add.rb +++ b/app/services/job_ideas/add.rb @@ -27,7 +27,7 @@ class JobIdeas # template would come back for the next planting of the crop. def add_template jobs = ApplyCropJobTemplate.new(@idea.template, @idea.planting, today: @today).call - JobIdeaDismissal.dismiss!(@member, @idea.key) + JobIdeaDismissal.hide!(@member, @idea.key) jobs end diff --git a/app/services/job_ideas/community.rb b/app/services/job_ideas/community.rb index 5d7cc66dc..5a8eafc06 100644 --- a/app/services/job_ideas/community.rb +++ b/app/services/job_ideas/community.rb @@ -5,12 +5,16 @@ # same time of year; nobody if the member has not said where they are. class JobIdeas class Community < OthersJobs + include SeasonsHelper + private def owner_ids - return [] unless @member.latitude - - Member.confirmed.located.where(@member.latitude.negative? ? 'latitude < 0' : 'latitude >= 0').pluck(:id) + @owner_ids ||= case hemisphere(@member.latitude) + when :southern then Member.confirmed.located.where('latitude < 0').pluck(:id) + when :northern then Member.confirmed.located.where('latitude >= 0').pluck(:id) + else [] + end end def crop_ids @@ -21,10 +25,8 @@ class JobIdeas :community end - def growing(crop) - return unless crop - - growers[crop.id] || 0 + def growing(crop_id) + growers[crop_id] || 0 if crop_id end def growers diff --git a/app/services/job_ideas/harvest.rb b/app/services/job_ideas/harvest.rb index b3805b6f0..5aa0fbf03 100644 --- a/app/services/job_ideas/harvest.rb +++ b/app/services/job_ideas/harvest.rb @@ -14,13 +14,15 @@ class JobIdeas end def ideas - plantings.filter_map { |planting| idea_for(planting) } + found = plantings.to_a + @last_harvests = ::Harvest.where(planting_id: found.map(&:id)).group(:planting_id).maximum(:harvested_at) + found.filter_map { |planting| idea_for(planting) } end private def plantings - @member.plantings.active.annual.where.not(planted_at: nil).preload(:crop, :harvests) + @member.plantings.active.annual.where.not(planted_at: nil).preload(:crop) end def idea_for(planting) @@ -42,7 +44,7 @@ class JobIdeas end def last_harvest(planting) - planting.harvests.filter_map(&:harvested_at).max&.to_date + @last_harvests[planting.id]&.to_date end def expected_on(planting) diff --git a/app/services/job_ideas/nearby.rb b/app/services/job_ideas/nearby.rb index fbf3f9639..e06fcd918 100644 --- a/app/services/job_ideas/nearby.rb +++ b/app/services/job_ideas/nearby.rb @@ -9,9 +9,11 @@ class JobIdeas private def owner_ids - return [] unless @member.latitude && @member.longitude - - Member.confirmed.located.near([@member.latitude, @member.longitude], RADIUS_KM, units: :km).reorder(nil).pluck(:id) + @owner_ids ||= if @member.latitude && @member.longitude + Member.confirmed.located.near([@member.latitude, @member.longitude], RADIUS_KM, units: :km).reorder(nil).pluck(:id) + else + [] + end end def kind diff --git a/app/services/job_ideas/others_jobs.rb b/app/services/job_ideas/others_jobs.rb index 30e2535a7..8a22ad9ce 100644 --- a/app/services/job_ideas/others_jobs.rb +++ b/app/services/job_ideas/others_jobs.rb @@ -13,7 +13,6 @@ class JobIdeas MIN_GARDENERS = 3 MAX_IDEAS = 5 CROP_ID = "COALESCE(CASE WHEN jobs.jobbable_type = 'Crop' THEN jobs.jobbable_id END, plantings.crop_id, seeds.crop_id)" - WITHIN = '(jobs.finished_at BETWEEN ? AND ? OR (jobs.finished_at IS NULL AND jobs.due_date BETWEEN ? AND ?))' Row = Struct.new(:name, :owner_id, :crop_id, :on) def initialize(member, today) @@ -24,8 +23,9 @@ class JobIdeas def ideas return [] if owner_ids&.empty? || crop_ids&.empty? - crops = Crop.where(id: grouped.keys.filter_map(&:last)).index_by(&:id) - grouped.filter_map { |(_name, crop_id), rows| idea_for(rows, crops[crop_id]) }.max_by(MAX_IDEAS, &:gardeners) + found = candidates.max_by(MAX_IDEAS) { |candidate| candidate[:gardeners] } + crops = Crop.where(id: found.filter_map { |candidate| candidate[:crop_id] }).index_by(&:id) + found.map { |candidate| idea_for(candidate, crops[candidate[:crop_id]]) } end private @@ -46,57 +46,43 @@ class JobIdeas # How many gardeners grow the crop, to say how many of them did it. Only # worth saying when the jobs are limited to those growing the crop. - def growing(_crop) + def growing(_crop_id) nil end - def idea_for(rows, crop) - return if open_names.include?(rows.first.name.downcase) + # What each job done by enough gardeners comes to, before the crops are loaded. + def candidates + rows.group_by { |row| [row.name.downcase, row.crop_id] }.filter_map do |(name, crop_id), group| + next if open_names.include?(name) - now, earlier = rows.partition { |row| row.on > @today - WINDOW } - owners = [now, earlier].map { |group| group.map(&:owner_id).uniq.size } - period, gardeners = owners.first >= MIN_GARDENERS ? [:now, owners.first] : [:earlier, owners.last] - return if gardeners < MIN_GARDENERS + now, earlier = group.partition { |row| row.on > @today - WINDOW } + owners = [now, earlier].map { |found| found.map(&:owner_id).uniq.size } + period, gardeners = owners.first >= MIN_GARDENERS ? [:now, owners.first] : [:earlier, owners.last] + next if gardeners < MIN_GARDENERS - Idea.new(kind:, key: "#{kind}:#{rows.first.name.downcase}:#{crop&.id}", name: rows.first.name, crop:, gardeners:, - period:, of: growing(crop), on: rows.map(&:on).max) + { name: group.first.name, crop_id:, period:, gardeners:, on: group.map(&:on).max } + end end - def grouped - @grouped ||= rows.group_by { |row| [row.name.downcase, row.crop_id] } + def idea_for(candidate, crop) + Idea.new(kind:, key: "#{kind}:#{candidate[:name].downcase}:#{crop&.id}", name: candidate[:name], crop:, + gardeners: candidate[:gardeners], period: candidate[:period], of: growing(crop&.id), on: candidate[:on]) end def rows jobs = Job.where(finished: true, crop_job_step_id: nil).where.not(owner_id: left_out_ids) jobs = jobs.where(owner_id: owner_ids) if owner_ids jobs = jobs.where("#{CROP_ID} IN (?)", crop_ids) if crop_ids - jobs.joins("LEFT JOIN plantings ON jobs.jobbable_type = 'Planting' AND plantings.id = jobs.jobbable_id") - .joins("LEFT JOIN seeds ON jobs.jobbable_type = 'Seed' AND seeds.id = jobs.jobbable_id") - .where(in_windows, *window_values) + finished_in(jobs.joins("LEFT JOIN plantings ON jobs.jobbable_type = 'Planting' AND plantings.id = jobs.jobbable_id") + .joins("LEFT JOIN seeds ON jobs.jobbable_type = 'Seed' AND seeds.id = jobs.jobbable_id"), + [(@today - WINDOW)..@today] + past_windows) .pluck('jobs.name', 'jobs.owner_id', 'jobs.finished_at', 'jobs.due_date', Arel.sql(CROP_ID)) .map { |name, owner_id, finished_at, due_date, crop_id| Row.new(name, owner_id, crop_id, finished_at&.to_date || due_date) } end - # Now, and the same fortnight in each earlier year. - def windows - [(@today - WINDOW)..@today] + past_windows - end - - def in_windows - windows.map { WITHIN }.join(' OR ') - end - - def window_values - windows.flat_map { |window| [window.first.beginning_of_day, window.last.end_of_day, window.first, window.last] } - end - # Themselves, and the people they have blocked. def left_out_ids - [@member.id] + @member.blocks.pluck(:blocked_id) - end - - def open_names - @open_names ||= @member.jobs.active.pluck(:name).map(&:downcase) + @left_out_ids ||= [@member.id] + @member.blocks.pluck(:blocked_id) end end end diff --git a/app/services/job_ideas/past_jobs.rb b/app/services/job_ideas/past_jobs.rb index 1a4b8c15d..c9b199578 100644 --- a/app/services/job_ideas/past_jobs.rb +++ b/app/services/job_ideas/past_jobs.rb @@ -27,18 +27,15 @@ class JobIdeas # Each job with the day it was done. A job finished before the time was # recorded was done on the day it was due. def done_jobs - jobs = past_windows.map { |window| finished_in(window) }.reduce(:or) - jobs.includes(:jobbable).map { |job| [job, job.finished_at&.to_date || job.due_date] } + jobs = finished_in(@member.jobs.where(finished: true, crop_job_step_id: nil), past_windows).includes(:jobbable).to_a + preload_crops(jobs) + jobs.map { |job| [job, job.finished_at&.to_date || job.due_date] } end - def finished_in(window) - @member.jobs.where(finished: true, crop_job_step_id: nil) - .where('jobs.finished_at BETWEEN ? AND ? OR (jobs.finished_at IS NULL AND jobs.due_date BETWEEN ? AND ?)', - window.first.beginning_of_day, window.last.end_of_day, window.first, window.last) - end - - def open_names - @open_names ||= @member.jobs.active.pluck(:name).map(&:downcase) + # The crop of every planting and seed a job is about, in one go. + def preload_crops(jobs) + about = jobs.filter_map(&:jobbable).grep_v(Crop) + ActiveRecord::Associations::Preloader.new(records: about, associations: :crop).call end def key_for(job) diff --git a/app/services/job_ideas/past_plantings.rb b/app/services/job_ideas/past_plantings.rb index c37da397e..7874e5377 100644 --- a/app/services/job_ideas/past_plantings.rb +++ b/app/services/job_ideas/past_plantings.rb @@ -17,8 +17,9 @@ class JobIdeas next if planted_since.include?(crop_id) || to_plant.include?(crop_id) latest = plantings.max_by(&:planted_at) + years = plantings.map { |planting| planting.planted_at.year }.uniq.size Idea.new(kind: :past_planting, key: "past_planting:#{crop_id}", name: latest.crop.name, crop: latest.crop, - planting: latest, on: latest.planted_at, years: plantings.map { |planting| planting.planted_at.year }.uniq.size) + on: latest.planted_at, years:) end end diff --git a/app/services/job_ideas/past_years.rb b/app/services/job_ideas/past_years.rb index 1a70a9c3c..d619be405 100644 --- a/app/services/job_ideas/past_years.rb +++ b/app/services/job_ideas/past_years.rb @@ -2,10 +2,15 @@ class JobIdeas # The time of year a source looks back to: the fortnight starting on today's day - # in each of the years before, as far back as MAX_YEARS. + # in each of the years before, as far back as MAX_YEARS. Also what the sources + # share about jobs, which are finished in a window and which the member already + # has open. module PastYears WINDOW = 14.days MAX_YEARS = 5 + # Done in the window by the time it was crossed off or, if that was not + # recorded, the day it was due. + FINISHED_IN = '(jobs.finished_at BETWEEN ? AND ? OR (jobs.finished_at IS NULL AND jobs.due_date BETWEEN ? AND ?))' private @@ -13,5 +18,17 @@ class JobIdeas def past_windows (1..MAX_YEARS).map { |years| (@today - years.years)..(@today - years.years + WINDOW) } end + + # The finished jobs in `jobs` that were done in any of the windows. + def finished_in(jobs, windows) + windows.map do |window| + jobs.where(FINISHED_IN, window.first.beginning_of_day, window.last.end_of_day, window.first, window.last) + end.reduce(:or) + end + + # The names of the jobs the member has open, lower case. + def open_names + @open_names ||= @member.jobs.active.pluck(:name).to_set(&:downcase) + end end end diff --git a/app/services/job_ideas/templates.rb b/app/services/job_ideas/templates.rb index 38ad844de..db2127d57 100644 --- a/app/services/job_ideas/templates.rb +++ b/app/services/job_ideas/templates.rb @@ -15,7 +15,7 @@ class JobIdeas by_template.map do |template, found| planting, entries = found.max_by { |planting, _entries| [planting.planted_at, planting.id] } Idea.new(kind: :template, key: "template:#{template.id}", name: template.name, crop: template.crop, planting:, template:, - schedule: entries, job_count: entries.size, on: planting.planted_at.to_date) + schedule: entries, on: planting.planted_at.to_date) end end diff --git a/app/views/job_ideas/_idea.html.haml b/app/views/job_ideas/_idea.html.haml index a5e3b42fe..a5eb991bb 100644 --- a/app/views/job_ideas/_idea.html.haml +++ b/app/views/job_ideas/_idea.html.haml @@ -27,7 +27,7 @@ %span.idea-preview-day= l(entry.date, format: '%-d %b') = entry.step.name .idea-actions - - add = idea.kind == :template ? t('job_ideas.add_all', count: idea.job_count) : t('job_ideas.add') + - add = idea.kind == :template ? t('job_ideas.add_all', count: idea.schedule.size) : t('job_ideas.add') = button_to add, job_ideas_path(skip:), params: { key: idea.key }, class: 'idea-add', form_class: 'idea-form' .idea-later - dismiss = { params: { key: idea.key }, class: 'idea-link', form_class: 'idea-form' } diff --git a/app/views/job_ideas/index.html.haml b/app/views/job_ideas/index.html.haml index c8cbe83c0..21a6d6484 100644 --- a/app/views/job_ideas/index.html.haml +++ b/app/views/job_ideas/index.html.haml @@ -18,7 +18,7 @@ .col-xl-6.mb-4.order-first.order-xl-last .notepad.job-ideas .notepad-title - %h2= t('job_ideas.card_title') + %h2= t('job_ideas.title') = render 'job_ideas/list' .col-xl-6.mb-4 = render 'jobs/notepad', owner: current_member, groups: @job_groups, done_this_week: @done_this_week, diff --git a/app/views/jobs/_ideas.html.haml b/app/views/jobs/_ideas.html.haml new file mode 100644 index 000000000..a88388f7f --- /dev/null +++ b/app/views/jobs/_ideas.html.haml @@ -0,0 +1,5 @@ +-# The ideas over a member's jobs: the yellow box that says there are some, if there +-# are, and the overlay it opens. +- count = JobIdeas.count_for(owner) += render 'jobs/ideas_banner', count: count if count.positive? += render 'jobs/ideas_modal' diff --git a/app/views/jobs/_ideas_modal.html.haml b/app/views/jobs/_ideas_modal.html.haml index d3995cfd6..e726b4ce3 100644 --- a/app/views/jobs/_ideas_modal.html.haml +++ b/app/views/jobs/_ideas_modal.html.haml @@ -5,7 +5,7 @@ .modal-dialog.modal-dialog-scrollable.modal-lg .modal-content .modal-header - %h2.modal-title#job-ideas-title= t('job_ideas.card_title') + %h2.modal-title#job-ideas-title= t('job_ideas.title') %button.btn-close{ type: 'button', data: { bs_dismiss: 'modal' }, 'aria-label': t('job_ideas.done') } .modal-body.p-0 .notepad.job-ideas#job-ideas-body{ 'aria-live': 'polite' } diff --git a/app/views/jobs/_notepad.html.haml b/app/views/jobs/_notepad.html.haml index b928604c6..2dad5816b 100644 --- a/app/views/jobs/_notepad.html.haml +++ b/app/views/jobs/_notepad.html.haml @@ -25,10 +25,7 @@ %span.notepad-add-saving-text %p.notepad-add-failed.text-danger.mx-3{ id: 'notepad-add-failed', role: 'alert', hidden: true }= t('jobs.pad.add_failed') %p.notepad-note= t('jobs.pad.public_note') + ' ' + t('jobs.pad.link_hint') - - banner = !garden && local_assigns.fetch(:ideas_banner, true) - - ideas = job_ideas_count(owner) if banner - = render 'jobs/ideas_banner', count: ideas if ideas&.positive? - = render 'jobs/ideas_modal' if banner + = render 'jobs/ideas', owner: owner unless garden || !local_assigns.fetch(:ideas_banner, true) #notepad-groups{ data: { celebrations: job_celebrations.to_json, garden_id: garden&.id } }= render 'jobs/notepad_groups', groups:, in_garden: garden.present? .notepad-snail diff --git a/config/locales/en.yml b/config/locales/en.yml index 172ae2ca8..91207a455 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -665,7 +665,6 @@ en: unknown: Unknown job_ideas: title: Ideas for now - card_title: Ideas for now banner: one: We have 1 suggested job for you this week other: We have %{count} suggested jobs for you this week @@ -691,7 +690,6 @@ en: nearby: Near you nearby_note: "%{place} and %{km} km around" none: Nothing to suggest right now. Check back as the season changes. - back: Back to your jobs add: "+ Add" add_all: Add all %{count} preview: diff --git a/spec/models/job_idea_dismissal_spec.rb b/spec/models/job_idea_dismissal_spec.rb index 9760f2aad..2ea8a189d 100644 --- a/spec/models/job_idea_dismissal_spec.rb +++ b/spec/models/job_idea_dismissal_spec.rb @@ -24,8 +24,8 @@ describe JobIdeaDismissal do end end - describe '.dismiss!' do - before { described_class.dismiss!(member, key) } + describe '.hide!' do + before { described_class.hide!(member, key) } it 'hides the idea' do expect(hidden_keys).to eq [key] @@ -61,7 +61,7 @@ describe JobIdeaDismissal do end it 'does not keep two for the same idea' do - described_class.dismiss!(member, key) + described_class.hide!(member, key) expect(member.job_idea_dismissals.count).to eq 1 end end diff --git a/spec/services/job_ideas/past_plantings_spec.rb b/spec/services/job_ideas/past_plantings_spec.rb index 6784879f8..de6074c0c 100644 --- a/spec/services/job_ideas/past_plantings_spec.rb +++ b/spec/services/job_ideas/past_plantings_spec.rb @@ -19,10 +19,6 @@ describe JobIdeas::PastPlantings do expect(ideas.first.on).to eq planted_at end - it 'is about the planting it was last done' do - expect(ideas.first.planting).to eq planting - end - it 'keys the idea by crop' do expect(ideas.first.key).to eq "past_planting:#{crop.id}" end diff --git a/spec/services/job_ideas/templates_spec.rb b/spec/services/job_ideas/templates_spec.rb index 66f3a0996..5b994c43d 100644 --- a/spec/services/job_ideas/templates_spec.rb +++ b/spec/services/job_ideas/templates_spec.rb @@ -25,7 +25,7 @@ describe JobIdeas::Templates do end it 'counts the jobs it would add' do - expect(ideas.first.job_count).to eq 2 + expect(ideas.first.schedule.size).to eq 2 end it 'keys the idea by template' do @@ -52,7 +52,7 @@ describe JobIdeas::Templates do end it 'counts the jobs for that planting only' do - expect(ideas.first.job_count).to eq 2 + expect(ideas.first.schedule.size).to eq 2 end end