From e32488ca9b46269f5a0cc60cc102e97076a84221 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Wed, 7 Feb 2018 07:57:14 +0000 Subject: [PATCH 01/15] Resolve #598 remove legacy notice --- app/models/recipient.rb | 2 +- .../eligibilities/_legacy_notice.html.haml | 10 ------ app/views/funds/index.haml | 2 -- spec/features/eligibility_spec.rb | 32 ------------------- 4 files changed, 1 insertion(+), 45 deletions(-) delete mode 100644 app/views/eligibilities/_legacy_notice.html.haml diff --git a/app/models/recipient.rb b/app/models/recipient.rb index c9ae8673..85212881 100644 --- a/app/models/recipient.rb +++ b/app/models/recipient.rb @@ -78,7 +78,7 @@ def subscribed? subscription&.active? end - def update_funds_checked!(eligibility) + def update_funds_checked!(eligibility) # TODO: remove & funds_checked update_column(:funds_checked, eligibility.count { |_, v| v.key? 'quiz' }) end diff --git a/app/views/eligibilities/_legacy_notice.html.haml b/app/views/eligibilities/_legacy_notice.html.haml deleted file mode 100644 index d29e37f9..00000000 --- a/app/views/eligibilities/_legacy_notice.html.haml +++ /dev/null @@ -1,10 +0,0 @@ --# TODO: performance -- if @recipient.recipient_funder_accesses.count > 0 - .cta.tight.uk-margin-large-bottom - %strong We've made some changes - %br - Last time you used Beehive you conducted - %strong= @recipient.funds_checked - eligibility check(s). We've made some changes to how this feature works, and if you'd like to reset this please contact - = succeed '.' do - = link_to 'support@beehivegiving.org', 'mailto:support@beehivegiving.org' diff --git a/app/views/funds/index.haml b/app/views/funds/index.haml index 56025ccb..93565fb4 100644 --- a/app/views/funds/index.haml +++ b/app/views/funds/index.haml @@ -59,8 +59,6 @@ %section.perc75.md.mb80.px20 %h1.mb20 Funds - = render partial: 'eligibilities/legacy_notice' - .mb30 = cell :filter, params, funding_duration: @proposal.funding_duration %small.slate diff --git a/spec/features/eligibility_spec.rb b/spec/features/eligibility_spec.rb index b5f1dd2d..552b49e4 100644 --- a/spec/features/eligibility_spec.rb +++ b/spec/features/eligibility_spec.rb @@ -19,38 +19,6 @@ scenario 'invalid recipient restriction affects all proposals' - scenario 'When I have previous eligibility checks, - I want to see a message explaining new changes, - so I understand what happend to my previous work' do - def assert(present: true) - copy = 'Last time you used Beehive you conducted 1 eligibility check' - [ - proposal_funds_path(@proposal), - eligible_proposal_funds_path(@proposal), - ineligible_proposal_funds_path(@proposal) - ].each do |path| - visit path - if present - expect(page).to have_text copy - else - expect(page).not_to have_text copy - end - end - end - - assert(present: false) - - RecipientFunderAccess.create( - recipient: @db[:recipient], funder_id: Funder.first.id - ) - Recipient.joins(:recipient_funder_accesses) - .group(:recipient_id).count.each do |k, v| - Recipient.find(k).update_column(:funds_checked, v) - end - - assert - end - scenario "When I check eligibility for the first time and need to update my proposal, I want to understand why I need to do it, so I feel I'm using my time in the best way" do From 9e8aad44a191d2ca6473a2a584bb5edf83d114e3 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Wed, 7 Feb 2018 16:48:16 +0000 Subject: [PATCH 02/15] Resolve #597 fund descriptions should not be in bold --- app/helpers/funds_helper.rb | 27 +++++++++++++++++ app/models/fund.rb | 10 ------ app/views/funds/_fund_redacted.haml | 2 +- app/views/funds/hidden.haml | 4 +-- app/views/public_funds/_fund_list.haml | 2 +- spec/helpers/funds_helper_spec.rb | 42 ++++++++++++++++++++++++++ spec/models/fund_spec.rb | 6 ---- 7 files changed, 73 insertions(+), 20 deletions(-) create mode 100644 spec/helpers/funds_helper_spec.rb diff --git a/app/helpers/funds_helper.rb b/app/helpers/funds_helper.rb index c86ce425..ed4261b9 100644 --- a/app/helpers/funds_helper.rb +++ b/app/helpers/funds_helper.rb @@ -1,4 +1,21 @@ module FundsHelper + def redact(fund, field, opts = {}) + placeholder = '**hidden**' + + tokens = fund.name.downcase.split + fund.funder.name.downcase.split + stop_words = %w[and the fund trust foundation grants charitable] + final_tokens = tokens - stop_words + + regex = Regexp.new(final_tokens.join('\b|\b'), options: 'i') + + with_placeholder = fund[field].gsub(regex, placeholder) + + scramble = Array.new(rand(5..10)) { [*'a'..'z'].sample }.join + + strip_and_trim(with_placeholder, opts) + .gsub(placeholder, "#{scramble}") + end + def period(fund = @fund, date_format="%b %Y") return unless fund.open_data if fund.period_start.strftime(date_format) == fund.period_end.strftime(date_format) @@ -48,4 +65,14 @@ def country_distribution .collect { |c| { c['name'] => c['count'] } } .reduce({}, :merge) end + + private + + def strip_and_trim(str, opts = {}) + if opts[:trim] + strip_tags(str).truncate_words(opts[:trim], omission: opts[:omission]) + else + str + end + end end diff --git a/app/models/fund.rb b/app/models/fund.rb index 64f0e35f..c05be098 100644 --- a/app/models/fund.rb +++ b/app/models/fund.rb @@ -157,16 +157,6 @@ def description_html markdown(description) end - def description_redacted(plain: false) - tokens = name.downcase.split + funder.name.downcase.split - stop_words = %w[and the fund trust foundation grants charitable] - final_tokens = tokens - stop_words - reg = Regexp.new(final_tokens.join('\b|\b'), options: 'i') - scramble = Array.new(rand(5..10)) { [*'a'..'z'].sample }.join - desc = description.gsub(reg, "#{scramble}") - markdown(desc, plain: plain) - end - def amount_desc # TODO: refactor & test return 'of any size' unless min_amount_awarded_limited || max_amount_awarded_limited opts = { precision: 0, unit: '£' } diff --git a/app/views/funds/_fund_redacted.haml b/app/views/funds/_fund_redacted.haml index 08b7bebd..cd572ee2 100644 --- a/app/views/funds/_fund_redacted.haml +++ b/app/views/funds/_fund_redacted.haml @@ -14,7 +14,7 @@ .p20.border-top.border-silver %h6.bold.mb10= cell(:fund_insight, fund, proposal: @proposal).call(:summary) - .night= raw fund.description_redacted(plain: true).truncate_words(30, omission: "... #{link_to 'more', proposal_fund_path(@proposal, fund)}") + .night= raw redact(fund, :description, trim: 30, omission: "... #{link_to 'more', proposal_fund_path(@proposal, fund)}") .truncate.mt10= cell(:fund_insight, fund, proposal: @proposal).call(:themes) diff --git a/app/views/funds/hidden.haml b/app/views/funds/hidden.haml index 952d281a..471146ac 100644 --- a/app/views/funds/hidden.haml +++ b/app/views/funds/hidden.haml @@ -20,7 +20,7 @@ by %span.redacted.grey= scramble_name(@fund.funder.name) - .mb30.fs18.night.lh25= raw @fund.description_redacted(plain: true).truncate_words(30) + .mb30.fs18.night.lh25= raw redact(@fund, :description, trim: 30) %hr %h6.my20.slate= cell(:fund_insight, @fund, proposal: @proposal).call(:summary) @@ -35,7 +35,7 @@ %h2.mb20 Summary .mb20.markdown - = raw @fund.description_redacted + = raw redact(@fund, :description) - if @fund.period_end.present? && @fund.period_end > 3.years.ago .mt30.night diff --git a/app/views/public_funds/_fund_list.haml b/app/views/public_funds/_fund_list.haml index f838298d..4284786f 100644 --- a/app/views/public_funds/_fund_list.haml +++ b/app/views/public_funds/_fund_list.haml @@ -23,7 +23,7 @@ .muted .mb5.blue.fs22.lh30.redacted= scramble_name fund.name .mb5.fs15.lh20.grey.redacted= scramble_name fund.funder.name - %p.mb5.fs15.lh20= raw fund.description_redacted(plain: true).truncate_words(30) + %p.mb5.fs15.lh20= raw redact(fund, :description, trim: 30) %p.fs14.lh18 = cell(:fund_insight, fund).call(:themes) diff --git a/spec/helpers/funds_helper_spec.rb b/spec/helpers/funds_helper_spec.rb new file mode 100644 index 00000000..6e5434d2 --- /dev/null +++ b/spec/helpers/funds_helper_spec.rb @@ -0,0 +1,42 @@ +require 'rails_helper' +include ActionView::Helpers::SanitizeHelper + +describe FundsHelper do + subject { Class.new.include(FundsHelper).new } + + let(:funder_name) { 'Super Foundation' } + let(:fund_name) { 'Social Change Fund' } + let(:fund) do + build( + :fund, + funder: build(:funder, name: funder_name), + name: fund_name, + description: "A description #{fund_name} #{funder_name}" + ) + end + let(:field) { :description } + + context '#redact' do + it 'fund name' do + expect(subject.redact(fund, field)).not_to have_text('Name') + end + + it 'funder name' do + expect(subject.redact(fund, field)).not_to have_text('Super') + end + + it 'trims' do + str = subject.redact(fund, field, trim: 5) + expect(str).not_to have_text('Foundation') + end + + it 'omission' do + str = subject.redact(fund, field, trim: 5, omission: 'more') + expect(str).to have_text('more') + end + + it 'does not split tags' do + expect(subject.redact(fund, field, trim: 3)).to have_selector('span') + end + end +end diff --git a/spec/models/fund_spec.rb b/spec/models/fund_spec.rb index 66ad67f8..d892f75c 100644 --- a/spec/models/fund_spec.rb +++ b/spec/models/fund_spec.rb @@ -159,12 +159,6 @@ end end - it '#description_redacted' do - @fund.description += @fund.name + ' ' + @fund.funder.name - expect(@fund.description_redacted).not_to include @fund.name - expect(@fund.description_redacted).not_to include @fund.funder.name - end - it 'is valid' do expect(@fund).to be_valid end From 5bb144b74ac00a99a0226db2e8cf65a1edb07e01 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Fri, 9 Feb 2018 14:07:58 +0000 Subject: [PATCH 03/15] Resolve #595 - When fund criteria are updated assessments are triggered to update --- app/controllers/funds_controller.rb | 5 +++- app/models/assessment.rb | 19 ++++++------ app/models/fund.rb | 2 +- app/models/question.rb | 2 +- app/services/check/each.rb | 2 ++ ...9113135_add_fund_version_to_assessments.rb | 5 ++++ db/schema.rb | 3 +- spec/features/eligibility_spec.rb | 18 ++++++++++++ spec/models/assessment_spec.rb | 29 ++++++++++++++++++- spec/models/fund_spec.rb | 7 +++++ spec/services/check/each_spec.rb | 3 +- 11 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 db/migrate/20180209113135_add_fund_version_to_assessments.rb diff --git a/app/controllers/funds_controller.rb b/app/controllers/funds_controller.rb index 555a8548..3be89821 100644 --- a/app/controllers/funds_controller.rb +++ b/app/controllers/funds_controller.rb @@ -41,7 +41,10 @@ def user_not_authorised end def update_analysis(funds) - unassessed = funds.where('assessments.eligibility_status IS NULL') + unassessed = funds.where( + "assessments.fund_version != #{Fund.version} OR " \ + 'assessments.fund_version IS NULL' + ) return if unassessed.empty? Assessment.analyse_and_update!(unassessed, @proposal) end diff --git a/app/models/assessment.rb b/app/models/assessment.rb index 463eeea4..2421fef1 100644 --- a/app/models/assessment.rb +++ b/app/models/assessment.rb @@ -8,16 +8,15 @@ class Assessment < ApplicationRecord Check::Eligibility::Quiz.new ].freeze - ELIGIBILITY_COLUMNS = %i[ - eligibility_amount - eligibility_funding_type - eligibility_location - eligibility_org_income - eligibility_org_type - eligibility_quiz + ELIGIBILITY_STATUS_COLUMNS = CHECKS.map do |check| + "eligibility_#{check.class.name.demodulize.underscore}".to_sym + end.freeze + + PERMITTED_COLUMNS = (ELIGIBILITY_STATUS_COLUMNS + %i[ eligibility_quiz_failing eligibility_status - ].freeze + fund_version + ]).freeze belongs_to :fund belongs_to :proposal @@ -35,7 +34,7 @@ def self.analyse(funds, proposal) def self.analyse_and_update!(funds, proposal) updates = analyse(funds, proposal) - Assessment.import!(updates, on_duplicate_key_update: ELIGIBILITY_COLUMNS) + Assessment.import!(updates, on_duplicate_key_update: PERMITTED_COLUMNS) end def attributes @@ -49,7 +48,7 @@ def set_eligibility_status end def eligible_status - columns = attributes.slice(*ELIGIBILITY_COLUMNS.slice(0..-3)).values + columns = attributes.slice(*ELIGIBILITY_STATUS_COLUMNS).values return INELIGIBLE if columns.any? { |c| c == INELIGIBLE } return ELIGIBLE if columns.all? { |c| c == ELIGIBLE } INCOMPLETE diff --git a/app/models/fund.rb b/app/models/fund.rb index c05be098..c4139948 100644 --- a/app/models/fund.rb +++ b/app/models/fund.rb @@ -3,8 +3,8 @@ class Fund < ApplicationRecord scope :active, -> { where(state: 'active') } scope :stubs, -> { where(state: 'stub') } - scope :newer_than, ->(date) { where('updated_at > ?', date) } scope :recent, -> { order updated_at: :desc } + scope :version, -> { active.order(:updated_at).pluck(:updated_at).hash } STATES = %w[active inactive draft stub].freeze diff --git a/app/models/question.rb b/app/models/question.rb index ed8d874a..cd8be0c7 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -1,6 +1,6 @@ class Question < ApplicationRecord scope :grouped, ->(type, group) { where(criterion_type: type, group: group) } belongs_to :criterion, polymorphic: true - belongs_to :fund + belongs_to :fund, touch: true has_many :answers, through: :criterion end diff --git a/app/services/check/each.rb b/app/services/check/each.rb index 0e781867..98a40c21 100644 --- a/app/services/check/each.rb +++ b/app/services/check/each.rb @@ -7,6 +7,7 @@ def initialize(criteria) end def call_each(funds, proposal) + fund_version = Fund.version updates = [] assessments = persisted_assessments(funds, proposal) @@ -16,6 +17,7 @@ def call_each(funds, proposal) preload_funds(funds).each do |fund| assessment = assessments[fund.id] || build(fund, proposal, recipient) @criteria.each { |check| check.call(assessment) } + assessment.fund_version = fund_version updates << assessment if assessment.valid? end diff --git a/db/migrate/20180209113135_add_fund_version_to_assessments.rb b/db/migrate/20180209113135_add_fund_version_to_assessments.rb new file mode 100644 index 00000000..9ae3fe7e --- /dev/null +++ b/db/migrate/20180209113135_add_fund_version_to_assessments.rb @@ -0,0 +1,5 @@ +class AddFundVersionToAssessments < ActiveRecord::Migration[5.1] + def change + add_column :assessments, :fund_version, :bigint + end +end diff --git a/db/schema.rb b/db/schema.rb index 3ea34b50..88d78d26 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.define(version: 20171221161206) do +ActiveRecord::Schema.define(version: 20180209113135) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -95,6 +95,7 @@ t.integer "eligibility_status", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.bigint "fund_version" t.index ["fund_id"], name: "index_assessments_on_fund_id" t.index ["proposal_id"], name: "index_assessments_on_proposal_id" t.index ["recipient_id"], name: "index_assessments_on_recipient_id" diff --git a/spec/features/eligibility_spec.rb b/spec/features/eligibility_spec.rb index 552b49e4..c494c23b 100644 --- a/spec/features/eligibility_spec.rb +++ b/spec/features/eligibility_spec.rb @@ -19,6 +19,24 @@ scenario 'invalid recipient restriction affects all proposals' + # TODO: refactor + scenario 'missing assessments created' do + Assessment.last.destroy + visit root_path + expect(Assessment.count).to eq(7) + end + + # TODO: refactor + scenario 'when fund criteria are updated assessments are also updated' do + old_fund_version = Fund.version + Fund.first.update(name: 'New name') + visit root_path + Assessment.all.each do |a| + expect(a.fund_version).not_to eq(old_fund_version) + expect(a.fund_version).to eq(Fund.version) + end + end + scenario "When I check eligibility for the first time and need to update my proposal, I want to understand why I need to do it, so I feel I'm using my time in the best way" do diff --git a/spec/models/assessment_spec.rb b/spec/models/assessment_spec.rb index 1ee4b331..bd9bfae0 100644 --- a/spec/models/assessment_spec.rb +++ b/spec/models/assessment_spec.rb @@ -18,6 +18,33 @@ end end + context do + let(:eligibility) do + %i[ + eligibility_amount + eligibility_funding_type + eligibility_location + eligibility_org_income + eligibility_org_type + eligibility_quiz + ] + end + + it 'ELIGIBILITY_STATUS_COLUMNS correct' do + expect(Assessment::ELIGIBILITY_STATUS_COLUMNS).to match_array(eligibility) + end + + it 'PERMITTED_COLUMNS correct' do + misc = %i[ + eligibility_quiz_failing + eligibility_status + fund_version + ] + permitted = eligibility + misc + expect(Assessment::PERMITTED_COLUMNS).to match_array(permitted) + end + end + context do let(:proposal) { create(:proposal) } @@ -76,7 +103,7 @@ subject { build(:eligible) } it { expect(subject.eligibility_status).to eq(ELIGIBLE) } end - end + end it '#attributes keys symbolized' do subject.attributes.keys.each { |k| expect(k).to be_a(Symbol) } diff --git a/spec/models/fund_spec.rb b/spec/models/fund_spec.rb index d892f75c..737c09e2 100644 --- a/spec/models/fund_spec.rb +++ b/spec/models/fund_spec.rb @@ -1,3 +1,5 @@ +# rubocop:disable Style/NumericLiterals + require 'rails_helper' describe Fund do @@ -12,6 +14,11 @@ end end + it '#version' do + Fund.update_all(updated_at: Date.new(2018, 1, 1)) + expect(Fund.version).to eq(-3875252393691411349) + end + context '#order_by' do subject { Fund.join(proposal).order_by(col) } diff --git a/spec/services/check/each_spec.rb b/spec/services/check/each_spec.rb index 859bf860..e1fc6c98 100644 --- a/spec/services/check/each_spec.rb +++ b/spec/services/check/each_spec.rb @@ -12,7 +12,8 @@ themes: themes, restrictions_known: false, priorities_known: false - )[0] + )[0], + fund_version: Fund.version ) end From 67df20fda3c476ae1dd748697200fb0f1ba89637 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Fri, 9 Feb 2018 14:51:44 +0000 Subject: [PATCH 04/15] Resolve #602 - ActionController::UnknownFormat --- app/controllers/application_controller.rb | 3 +++ spec/models/fund_spec.rb | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index fe006bb8..7c7b8629 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,6 +10,9 @@ class ApplicationController < ActionController::Base rescue_from ActionController::InvalidAuthenticityToken, with: :bad_token rescue_from Pundit::NotAuthorizedError, with: :user_not_authorised + rescue_from ActionController::UnknownFormat do + render 'errors/not_found', status: 404 + end def logged_in? current_user != nil diff --git a/spec/models/fund_spec.rb b/spec/models/fund_spec.rb index 737c09e2..0b93cf34 100644 --- a/spec/models/fund_spec.rb +++ b/spec/models/fund_spec.rb @@ -15,8 +15,8 @@ end it '#version' do - Fund.update_all(updated_at: Date.new(2018, 1, 1)) - expect(Fund.version).to eq(-3875252393691411349) + Fund.update_all(updated_at: DateTime.new(2018, 1, 1).in_time_zone) + expect(Fund.version).to eq(-2684838205550011942) end context '#order_by' do From b22ec895065819d83ea91a36f201e7389d7c0c61 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Fri, 9 Feb 2018 15:14:15 +0000 Subject: [PATCH 05/15] Resolve #603 - ActionView::Template::Error --- app/controllers/recipients_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/recipients_controller.rb b/app/controllers/recipients_controller.rb index 51df3b19..41d0b002 100644 --- a/app/controllers/recipients_controller.rb +++ b/app/controllers/recipients_controller.rb @@ -3,6 +3,7 @@ class RecipientsController < ApplicationController def edit @recipient = Recipient.find_by(slug: params[:id]) + redirect_to root_path unless @recipient end def update From 33d9220627db13423916e3d5256a35299b15dd1d Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Fri, 9 Feb 2018 15:34:29 +0000 Subject: [PATCH 06/15] Ensure consistent Fund.version --- Gemfile | 1 + Gemfile.lock | 2 ++ app/models/fund.rb | 5 ++++- spec/models/fund_spec.rb | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 7fbb1728..e0297ca9 100644 --- a/Gemfile +++ b/Gemfile @@ -55,6 +55,7 @@ gem 'sitemap_generator' gem 'stripe' gem 'uikit-sass-rails' gem 'workflow' +gem 'xxhash' # Admin gem 'activeadmin' diff --git a/Gemfile.lock b/Gemfile.lock index fded0cbe..6098bb5d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -429,6 +429,7 @@ GEM workflow (1.2.0) xpath (2.1.0) nokogiri (~> 1.3) + xxhash (0.4.0) PLATFORMS ruby @@ -498,6 +499,7 @@ DEPENDENCIES webmock webpacker workflow + xxhash RUBY VERSION ruby 2.4.1p111 diff --git a/app/models/fund.rb b/app/models/fund.rb index c4139948..22cdeb20 100644 --- a/app/models/fund.rb +++ b/app/models/fund.rb @@ -4,7 +4,6 @@ class Fund < ApplicationRecord scope :active, -> { where(state: 'active') } scope :stubs, -> { where(state: 'stub') } scope :recent, -> { order updated_at: :desc } - scope :version, -> { active.order(:updated_at).pluck(:updated_at).hash } STATES = %w[active inactive draft stub].freeze @@ -78,6 +77,10 @@ def save(*args) end end + def self.version + XXhash.xxh32(active.order(:updated_at).pluck(:updated_at).join) + end + def self.join(proposal = nil) joins( 'LEFT JOIN assessments ' \ diff --git a/spec/models/fund_spec.rb b/spec/models/fund_spec.rb index 0b93cf34..3454f454 100644 --- a/spec/models/fund_spec.rb +++ b/spec/models/fund_spec.rb @@ -16,7 +16,7 @@ it '#version' do Fund.update_all(updated_at: DateTime.new(2018, 1, 1).in_time_zone) - expect(Fund.version).to eq(-2684838205550011942) + expect(Fund.version).to eq(269159070) end context '#order_by' do From 8971714d10815022133fb42b05defc468ae1dc89 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Tue, 13 Feb 2018 12:06:33 +0000 Subject: [PATCH 07/15] Fund listing page functions without sign in Resolve #592 Resolve #594 --- app/cells/application/index.slim | 2 +- app/cells/breadcrumb_cell.rb | 1 + app/cells/eligibility/quiz.slim | 2 +- app/cells/filter/show.slim | 2 +- app/cells/filter_cell.rb | 9 +- app/cells/footer_themes/show.slim | 5 +- app/cells/fund_insight/title.slim | 2 +- app/cells/fund_insight_cell.rb | 14 +-- app/cells/new_proposal_links/dashboard.slim | 2 +- app/cells/progress/apply.rb | 2 +- app/cells/suitability/index.slim | 2 +- app/cells/suitability/quiz.slim | 2 +- app/cells/v2_navbar/show.slim | 2 +- app/controllers/application_controller.rb | 6 +- app/controllers/eligibilities_controller.rb | 2 +- app/controllers/enquiries_controller.rb | 6 +- app/controllers/feedback_controller.rb | 6 +- app/controllers/funds_controller.rb | 22 ++-- app/controllers/proposals_controller.rb | 10 +- app/controllers/public_funds_controller.rb | 35 ------ app/controllers/requests_controller.rb | 2 +- app/controllers/reveals_controller.rb | 2 +- .../signup_proposals_controller.rb | 6 +- app/policies/enquiry_policy.rb | 11 +- app/policies/fund_policy.rb | 26 +--- app/views/enquiries/new.html.haml | 8 +- app/views/funds/_fund.haml | 4 +- app/views/funds/_fund_redacted.haml | 8 +- app/views/funds/_not_enough_data.html.haml | 7 -- app/views/funds/_sidebar.haml | 28 +++++ app/views/funds/hidden.haml | 9 +- app/views/funds/index.haml | 46 ++----- app/views/funds/show.haml | 9 +- app/views/funds/stub.haml | 9 +- app/views/funds/themed.haml | 10 +- app/views/proposals/_card.html.haml | 4 +- app/views/proposals/index.html.haml | 1 + app/views/public_funds/_fund_list.haml | 38 ------ app/views/public_funds/index.haml | 7 -- app/views/public_funds/show.haml | 76 ----------- app/views/public_funds/themed.haml | 7 -- app/views/signup/user.html.haml | 4 +- config/routes.rb | 42 +++---- spec/cells/filter_cell_spec.rb | 21 +++- spec/features/account_spec.rb | 3 +- spec/features/apply_spec.rb | 2 +- spec/features/browse_spec.rb | 27 ++-- spec/features/eligibility_spec.rb | 8 +- spec/features/match_spec.rb | 6 +- spec/features/proposal_spec.rb | 38 +++--- spec/features/public_funds_spec.rb | 72 ----------- spec/features/reveal_spec.rb | 20 +-- spec/features/sessions_spec.rb | 12 +- spec/features/subscriptions_spec.rb | 2 +- spec/policies/enquiry_policy_spec.rb | 118 ++++-------------- spec/policies/fund_policy_spec.rb | 101 ++++----------- 56 files changed, 260 insertions(+), 668 deletions(-) delete mode 100644 app/controllers/public_funds_controller.rb delete mode 100644 app/views/funds/_not_enough_data.html.haml create mode 100644 app/views/funds/_sidebar.haml delete mode 100644 app/views/public_funds/_fund_list.haml delete mode 100644 app/views/public_funds/index.haml delete mode 100644 app/views/public_funds/show.haml delete mode 100644 app/views/public_funds/themed.haml delete mode 100644 spec/features/public_funds_spec.rb diff --git a/app/cells/application/index.slim b/app/cells/application/index.slim index 77aed3bd..3db52e0a 100644 --- a/app/cells/application/index.slim +++ b/app/cells/application/index.slim @@ -14,4 +14,4 @@ div class=[muted] - elsif options[:current_user].subscription_version == 2 = link_to 'Reveal & Apply ❯', reveals_path(fund: options[:fund]), method: :post, class: 'blue' - else - = link_to status[:link_text] || 'Apply ❯', proposal_fund_path(model, options[:fund]), class: 'blue' \ No newline at end of file + = link_to status[:link_text] || 'Apply ❯', fund_path(options[:fund], model), class: 'blue' \ No newline at end of file diff --git a/app/cells/breadcrumb_cell.rb b/app/cells/breadcrumb_cell.rb index 69140065..0de1b2d0 100644 --- a/app/cells/breadcrumb_cell.rb +++ b/app/cells/breadcrumb_cell.rb @@ -1,6 +1,7 @@ class BreadcrumbCell < Cell::ViewModel def show return unless model.is_a?(Hash) && model.present? + model.delete_if { |k, _v| k.nil? } render end diff --git a/app/cells/eligibility/quiz.slim b/app/cells/eligibility/quiz.slim index 56d05e02..024c4c36 100644 --- a/app/cells/eligibility/quiz.slim +++ b/app/cells/eligibility/quiz.slim @@ -2,7 +2,7 @@ h3.mb10 Quiz .uk-form.uk-form-stacked.uk-clearfix - if model.present? - = simple_form_for :check, url: eligibility_proposal_fund_path(model, fund), method: :patch do |f| + = simple_form_for :check, url: eligibility_path(fund, model), method: :patch do |f| - if model.recipient.incomplete_first_proposal? diff --git a/app/cells/filter/show.slim b/app/cells/filter/show.slim index 89efd0d3..7a156f22 100644 --- a/app/cells/filter/show.slim +++ b/app/cells/filter/show.slim @@ -10,4 +10,4 @@ form#filter.flex.flex-wrap .md.inline-block | Grant duration: - = select 'duration', [["all", "All"], ["proposal", "Your proposal (#{@options[:funding_duration]} months)"], ["up-to-2y", "Up to 2 years"], ["2y-plus", "More than 2 years"]] + = select 'duration', [["all", "All"], proposal_duration, ["up-to-2y", "Up to 2 years"], ["2y-plus", "More than 2 years"]].compact diff --git a/app/cells/filter_cell.rb b/app/cells/filter_cell.rb index 1ba065a5..24524f98 100644 --- a/app/cells/filter_cell.rb +++ b/app/cells/filter_cell.rb @@ -14,11 +14,14 @@ def selected?(id, value) def select(id, options) tag.select id: id do options.map do |opt| - unless opt.kind_of?(Array) - opt = [opt, opt.humanize.capitalize] - end + opt = [opt, opt.humanize.capitalize] unless opt.is_a?(Array) tag.option(opt[1], value: url_encode(opt[0]), selected: selected?(id, opt[0])) end.reduce(:+) end end + + def proposal_duration + ['proposal', "Your proposal (#{options[:funding_duration]} months)"] if + options[:funding_duration] + end end diff --git a/app/cells/footer_themes/show.slim b/app/cells/footer_themes/show.slim index 4051a131..e489cf25 100644 --- a/app/cells/footer_themes/show.slim +++ b/app/cells/footer_themes/show.slim @@ -1,7 +1,4 @@ ul - themes.each do |theme| li.mb10 - - if proposal.present? - = link_to theme.name, theme_proposal_funds_path(proposal, theme), class: 'slate' - - else - = link_to theme.name, public_funds_theme_path(theme), class: 'slate' + = link_to theme.name, theme_path(theme, proposal), class: 'slate' diff --git a/app/cells/fund_insight/title.slim b/app/cells/fund_insight/title.slim index 99c788a7..af3d042d 100644 --- a/app/cells/fund_insight/title.slim +++ b/app/cells/fund_insight/title.slim @@ -1,3 +1,3 @@ h3.uk-margin-top - => link_to model.title, proposal_fund_path(proposal, model), class: 'yellow' + => link_to model.title, fund_path(model, proposal), class: 'yellow' span.year.muted= model.subtitle diff --git a/app/cells/fund_insight_cell.rb b/app/cells/fund_insight_cell.rb index d3152572..661e6c8b 100644 --- a/app/cells/fund_insight_cell.rb +++ b/app/cells/fund_insight_cell.rb @@ -31,7 +31,11 @@ def title def themes model.themes.map do |theme| - link_to(theme.name, theme_path(theme), class: "tag #{theme.classes}") + link_to( + theme.name, + theme_path(theme, options[:proposal]), + class: "tag #{theme.classes}" + ) end.join end @@ -89,14 +93,6 @@ def title_name funder.funds.size > 1 ? [name, funder.name] : [funder.name, name] end - def theme_path(theme) - if options[:proposal] - theme_proposal_funds_path(options[:proposal], theme) - else - public_funds_theme_path(theme) - end - end - def grant_types_message costs = model.permitted_costs.reject(&:zero?) .map { |c| FUNDING_TYPES[c][0].split.first.downcase } diff --git a/app/cells/new_proposal_links/dashboard.slim b/app/cells/new_proposal_links/dashboard.slim index 260d1ec7..499dbcf9 100644 --- a/app/cells/new_proposal_links/dashboard.slim +++ b/app/cells/new_proposal_links/dashboard.slim @@ -1,3 +1,3 @@ .fs16.center.py15.border-top.border-lightest-gray - = link_to 'Funds', proposal_funds_path(model), class: 'red px15' + = link_to 'Funds', funds_path(model), class: 'red px15' = link_to 'Edit', edit_proposal_path(model), class: 'blue px15' diff --git a/app/cells/progress/apply.rb b/app/cells/progress/apply.rb index 1e6f525c..87597fd4 100644 --- a/app/cells/progress/apply.rb +++ b/app/cells/progress/apply.rb @@ -17,7 +17,7 @@ def message when ELIGIBLE link_to( 'Apply ❯', - url_helpers.apply_proposal_fund_path(@proposal, @fund), + url_helpers.apply_path(@fund, @proposal), class: 'fs15 btn white bg-blue shadow' ) else diff --git a/app/cells/suitability/index.slim b/app/cells/suitability/index.slim index 0e2fa3d8..1b16c8d4 100644 --- a/app/cells/suitability/index.slim +++ b/app/cells/suitability/index.slim @@ -7,4 +7,4 @@ =< status[:status] - if status[:message] .mb5 = status[:message] -= link_to status[:link_text], proposal_fund_path(model, fund), class: 'blue' \ No newline at end of file += link_to status[:link_text], fund_path(fund, model), class: 'blue' \ No newline at end of file diff --git a/app/cells/suitability/quiz.slim b/app/cells/suitability/quiz.slim index cfe13fd2..e939ef0c 100644 --- a/app/cells/suitability/quiz.slim +++ b/app/cells/suitability/quiz.slim @@ -3,7 +3,7 @@ h4.mb10 .uk-form.uk-form-stacked.uk-clearfix - if model.present? - = simple_form_for :check, url: eligibility_proposal_fund_path(model, fund), method: :patch do |f| + = simple_form_for :check, url: eligibility_path(fund, model), method: :patch do |f| - if model.recipient.incomplete_first_proposal? diff --git a/app/cells/v2_navbar/show.slim b/app/cells/v2_navbar/show.slim index 43994867..26bcc3c4 100644 --- a/app/cells/v2_navbar/show.slim +++ b/app/cells/v2_navbar/show.slim @@ -8,5 +8,5 @@ = link_to 'Sign out', logout_path, class: "#{type} ml35" - else = link_to 'About', about_path, class: "#{type} ml35" - = link_to 'Funds', public_funds_path, class: "#{type} ml35" + = link_to 'Funds', funds_path, class: "#{type} ml35" = link_to 'Sign in', sign_in_path, class: "#{type} ml35" diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7c7b8629..6cb6e429 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -53,7 +53,7 @@ def load_last_proposal @proposal = if params[:proposal_id] @recipient.proposals.find_by(id: params[:proposal_id]) else - @recipient.proposals.last + @recipient.proposals.last # TODO: remove end end @@ -99,10 +99,10 @@ def start_path return edit_signup_recipient_path(@recipient) unless @recipient.valid? # NOTE: legacy return new_signup_proposal_path unless @proposal return new_signup_proposal_path if @proposal.initial? # NOTE: legacy - proposal_funds_path(@proposal) + funds_path(@proposal) end def ensure_not_signed_up - redirect_to proposal_funds_path(@proposal) if signed_up? + redirect_to funds_path(@proposal) if signed_up? end end diff --git a/app/controllers/eligibilities_controller.rb b/app/controllers/eligibilities_controller.rb index 303ff4d0..ef6511c0 100644 --- a/app/controllers/eligibilities_controller.rb +++ b/app/controllers/eligibilities_controller.rb @@ -16,7 +16,7 @@ def create @recipient.update_funds_checked!(@proposal.eligibility) Assessment.analyse_and_update!(Fund.active, @proposal) # TODO: refactor track_quiz_completion(@fund) - redirect_to proposal_fund_path(@proposal, @fund) + redirect_to fund_path(@fund, @proposal) end end diff --git a/app/controllers/enquiries_controller.rb b/app/controllers/enquiries_controller.rb index 59405935..b3978a5f 100644 --- a/app/controllers/enquiries_controller.rb +++ b/app/controllers/enquiries_controller.rb @@ -23,10 +23,6 @@ def authenticate end def user_not_authorised - if @current_user.subscription_version == 2 - redirect_to account_upgrade_path(@recipient) - else - redirect_to proposal_fund_path(@proposal, @fund) - end + redirect_to account_upgrade_path(@recipient) end end diff --git a/app/controllers/feedback_controller.rb b/app/controllers/feedback_controller.rb index af76be90..9abadea4 100644 --- a/app/controllers/feedback_controller.rb +++ b/app/controllers/feedback_controller.rb @@ -7,7 +7,7 @@ def new @fund = Fund.find_by(slug: @redirect_to_funder) # TODO: refactor return unless current_user.feedbacks.count.positive? - redirect_to proposal_funds_path(@proposal), + redirect_to funds_path(@proposal), alert: "It looks like you've already provided feedback" end @@ -17,7 +17,7 @@ def create if @feedback.save session.delete(:redirect_to_funder) - redirect_to proposal_fund_path(@proposal, @fund), + redirect_to fund_path(@fund, @proposal), notice: "You're a star! Thanks for the feedback." else render :new @@ -34,7 +34,7 @@ def update # TODO: depreceted if @feedback.update_attributes(params.require(:feedback).permit(:price)) flash[:notice] = 'Thanks for the feedback!' redirect_to session.delete(:return_to) || - proposal_funds_path(@proposal) + funds_path(@proposal) else render :edit end diff --git a/app/controllers/funds_controller.rb b/app/controllers/funds_controller.rb index 3be89821..b1ab507a 100644 --- a/app/controllers/funds_controller.rb +++ b/app/controllers/funds_controller.rb @@ -1,5 +1,5 @@ class FundsController < ApplicationController - before_action :ensure_logged_in, :update_legacy_suitability, except: :sources + before_action :update_legacy_suitability, except: :sources before_action :load_fund, only: %i[hidden show] def show @@ -9,7 +9,7 @@ def show end def index - update_analysis(query) + update_analysis(query) if @proposal @funds = query.page(params[:page]) # TODO: refactor @fund_count = query.size @@ -19,25 +19,21 @@ def index def themed @theme = Theme.find_by(slug: params[:theme]) - redirect_to root_path, alert: 'Not found' unless @theme + redirect_to funds_path(@proposal), alert: 'Not found' unless @theme @funds = themed_query.page(params[:page]) @fund_count = themed_query.size # TODO: refactor end def hidden return redirect_to root_path unless @fund - @assessment = @proposal.assessments.where(fund: @fund).first + @assessment = @proposal.assessments.where(fund: @fund).first if @proposal end private def user_not_authorised - if @current_user.subscription_version == 2 - return redirect_to root_path if @fund.nil? - redirect_to hidden_proposal_fund_path(@proposal, @fund) - else - redirect_to account_upgrade_path(@recipient) - end + return redirect_to funds_path(@proposal) if @fund.nil? + redirect_to hidden_path(@fund, @proposal) end def update_analysis(funds) @@ -50,7 +46,7 @@ def update_analysis(funds) end def update_legacy_suitability # TODO: depreceted - @proposal.update_legacy_suitability + @proposal&.update_legacy_suitability end def query @@ -69,6 +65,8 @@ def themed_query end def load_fund - @fund = Fund.includes(:funder).active.find_by_hashid(params[:id]) + @fund = Fund.includes(:funder) + .where("state = 'active' OR state = 'stub'") + .find_by_hashid(params[:id]) end end diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb index c2704aa8..94521106 100644 --- a/app/controllers/proposals_controller.rb +++ b/app/controllers/proposals_controller.rb @@ -19,7 +19,7 @@ def create if @proposal.save Assessment.analyse_and_update!(Fund.active, @proposal) @proposal.next_step! - redirect_to proposal_funds_path(@proposal) + redirect_to funds_path(@proposal) else render :new end @@ -44,20 +44,20 @@ def update fund = Fund.find_by_hashid(session.delete(:return_to)) format.js do if session[:return_to] - render js: "window.location.href = '#{proposal_fund_path(@proposal, fund)}'; + render js: "window.location.href = '#{fund_path(fund, @proposal)}'; $('button[type=submit]').prop('disabled', true) .removeAttr('data-disable-with');" else - render js: "window.location.href = '#{proposal_funds_path(@proposal)}'; + render js: "window.location.href = '#{funds_path(@proposal)}'; $('button[type=submit]').prop('disabled', true) .removeAttr('data-disable-with');" end end format.html do if session[:return_to] - redirect_to proposal_fund_path(@proposal, fund) + redirect_to fund_path(fund, @proposal) else - redirect_to proposal_funds_path(@proposal) + redirect_to funds_path(@proposal) end end else diff --git a/app/controllers/public_funds_controller.rb b/app/controllers/public_funds_controller.rb deleted file mode 100644 index ba2bb219..00000000 --- a/app/controllers/public_funds_controller.rb +++ /dev/null @@ -1,35 +0,0 @@ -class PublicFundsController < ApplicationController - before_action :ensure_logged_out - - def index - @funds = Fund.active.recent.includes(:funder).page(params[:page]) - end - - def show - return redirect_to public_funds_path unless public_fund?(params[:id]) - @fund = Fund.find_by_hashid(params[:id]) - return redirect_to public_funds_path unless @fund - @restrictions = @fund.restrictions.pluck(:category, :details) - end - - def themed - @theme = Theme.find_by(slug: params[:theme]) if params[:theme].present? - @funds = Fund.active - .includes(:funder, :themes) - .where(themes: { id: @theme }) - .page(params[:page]) - redirect_to public_funds_path, alert: 'Not found' if @funds.empty? - end - - private - - def ensure_logged_out - redirect_to root_path if logged_in? - end - - def public_fund?(id) - Fund.active.recent.limit(3).pluck(:id) - .map { |i| HASHID.encode(i) } - .include?(id) - end -end diff --git a/app/controllers/requests_controller.rb b/app/controllers/requests_controller.rb index 53845cb4..a55d869b 100644 --- a/app/controllers/requests_controller.rb +++ b/app/controllers/requests_controller.rb @@ -5,7 +5,7 @@ def create authorize :request fund = Fund.find_by_hashid(params[:fund]) Request.create(fund: fund, recipient: @recipient, message: params[:message]) - redirect_to proposal_fund_path(@proposal, fund) + redirect_to fund_path(fund, @proposal) end private diff --git a/app/controllers/reveals_controller.rb b/app/controllers/reveals_controller.rb index d65c5131..f206b9a6 100644 --- a/app/controllers/reveals_controller.rb +++ b/app/controllers/reveals_controller.rb @@ -6,7 +6,7 @@ def create fund = Fund.find_by_hashid(params[:fund]) @recipient.reveals << fund.slug @recipient.save - redirect_to proposal_fund_path(@proposal, params[:fund]) + redirect_to fund_path(fund, @proposal) end private diff --git a/app/controllers/signup_proposals_controller.rb b/app/controllers/signup_proposals_controller.rb index 5d07bef7..875425a4 100644 --- a/app/controllers/signup_proposals_controller.rb +++ b/app/controllers/signup_proposals_controller.rb @@ -23,7 +23,7 @@ def create if @proposal.save Assessment.analyse_and_update!(Fund.active, @proposal) @proposal.next_step! - redirect_to proposal_funds_path(@proposal) + redirect_to funds_path(@proposal) else render :new end @@ -45,9 +45,9 @@ def update def return_to_path if session[:return_to] - proposal_fund_path(@proposal, session.delete(:return_to)) + fund_path(session.delete(:return_to), @proposal) else - proposal_funds_path(@proposal) + funds_path(@proposal) end end diff --git a/app/policies/enquiry_policy.rb b/app/policies/enquiry_policy.rb index 6b0e00d1..19c07225 100644 --- a/app/policies/enquiry_policy.rb +++ b/app/policies/enquiry_policy.rb @@ -1,19 +1,10 @@ class EnquiryPolicy < ApplicationPolicy def create? - use_subscription_version(__method__) + assessment&.eligibility_status == ELIGIBLE ? _fund_policy_show? : false end private - def v1_create? # TODO: deprecated - return true if user.subscription_active? - _fund_policy_show? ? record.proposal.eligible?(record.fund.slug) : false - end - - def v2_create? - assessment&.eligibility_quiz == 1 ? _fund_policy_show? : false - end - def _fund_policy_show? FundPolicy.new(user, FundContext.new(record.fund, record.proposal)).show? end diff --git a/app/policies/fund_policy.rb b/app/policies/fund_policy.rb index 1ddb5418..753509bb 100644 --- a/app/policies/fund_policy.rb +++ b/app/policies/fund_policy.rb @@ -1,25 +1,9 @@ class FundPolicy < ApplicationPolicy def show? - use_subscription_version(__method__) + return false unless record.fund + return true if record.featured || record.stub? + return false unless record.proposal + return true if user.subscription_active? + user.reveals.include?(record.slug) end - - private - - def v1_show? - return false unless record.fund && record.proposal - return true if record.fund.featured || record.fund.stub? - return true if user.subscription_active? - record.proposal - .suitable_funds - .pluck(0) - .take(RECOMMENDATION_LIMIT) - .include?(record.fund.slug) - end - - def v2_show? - return false unless record.fund - return true if record.featured || record.stub? - return true if user.subscription_active? - user.reveals.include?(record.slug) - end end diff --git a/app/views/enquiries/new.html.haml b/app/views/enquiries/new.html.haml index ea5dc6f0..6c3f7a5b 100644 --- a/app/views/enquiries/new.html.haml +++ b/app/views/enquiries/new.html.haml @@ -2,7 +2,7 @@ %header = cell :v2_navbar, @current_user - = cell :breadcrumb, (@proposal.title? ? @proposal.title : 'Current proposal').upcase_first => nil, 'Funds' => proposal_funds_path(@proposal), @fund.funder.name => nil, @fund.name => proposal_fund_path(@proposal, @fund), 'Apply' => apply_proposal_fund_path(@proposal, @fund) + = cell :breadcrumb, (@proposal.title? ? @proposal.title : 'Current proposal').upcase_first => nil, 'Funds' => funds_path(@proposal), @fund.funder.name => nil, @fund.name => fund_path(@fund, @proposal), 'Apply' => apply_path(@fund, @proposal) :javascript mixpanel.track("View Apply Page", {"Fund": "#{@fund.name}"}); @@ -36,7 +36,7 @@ %section %p If you haven't already, be sure to explore our analysis of the - = link_to "#{@fund.short_name.strip}", proposal_fund_path(@proposal, @fund), class: 'blue' + = link_to "#{@fund.short_name.strip}", fund_path(@fund, @proposal), class: 'blue' fund, otherwise below are a few things to consider before you proceed. %hr.my40 @@ -50,7 +50,7 @@ - if @fund.open_call? = link_to "Apply to #{@fund.title} ❯", - apply_proposal_fund_path(@proposal, @fund), + apply_path(@fund, @proposal), method: :post, target: '_blank', 'data-uk-tooltip': "{pos:'top'}", @@ -61,7 +61,7 @@ .bold.caps.mb10.fs15 Unsolicited applications Please note this fund does not accept unsolicited applications. Please read the fund guidance for more information. = link_to "More information on #{@fund.name.strip}", - apply_proposal_fund_path(@proposal, @fund), + apply_path(@fund, @proposal), method: :post, target: '_blank', 'data-uk-tooltip': "{pos:'top'}", diff --git a/app/views/funds/_fund.haml b/app/views/funds/_fund.haml index 08593a86..15f95b11 100644 --- a/app/views/funds/_fund.haml +++ b/app/views/funds/_fund.haml @@ -3,7 +3,7 @@ .p20 .flex.justify-between .block - %h3.mb5= link_to fund.name, proposal_fund_path(@proposal, fund) + %h3.mb5= link_to fund.name, fund_path(fund, @proposal) .fs15.night by = fund.funder.name @@ -15,7 +15,7 @@ .p20.border-top.border-silver %h6.bold.mb10= cell(:fund_insight, fund, proposal: @proposal).call(:summary) - .night= raw strip_tags(fund.description_html).truncate_words(30, omission: "... #{link_to 'more', proposal_fund_path(@proposal, fund)}") + .night= raw strip_tags(fund.description_html).truncate_words(30, omission: "... #{link_to 'more', fund_path(fund, @proposal)}") .truncate.mt10= cell(:fund_insight, fund, proposal: @proposal).call(:themes) diff --git a/app/views/funds/_fund_redacted.haml b/app/views/funds/_fund_redacted.haml index cd572ee2..9f408d33 100644 --- a/app/views/funds/_fund_redacted.haml +++ b/app/views/funds/_fund_redacted.haml @@ -2,19 +2,15 @@ .p20 .flex.justify-between .block - %h3.mb5= link_to 'Hidden fund', proposal_fund_path(@proposal, fund) + %h3.mb5= link_to 'Hidden fund', fund_path(fund, @proposal) .fs15.night by %span.grey.redacted= scramble_name fund.funder.name - - unless fund.eligibility_status - .block - .btn.fs15.slate.border-silver.disabled New - .p20.border-top.border-silver %h6.bold.mb10= cell(:fund_insight, fund, proposal: @proposal).call(:summary) - .night= raw redact(fund, :description, trim: 30, omission: "... #{link_to 'more', proposal_fund_path(@proposal, fund)}") + .night= raw redact(fund, :description, trim: 30, omission: "... #{link_to 'more', fund_path(fund, @proposal)}") .truncate.mt10= cell(:fund_insight, fund, proposal: @proposal).call(:themes) diff --git a/app/views/funds/_not_enough_data.html.haml b/app/views/funds/_not_enough_data.html.haml deleted file mode 100644 index 6a90a223..00000000 --- a/app/views/funds/_not_enough_data.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -.cta.tight - %strong Data unavailable - %br - = @fund.funder.name - does not publish this information in a way that we can use to provide you with analysis. We are working hard to change this, and remember you can - = link_to 'check your eligibility', proposal_fund_path(@proposal, @fund) - in the meantime. diff --git a/app/views/funds/_sidebar.haml b/app/views/funds/_sidebar.haml new file mode 100644 index 00000000..df68a3ca --- /dev/null +++ b/app/views/funds/_sidebar.haml @@ -0,0 +1,28 @@ +-# TODO: refactor +- if @proposal + .border.rounded.mb20.border-blue + .px10.py10.bg-blue + .fs15.lh20.caps.bold.white + Current Proposal + = render partial: 'proposals/card', locals: { proposal: @proposal, context: 'fund' } +- else + .bg-mint.white.px10.py15.rounded-top + %h3 Check your eligibility and suitability for funds in seconds. + %a.button-wide.button-bottom.white.bg-olive.caps.shadow{ href: root_path } Sign up + +- if @fund_stubs.present? + .mt40 + %h2.mb20.fs18 + Other potential leads + .fs12.lh16.mb20.slate + %p.mb10 + These are funds without the full set of data that + Beehive uses to check eligibility and suitability. + %p.mb10 + They have been selected at random. + %p + Follow the links below to request more information about these funds. + %ul + - @fund_stubs.each do |f| + %li.mb20.fs14.lh16 + = link_to f.funder.name, fund_path(f, @proposal), class: 'blue' \ No newline at end of file diff --git a/app/views/funds/hidden.haml b/app/views/funds/hidden.haml index 471146ac..e5336b2a 100644 --- a/app/views/funds/hidden.haml +++ b/app/views/funds/hidden.haml @@ -2,11 +2,12 @@ %header = cell :v2_navbar, @current_user - = cell :breadcrumb, (@proposal.title? ? @proposal.title : 'Current proposal').upcase_first => nil, 'Funds' => proposal_funds_path(@proposal), 'Hidden funder' => nil, 'Hidden fund' => proposal_fund_path(@proposal, @fund) + = cell(:breadcrumb, (@proposal ? 'Current proposal' : nil) => nil, 'Funds' => funds_path, 'Hidden funder' => nil, 'Hidden fund' => fund_path(@fund)) -:javascript - mixpanel.track('View Fund Page', { "Fund": "#{@fund.hashid}" }); - mixpanel.identify("#{@current_user.id}"); +- if @proposal # TODO: refactor + :javascript + mixpanel.track('View Fund Page', { "Fund": "#{@fund.hashid}" }); + mixpanel.identify("#{@current_user.id}"); %main .maxw1080.mx-auto.px40.mt40 diff --git a/app/views/funds/index.haml b/app/views/funds/index.haml index 93565fb4..240ff81d 100644 --- a/app/views/funds/index.haml +++ b/app/views/funds/index.haml @@ -2,17 +2,18 @@ %header = cell :v2_navbar, @current_user - = cell :breadcrumb, (@proposal.title? ? @proposal.title : 'Current proposal').upcase_first => nil,'Funds' => proposal_funds_path(@proposal) + = cell :breadcrumb, (@proposal ? 'Current proposal' : nil) => nil, 'Funds' => funds_path(@proposal) -:javascript - mixpanel.track('View Funds Page'); - mixpanel.identify("#{@current_user.id}"); - mixpanel.people.set({ - 'Fund Unlocks': "#{@recipient.funds_checked}", - }); +- if @proposal # TODO: refactor + :javascript + mixpanel.track('View Funds Page'); + mixpanel.identify("#{@current_user.id}"); + mixpanel.people.set({ + 'Fund Unlocks': "#{@recipient.funds_checked}", + }); - ga("send", "event", "Eligibility", "Eligible", "#{@proposal.eligible_funds.count}"); - ga("send", "event", "Eligibility", "Ineligible", "#{@proposal.ineligible_funds.count}"); + ga("send", "event", "Eligibility", "Eligible", "#{@proposal.eligible_funds.count}"); + ga("send", "event", "Eligibility", "Ineligible", "#{@proposal.ineligible_funds.count}"); / Google Code for View recommended funds :javascript @@ -29,38 +30,17 @@ %div{ style: "display:inline;" } %img{ alt: "", height: "1", src: "//www.googleadservices.com/pagead/conversion/937505505/?label=LPzOCKWprGMQ4eWEvwM&guid=ON&script=0", style: "border-style:none;", width: "1" } - %main + %h1= params[:pid] .maxw1080.mx-auto.px20.flex.flex-wrap %aside.perc25.md.px20.mb40.to-end-md - .border.rounded.mb20.border-blue - .px10.py10.bg-blue - .fs15.lh20.caps.bold.white - Current Proposal - = render partial: 'proposals/card', locals: { proposal: @proposal, context: 'fund' } - - - if @fund_stubs.present? - .mt40 - %h2.mb20.fs18 - Other potential leads - .fs12.lh16.mb20.slate - %p.mb10 - These are funds without the full set of data that - Beehive uses to check eligibility and suitability. - %p.mb10 - They have been selected at random. - %p - Follow the links below to request more information about these funds. - %ul - - @fund_stubs.each do |f| - %li.mb20.fs14.lh16 - = link_to f.funder.name, proposal_fund_path(@proposal, f), class: "blue" + = render partial: 'sidebar' %section.perc75.md.mb80.px20 %h1.mb20 Funds .mb30 - = cell :filter, params, funding_duration: @proposal.funding_duration + = cell :filter, params, funding_duration: @proposal&.funding_duration %small.slate = pluralize(@fund_count, 'fund') found diff --git a/app/views/funds/show.haml b/app/views/funds/show.haml index 319dc365..6d88072d 100644 --- a/app/views/funds/show.haml +++ b/app/views/funds/show.haml @@ -1,12 +1,13 @@ = content_for :title, @fund.title -:javascript - mixpanel.track('View Fund Page', { "Fund": "#{@fund.name}" }); - mixpanel.identify("#{@current_user.id}"); +- if @proposal # TODO: refactor + :javascript + mixpanel.track('View Fund Page', { "Fund": "#{@fund.name}" }); + mixpanel.identify("#{@current_user.id}"); %header = cell :v2_navbar, @current_user - = cell :breadcrumb, (@proposal.title? ? @proposal.title : 'Current proposal').upcase_first => nil, 'Funds' => proposal_funds_path(@proposal), @fund.funder.name => nil, @fund.name => proposal_fund_path(@proposal, @fund) + = cell :breadcrumb, (@proposal ? 'Current proposal' : nil) => nil, 'Funds' => funds_path(@proposal), @fund.funder.name => nil, @fund.name => fund_path(@fund, @proposal) %main .maxw1080.mx-auto.px40.mt40 diff --git a/app/views/funds/stub.haml b/app/views/funds/stub.haml index 4342947e..e3cb965c 100644 --- a/app/views/funds/stub.haml +++ b/app/views/funds/stub.haml @@ -1,12 +1,13 @@ = content_for :title, @fund.title -:javascript - mixpanel.track('View Fund Page', { "Fund": "#{@fund.name}" }); - mixpanel.identify("#{@current_user.id}"); +- if @proposal # TODO: refactor + :javascript + mixpanel.track('View Fund Page', { "Fund": "#{@fund.name}" }); + mixpanel.identify("#{@current_user.id}"); %header = cell :v2_navbar, @current_user - = cell :breadcrumb, (@proposal.title? ? @proposal.title : 'Current proposal').upcase_first => nil, 'Funds' => proposal_funds_path(@proposal), @fund.funder.name => proposal_fund_path(@proposal, @fund) + = cell :breadcrumb, (@proposal ? 'Current proposal' : nil) => nil, 'Funds' => funds_path(@proposal), @fund.funder.name => fund_path(@fund, @proposal) %main .maxw1080.mx-auto.px40.mt40 diff --git a/app/views/funds/themed.haml b/app/views/funds/themed.haml index 5fd6fb89..deb66514 100644 --- a/app/views/funds/themed.haml +++ b/app/views/funds/themed.haml @@ -2,22 +2,18 @@ %header = cell :v2_navbar, @current_user - = cell :breadcrumb, 'Funds' => proposal_funds_path(@proposal), @theme.name => theme_proposal_funds_path(@proposal, @theme) + = cell :breadcrumb, (@proposal ? 'Current proposal' : nil) => nil, 'Funds' => funds_path(@proposal), @theme.name => theme_path(@theme, @proposal) %main .maxw1080.mx-auto.px20.flex.flex-wrap %aside.perc25.md.px20.mb40.to-end-md - .border.rounded.mb20.border-blue - .px10.py10.bg-blue - .fs15.lh20.caps.bold.white - Current Proposal - = render partial: 'proposals/card', locals: { proposal: @proposal, context: 'fund' } + = render partial: 'sidebar' %section.perc75.md.mb80.px20 %h1.mb20=@theme.name .mb30 - = cell :filter, params, funding_duration: @proposal.funding_duration + = cell :filter, params, funding_duration: @proposal&.funding_duration %small.slate = pluralize(@fund_count, 'fund') found diff --git a/app/views/proposals/_card.html.haml b/app/views/proposals/_card.html.haml index 200018ea..9e83d9b9 100644 --- a/app/views/proposals/_card.html.haml +++ b/app/views/proposals/_card.html.haml @@ -12,7 +12,7 @@ funds checked - if proposal.complete? .fs20.mb40 - = link_to proposal.title.truncate_words(3), proposal_funds_path(proposal), class: 'black' + = link_to proposal.title.truncate_words(3), funds_path(proposal), class: 'black' .fs15.mb20 = number_to_currency(proposal.total_costs, unit: "£", precision: 0) · @@ -37,7 +37,7 @@ = link_to 'Change', proposals_path, class: 'blue px10' = link_to 'New', new_proposal_path, class: 'blue px10' - elsif proposal.registered? || proposal.complete? - = link_to 'Funds', proposal_funds_path(proposal), class: 'red px15' + = link_to 'Funds', funds_path(proposal), class: 'red px15' = link_to 'Edit', edit_proposal_path(proposal), class: 'blue px15' - else = link_to 'Complete', edit_signup_proposal_path(proposal), class: 'blue px15' diff --git a/app/views/proposals/index.html.haml b/app/views/proposals/index.html.haml index 88d7ad05..533698bd 100644 --- a/app/views/proposals/index.html.haml +++ b/app/views/proposals/index.html.haml @@ -27,6 +27,7 @@ = render partial: 'proposals/card', locals: { proposal: proposal, context: 'proposal' } .mb20.md + -# TODO: refactor - if @proposal.complete? - if @recipient.subscribed? = link_to "+ New proposal", new_proposal_path, class: 'button-wide bg-green white fs16 caps' diff --git a/app/views/public_funds/_fund_list.haml b/app/views/public_funds/_fund_list.haml deleted file mode 100644 index 4284786f..00000000 --- a/app/views/public_funds/_fund_list.haml +++ /dev/null @@ -1,38 +0,0 @@ -%main - .maxw1080.mx-auto.px20.flex.flex-wrap - %aside.perc25.md.px20.mb40 - .bg-mint.white.px10.py15.rounded-top - %h3 Check your eligibility and suitability for funds in seconds. - %a.button-wide.button-bottom.white.bg-olive.caps.shadow{ href: root_path } Sign up - - %section.perc75.md.mb80.px20 - %h1.mb30= name - - - @funds.each_with_index do |fund, i| - %article.mb40 - - - if i < funds_to_show && params[:page].to_i < 2 - .mb5 - = link_to fund.title, public_fund_path(fund), class: 'blue fs22 lh30' - .mb5.fs15.lh20.night - = fund.subtitle - %p.mb5.fs15.lh20 - = strip_tags fund.description_html.truncate_words(30) - - - else - .muted - .mb5.blue.fs22.lh30.redacted= scramble_name fund.name - .mb5.fs15.lh20.grey.redacted= scramble_name fund.funder.name - %p.mb5.fs15.lh20= raw redact(fund, :description, trim: 30) - - %p.fs14.lh18 - = cell(:fund_insight, fund).call(:themes) - - - if i == funds_to_show - 1 && params[:page].to_i < 2 - %a{ href: root_path } - .mb40.fs14.white.bg-darken-blue.circle.shadow.inline-block.py5.pr14.pl5.truncate - .fs12.bg-green.caps.bold.circle.inline-block.py3.px5 Preview - Sign in or create a free account to see more - - .center - = paginate @funds diff --git a/app/views/public_funds/index.haml b/app/views/public_funds/index.haml deleted file mode 100644 index d8e66c23..00000000 --- a/app/views/public_funds/index.haml +++ /dev/null @@ -1,7 +0,0 @@ -= content_for :title, 'Funds preview' - -%header - = cell :v2_navbar, @current_user - = cell :breadcrumb, 'Funds' => public_funds_path - -= render partial: 'fund_list', locals: { name: 'Funds', funds_to_show: 3 } diff --git a/app/views/public_funds/show.haml b/app/views/public_funds/show.haml deleted file mode 100644 index 9d2331bc..00000000 --- a/app/views/public_funds/show.haml +++ /dev/null @@ -1,76 +0,0 @@ -= content_for :title, "Preview #{@fund.title}" - -%header - = cell :v2_navbar, @current_user - = cell :breadcrumb, 'Funds' => public_funds_path, @fund.name => public_fund_path - -%main - .maxw1080.mx-auto.px40.mt40 - - .flex.flex-wrap.mb40 - %section.perc50.md.mb40.px20 - %h1.mb10= @fund.name - .mb30 - by - = @fund.funder.name - - .mb30.fs18.night.lh25= raw strip_tags @fund.description_html.truncate_words(30) - - %hr - %h6.my20.slate= cell(:fund_insight, @fund, proposal: @proposal).call(:summary) - %hr.mb30 - - = cell(:fund_insight, @fund, proposal: @proposal).call(:themes) - - %section.perc50.md - .mx-auto.shadow.border.rounded.border-silver.maxw420 - .p20.bg-light-blue - %h5.bold.mb10 Free, personal grant funding advice & eligibility checking. - = link_to 'Sign up', root_path, class: 'btn wide white bg-blue shadow' - - .border-top.border-silver - .flex.items-center.px20.py16.progress-item - .indicator.bot.mr20 - %h5.mr-auto Eligibility - %h6.bold.slate Pending - - .border-top.border-silver - .flex.items-center.px20.py16.progress-item - .indicator.top.bot.mr20 - %h5.mr-auto Suitability - %h6.bold.slate Pending - - .border-top.border-silver - .flex.items-center.px20.py16.progress-item - .indicator.top.mr20 - %h5.mr-auto Apply - %h6.bold.btn.fs15.slate.border-silver.disabled Apply ❯ - - %section.px20.mb80.md - %h2.mb20 Summary - - .mb20.markdown - = raw @fund.description_html - - - if @fund.period_end.present? && @fund.period_end > 3.years.ago - .mt30.night - .fs14.mb10 - = cell(:fund_insight, @fund).call(:data_source) - .mb20 - %strong= period(@fund) - - - unless @fund.open_call? - .bg-ice.rounded.shadow.fs18.lh22.p10.mb20 - .bold.caps.mb10.fs15 Unsolicited applications - Please note this fund does not accept unsolicited applications. Please read the fund guidance for more information. - - %ul.list.pl30 - = cell(:fund_insight, @fund).call(:amount) - = cell(:fund_insight, @fund).call(:costs) - = cell(:fund_insight, @fund).call(:countries) - = cell(:fund_insight, @fund).call(:duration) - = cell(:fund_insight, @fund).call(:grant_count) - = cell(:fund_insight, @fund).call(:award_months) - = cell(:fund_insight, @fund).call(:grant_examples) - - = render partial: 'layouts/sign_up_cta' diff --git a/app/views/public_funds/themed.haml b/app/views/public_funds/themed.haml deleted file mode 100644 index 9670711b..00000000 --- a/app/views/public_funds/themed.haml +++ /dev/null @@ -1,7 +0,0 @@ -= content_for :title, "#{@theme.name} Funds preview" - -%header - = cell :v2_navbar, @current_user - = cell :breadcrumb, {'Funds' => public_funds_path, @theme.name.truncate_words(4) => nil} - -= render partial: 'fund_list', locals: { name: @theme.name, funds_to_show: 1 } diff --git a/app/views/signup/user.html.haml b/app/views/signup/user.html.haml index a8b0ca0e..b4bec94c 100644 --- a/app/views/signup/user.html.haml +++ b/app/views/signup/user.html.haml @@ -68,9 +68,9 @@ %p We’ve an ever growing number of funders and funding themes ranging from the = succeed ',' do - = link_to 'Arts', public_funds_theme_path('arts') + = link_to 'Arts', theme_path('arts') to - = link_to 'Youth', public_funds_theme_path('children-and-young-people') + = link_to 'Youth', theme_path('children-and-young-people') (and everything in between) which you can easily use to narrow your search and find the right funding opportunity. .perc50.md.px40.pb20.mb40 diff --git a/config/routes.rb b/config/routes.rb index 7d3b8b6c..d535cba6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,32 +1,11 @@ Rails.application.routes.draw do - resources :articles, only: [:index, :show] - resources :password_resets, except: [:show, :index, :destroy] + resources :articles, only: %i[index show] + resources :feedback, except: %i[show index destroy] # TODO: remove + resources :password_resets, except: %i[show index destroy] + resources :proposals, except: %i[show destroy] resources :reveals, only: :create resources :requests, only: :create - get '/funds', to: 'public_funds#index', as: 'public_funds' - get '/funds/:id', to: 'public_funds#show', as: 'public_fund' - get '/funds/theme/:theme', to: 'public_funds#themed', as: 'public_funds_theme' - - resources :proposals, except: %i[show destroy] do - resources :funds, only: %i[show index] do - collection do - get '/theme/:theme', to: 'funds#themed', as: 'theme' - end - member do - patch :eligibility, to: 'eligibilities#create' - get :apply, to: 'enquiries#new' - post :apply, to: 'enquiries#create' - get :hidden, to: 'funds#hidden' - end - end - end - - get '/proposals/:id/funds?eligibility=eligible', to: 'funds#index', as: 'eligible_proposal_funds' - get '/proposals/:id/funds?eligibility=ineligible', to: 'funds#index', as: 'ineligible_proposal_funds' - - resources :feedback, except: %i[show index destroy] - # Errors match '/404', to: 'errors#not_found', via: :all match '/410', to: 'errors#gone', via: :all @@ -83,6 +62,19 @@ post '/account/:id/subscription/upgrade', to: 'charges#create' get '/account/:id/subscription/thank-you', to: 'charges#thank_you', as: 'thank_you' + # Funds + get '/funds/(:proposal_id)', to: 'funds#index', as: 'funds' + get '/fund/:id/(:proposal_id)', to: 'funds#show', as: 'fund' + get '/h/fund/:id/(:proposal_id)', to: 'funds#hidden', as: 'hidden' + get '/:theme/funds/(:proposal_id)', to: 'funds#themed', as: 'theme' + + # Eligibilities + patch '/fund/:id/:proposal_id/eligibility', to: 'eligibilities#create', as: 'eligibility' + + # Enquiries + get '/fund/:id/:proposal_id/apply', to: 'enquiries#new', as: 'apply' + post '/fund/:id/:proposal_id/apply', to: 'enquiries#create' + # Microsite get '/check/:slug', to: 'microsites#basics', as: 'microsite_basics' post '/check/:slug', to: 'microsites#check_basics' diff --git a/spec/cells/filter_cell_spec.rb b/spec/cells/filter_cell_spec.rb index cb77757d..70f82fa2 100644 --- a/spec/cells/filter_cell_spec.rb +++ b/spec/cells/filter_cell_spec.rb @@ -1,8 +1,13 @@ require 'rails_helper' describe FilterCell do - before(:each) do - @filter = cell(:filter, sort: 'eligibility', funding_duration: 12).call(:show) + let(:funding_duration) { 12 } + let(:filter) do + cell( + :filter, + { sort: 'eligibility' }, + funding_duration: funding_duration + ).call(:show) end it 'has correct options' do @@ -17,11 +22,19 @@ 'Up to 2 years', 'More than 2 years' ].each do |option| - expect(@filter).to have_text option + expect(filter).to have_text(option) + end + end + + context 'funding_duration missing' do + let(:funding_duration) { nil } + + it 'hides proposal duration option' do + expect(filter).not_to have_text('Your proposal') end end it 'selected option' do - expect(@filter).to have_select('sort', selected: 'Eligibility') + expect(filter).to have_select('sort', selected: 'Eligibility') end end diff --git a/spec/features/account_spec.rb b/spec/features/account_spec.rb index 34b6aa45..a178d956 100644 --- a/spec/features/account_spec.rb +++ b/spec/features/account_spec.rb @@ -47,8 +47,7 @@ fill_in :email, with: 'updates.user@email.com' fill_in :password, with: 'newPa55word' click_button 'Sign in' - expect(current_path) - .to eq proposal_funds_path(@db[:registered_proposal]) + expect(current_path).to eq funds_path(@db[:registered_proposal]) end scenario 'password update optional when updating user profile' diff --git a/spec/features/apply_spec.rb b/spec/features/apply_spec.rb index 9be19866..a7aa0a82 100644 --- a/spec/features/apply_spec.rb +++ b/spec/features/apply_spec.rb @@ -41,7 +41,7 @@ expect(@proposal.enquiries.last.approach_funder_count).to eq 1 expect(current_url).to eq @fund.application_link - visit apply_proposal_fund_path(@proposal, @fund) + visit apply_path(@fund, @proposal) click_link "Apply to #{@fund.title}" expect(@proposal.enquiries.last.approach_funder_count).to eq 2 end diff --git a/spec/features/browse_spec.rb b/spec/features/browse_spec.rb index c3537c8e..eb852217 100644 --- a/spec/features/browse_spec.rb +++ b/spec/features/browse_spec.rb @@ -22,7 +22,7 @@ fill_in :email, with: @db[:user].email fill_in :password, with: @db[:user].password click_button 'Sign in' - expect(current_path).to eq proposal_funds_path(@proposal) + expect(current_path).to eq funds_path(@proposal) end scenario 'When I browse the site, @@ -53,36 +53,35 @@ end it 'hidden' do - visit hidden_proposal_fund_path(@proposal, @fund) - expect(current_path).to eq(proposal_funds_path(@proposal)) + visit hidden_path(@fund, @proposal) + expect(current_path).to eq(funds_path(@proposal)) end it 'revealed' do @proposal.recipient.update(reveals: [@fund.slug]) - visit proposal_fund_path(@proposal, @fund) - expect(current_path).to eq(proposal_funds_path(@proposal)) + visit fund_path(@fund, @proposal) + expect(current_path).to eq(funds_path(@proposal)) end end scenario "Fund stub selection shown on proposal fund page" do @proposal.update_column(:eligibility, @proposal.eligibility.merge( @fund_stubs.first.slug => {'location': true}) ) - visit proposal_funds_path(@proposal) + visit funds_path(@proposal) expect(page).to have_text @fund_stubs.first.funder.name end scenario "When I visit a fund that doesn't exist, I want to be redirected to where I came from and see a message, so I avoid an error and understand what happened" do - visit proposal_fund_path(@proposal, 'missing-fund') - expect(current_path).to eq(proposal_funds_path(@proposal)) + visit fund_path('missing-fund', @proposal) + expect(current_path).to eq(funds_path(@proposal)) end scenario "When I find a funding theme I'm interested in, I want to see similar funds, so I can discover new funding opportunties" do click_link @theme.name, match: :first - expect(current_path) - .to eq(theme_proposal_funds_path(@proposal, @theme.slug)) + expect(current_path).to eq(theme_path(@theme.slug, @proposal)) expect(page).to have_css('h3', count: 6) end @@ -95,17 +94,17 @@ scenario "When I visit a funding theme which isn't listed, I want to see a message and be directed to safety, so I can continue my search" do - visit theme_proposal_funds_path(@proposal, '') + visit theme_path('missing', @proposal) # TODO: v2 flash notices #391 # expect(page.all('body script', visible: false)[0].native.text) # .to have_text 'Fund not found' - expect(current_path).to eq(proposal_funds_path(@proposal)) + expect(current_path).to eq(funds_path(@proposal)) - visit theme_proposal_funds_path(@proposal, 'missing') + visit theme_path('missing', @proposal) # TODO: v2 flash notices #391 # expect(page.all('body script', visible: false)[0].native.text) # .to have_text 'Not found' - expect(current_path).to eq proposal_funds_path(@proposal) + expect(current_path).to eq funds_path(@proposal) end def subscribe_and_visit(path) diff --git a/spec/features/eligibility_spec.rb b/spec/features/eligibility_spec.rb index c494c23b..890a060b 100644 --- a/spec/features/eligibility_spec.rb +++ b/spec/features/eligibility_spec.rb @@ -47,7 +47,7 @@ scenario "When I'm try to access application details before checking eligiblity, I want to be told why I can't access them, so I understand what to do next" do - visit apply_proposal_fund_path(@proposal, @fund) + visit apply_path(@fund, @proposal) expect(current_path).to eq(account_upgrade_path(@proposal.recipient)) end @@ -117,7 +117,7 @@ .answer_priorities(@fund).check_suitability click_link 'Reveal' click_link 'Apply ❯' - expect(current_path).to eq(apply_proposal_fund_path(@proposal, @fund)) + expect(current_path).to eq(apply_path(@fund, @proposal)) end scenario "When I run a check and I'm ineligible, @@ -152,7 +152,7 @@ helper.answer_recipient_restrictions(@fund) .answer_proposal_restrictions(@fund, eligible: false) .check_eligibility - visit proposal_fund_path(@proposal, Fund.last) + visit fund_path(Fund.last, @proposal) helper.check_eligibility # 3 questions previously answered should be checked @@ -167,7 +167,7 @@ .answer_proposal_restrictions(@fund, eligible: false) .check_eligibility click_link 'Reveal' - visit apply_proposal_fund_path(@proposal, @fund) + visit apply_path(@fund, @proposal) expect(current_path).to eq(account_upgrade_path(@proposal.recipient)) end end diff --git a/spec/features/match_spec.rb b/spec/features/match_spec.rb index 79b8652f..748447dc 100644 --- a/spec/features/match_spec.rb +++ b/spec/features/match_spec.rb @@ -213,6 +213,7 @@ def expect_company_lookup(count: 1) scenario "When I complete my first funding proposal, I want to see a shortlist of the most relevant funds, so I feel I've found suitable funding opportunities" do + Fund.last.update(max_org_income_limited: false) helper.submit_user_form! expect(current_path).to eq new_signup_recipient_path @@ -232,11 +233,10 @@ def expect_company_lookup(count: 1) expect(current_path).to eq new_signup_proposal_path helper.submit_proposal_form - expect(current_path).to eq proposal_funds_path(Proposal.last) + expect(current_path).to eq funds_path(Proposal.last) expect(page).to have_text 'Hidden fund', count: 3 click_link 'Hidden fund', match: :first - expect(current_path) - .to eq hidden_proposal_fund_path(Proposal.last, Fund.first) + expect(current_path).to eq hidden_path(Fund.last, Proposal.last) end end diff --git a/spec/features/proposal_spec.rb b/spec/features/proposal_spec.rb index 6ee689f4..42001802 100644 --- a/spec/features/proposal_spec.rb +++ b/spec/features/proposal_spec.rb @@ -21,15 +21,13 @@ I want to be told why I am redirected, so I know what to do next' do [ - proposal_funds_path('missing'), - eligible_proposal_funds_path('missing'), - ineligible_proposal_funds_path('missing'), + funds_path('missing'), account_subscription_path(@recipient), account_upgrade_path(@recipient), edit_proposal_path('missing'), - apply_proposal_fund_path('missing', @fund), - proposal_fund_path('missing', @fund), - theme_proposal_funds_path('missing', @theme.slug), + apply_path(@fund, 'missing'), + fund_path(@fund, 'missing'), + theme_path(@theme.slug, 'missing'), new_feedback_path, edit_feedback_path(1) ].each do |path| @@ -43,7 +41,7 @@ so I can see my results' do expect(current_path).to eq new_signup_proposal_path match.submit_proposal_form - expect(current_path).to eq proposal_funds_path(Proposal.last) + expect(current_path).to eq funds_path(Proposal.last) end context 'registered' do @@ -55,9 +53,7 @@ scenario 'When I create my first proposal, I want to see options to change it or create a new one' do [ - proposal_funds_path(@proposal), - eligible_proposal_funds_path(@proposal), - ineligible_proposal_funds_path(@proposal) + funds_path(@proposal) ].each do |path| visit path expect(page).to have_link(nil, href: edit_proposal_path(@proposal)) @@ -81,7 +77,7 @@ eligibility_helper.complete_proposal click_button 'Update and review recommendations' - expect(current_path).to eq proposal_funds_path(@proposal) + expect(current_path).to eq funds_path(@proposal) # expect upgrade prompt click_link 'New' @@ -95,7 +91,7 @@ # can create multiple proposals click_link 'Continue' - expect(current_path).to eq proposal_funds_path(@proposal) + expect(current_path).to eq funds_path(@proposal) StripeMock.stop end @@ -113,8 +109,8 @@ unauthorised_proposal = @app.instances[:complete_proposal] unauthorised_proposal.update(recipient: create(:recipient)) - visit proposal_funds_path(unauthorised_proposal) - expect(current_path).to eq proposal_funds_path(@proposal) + visit funds_path(unauthorised_proposal) + expect(current_path).to eq funds_path(@proposal) end scenario 'When I try to create a new proposal from the proposals index, @@ -133,7 +129,7 @@ scenario 'When I have funding proposals, I want to be able to edit them, so I make any neccessary changes' do - visit proposal_funds_path(@proposal) + visit funds_path(@proposal) click_link 'Dashboard' expect(current_path).to eq proposals_path @@ -156,7 +152,7 @@ eligibility.complete_proposal click_button 'Update and review recommendations' - expect(current_path).to eq proposal_funds_path(@proposal) + expect(current_path).to eq funds_path(@proposal) end end @@ -203,7 +199,7 @@ @eligibility_helper.complete_proposal click_button 'Save and recommend funders' - expect(current_path).to eq proposal_funds_path(Proposal.last) + expect(current_path).to eq funds_path(Proposal.last) click_link 'Change' expect(page).to have_css '.proposal', count: 2 @@ -215,13 +211,13 @@ proposal1 = @app.instances[:complete_proposal] visit proposals_path click_link 'Funds', match: :first - expect(current_path).to eq proposal_funds_path(proposal1) + expect(current_path).to eq funds_path(proposal1) @app.create_registered_proposal proposal2 = @app.instances[:registered_proposal] visit proposals_path click_link 'Funds', match: :first - expect(current_path).to eq proposal_funds_path(proposal2) + expect(current_path).to eq funds_path(proposal2) end end @@ -230,7 +226,7 @@ so I can update my proposal' do @app.create_initial_proposal proposal = Proposal.last - visit proposal_funds_path(proposal) + visit funds_path(proposal) expect(current_path).to eq new_signup_proposal_path expect(page).to have_text 'Your details are out of date' end @@ -248,7 +244,7 @@ @db[:user].organisation = recipient @db[:user].save @app.sign_in - visit proposal_funds_path(proposal) + visit funds_path(proposal) expect(current_path).to eq edit_signup_recipient_path(recipient) expect(page).to have_text 'Your details are out of date' fill_in :recipient_street_address, with: 'London Road' diff --git a/spec/features/public_funds_spec.rb b/spec/features/public_funds_spec.rb deleted file mode 100644 index 2cfc9cb6..00000000 --- a/spec/features/public_funds_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -require 'rails_helper' - -feature 'PublicFunds' do - before(:each) do - @app.seed_test_db.setup_funds(num: 7, open_data: true) - @public_fund = Fund.last - @private_fund = Fund.first - end - - context 'public_funds#index' do - before(:each) do - visit public_funds_path - end - - scenario 'logged_in? redirects to funds#index' do - @app.create_recipient.with_user.create_registered_proposal.sign_in - proposal = @app.instances[:registered_proposal] - - visit public_funds_path - expect(current_path).to eq proposal_funds_path(proposal) - end - - scenario 'navigate' do - visit root_path - click_link 'Funds' - expect(current_path).to eq public_funds_path - end - - scenario 'public fund visible' do - expect(page).to have_css('a.fs22', count: 3) - end - - scenario 'private fund redacted' do - expect(page).to have_css('.fs22.redacted', count: 3) - end - - scenario 'can click visible fund' do - click_link @public_fund.name - expect(current_path).to eq public_fund_path(@public_fund) - end - - scenario 'page 2 all redacted' do - click_link '2' - expect(page).to have_css('.fs22.redacted', count: 1) - end - end - - context 'public_funds#show' do - scenario 'logged_in? redirects to funds#show' do - @app.create_recipient.with_user.create_registered_proposal.sign_in - proposal = @app.instances[:registered_proposal] - - visit public_fund_path(@public_fund) - expect(current_path).to eq proposal_funds_path(proposal) - end - - scenario 'visit public fund' do - visit public_fund_path(@public_fund) - expect(current_path).to eq public_fund_path(@public_fund) - end - - scenario 'visit private fund' do - visit public_fund_path(@private_fund) - expect(current_path).to eq public_funds_path - end - - scenario 'missing fund' do - visit public_fund_path('missing') - expect(current_path).to eq public_funds_path - end - end -end diff --git a/spec/features/reveal_spec.rb b/spec/features/reveal_spec.rb index 8de5658a..7b39174a 100644 --- a/spec/features/reveal_spec.rb +++ b/spec/features/reveal_spec.rb @@ -18,7 +18,7 @@ context 'v2 subscription' do before(:each) do Subscription.last.update(version: 2) - visit proposal_fund_path(@proposal, @fund) + visit fund_path(@fund, @proposal) end scenario 'reveal button hidden once revealed' do @@ -29,14 +29,14 @@ scenario 'Featured fund does not need to be revealed' do @fund.update(featured: true) - visit proposal_fund_path(@proposal, @fund) + visit fund_path(@fund, @proposal) expect(page).not_to have_link('Reveal') end context 'limit reached' do before(:each) do @recipient.update(reveals: [1, 2, 3]) - visit proposal_fund_path(@proposal, @fund) + visit fund_path(@fund, @proposal) end scenario 'cant reveal fund after reaching limit' do @@ -45,9 +45,9 @@ end scenario 'can browse redacted fund after reaching limit' do - visit proposal_funds_path(@proposal) + visit funds_path(@proposal) click_link('Hidden fund', match: :first) - expect(current_path).to eq(hidden_proposal_fund_path(@proposal, @fund)) + expect(current_path).to eq(hidden_path(@fund, @proposal)) end scenario 'can check eligibility after reaching limit' do @@ -61,19 +61,19 @@ scenario 'can apply once revealed' do create(:eligible, fund: @fund, proposal: @proposal) - visit apply_proposal_fund_path(@proposal, @fund) + visit apply_path(@fund, @proposal) expect(current_path).to eq(account_upgrade_path(@recipient)) @recipient.update(reveals: [@fund.slug]) - visit apply_proposal_fund_path(@proposal, @fund) - expect(current_path).to eq(apply_proposal_fund_path(@proposal, @fund)) + visit apply_path(@fund, @proposal) + expect(current_path).to eq(apply_path(@fund, @proposal)) end scenario 'can apply once subscribed' do create(:eligible, fund: @fund, proposal: @proposal) @recipient.subscribe! - visit apply_proposal_fund_path(@proposal, @fund) - expect(current_path).to eq(apply_proposal_fund_path(@proposal, @fund)) + visit apply_path(@fund, @proposal) + expect(current_path).to eq(apply_path(@fund, @proposal)) end end end diff --git a/spec/features/sessions_spec.rb b/spec/features/sessions_spec.rb index 1d468920..c0e46cd6 100644 --- a/spec/features/sessions_spec.rb +++ b/spec/features/sessions_spec.rb @@ -37,7 +37,7 @@ def expect_path(array) it 'enquiries' do expect_path([ - apply_proposal_fund_path(@proposal, @fund) + apply_path(@fund, @proposal) ]) end @@ -48,16 +48,6 @@ def expect_path(array) ]) end - it 'funds' do - expect_path([ - proposal_funds_path(@proposal), - eligible_proposal_funds_path(@proposal), - ineligible_proposal_funds_path(@proposal), - theme_proposal_funds_path(@proposal, @theme.slug), - proposal_fund_path(@proposal, @fund) - ]) - end - it 'proposals' do expect_path([ proposals_path, diff --git a/spec/features/subscriptions_spec.rb b/spec/features/subscriptions_spec.rb index d16a1a88..8bb23833 100644 --- a/spec/features/subscriptions_spec.rb +++ b/spec/features/subscriptions_spec.rb @@ -175,7 +175,7 @@ scenario 'remaining free checks hidden' do @app.setup_funds @db[:complete_proposal].save! - visit proposal_fund_path(@db[:complete_proposal], Fund.first) + visit fund_path(Fund.first, @db[:complete_proposal]) expect(page).not_to have_button 'Check eligibility (3 left)' end end diff --git a/spec/policies/enquiry_policy_spec.rb b/spec/policies/enquiry_policy_spec.rb index 925f7229..919e6e86 100644 --- a/spec/policies/enquiry_policy_spec.rb +++ b/spec/policies/enquiry_policy_spec.rb @@ -4,110 +4,44 @@ describe EnquiryPolicy do subject { described_class } - let(:version) { 1 } - let(:subscribed) { false } - let(:eligibility) { {} } - let(:suitability) { {} } - - let(:fund) { Fund.new(slug: 'fund', state: 'active') } - let(:proposal) do - Proposal.new(eligibility: eligibility, suitability: suitability) - end let(:reveals) { [] } let(:user) do - instance_double( - User, - subscription_active?: subscribed, - subscription_version: version, - reveals: reveals - ) + instance_double(User, subscription_active?: subscribed, reveals: reveals) end - context 'v1' do # TODO: deprecated - permissions :new?, :create? do - context 'eligible' do - let(:eligibility) do - { fund.slug => { 'quiz' => { 'eligible' => true } } } - end - - it 'fund not recommended denies access' do - is_expected.not_to permit(user, EnquiryContext.new(fund, proposal)) - end - - context 'fund recommended' do - let(:suitability) { { fund.slug => { 'total' => 1 } } } + let(:eligibility) { ELIGIBLE } + let(:assessment) { create(:eligible, eligibility_quiz: eligibility) } + let(:context) { EnquiryContext.new(assessment.fund, assessment.proposal) } - it 'grants access' do - is_expected.to permit(user, EnquiryContext.new(fund, proposal)) - end - end - end - - it 'ineligible denies access' do - is_expected.not_to permit(user, EnquiryContext.new(fund, proposal)) - end - - context 'user subscribed' do - let(:subscribed) { true } - - it 'grants access' do - is_expected.to permit(user, EnquiryContext.new(fund, proposal)) - end - end + permissions :new?, :create? do + it 'denies access if no fund supplied' do + is_expected.not_to permit(user, context) end - end - - context 'v2' do - let(:version) { 2 } - let(:assessment) { create(:assessment, eligibility_quiz: eligibility) } - let(:fund) { assessment.fund } - let(:proposal) { assessment.proposal } - permissions :new?, :create? do - it 'denies access if no fund supplied' do - is_expected.not_to permit(user, EnquiryContext.new(fund, proposal)) - end - - context 'incomplete' do - let(:eligibility) { nil } - - it 'denies access' do - is_expected.not_to permit(user, EnquiryContext.new(fund, proposal)) - end - end - - context 'ineligible' do - let(:eligibility) { 0 } - - it 'denies access' do - is_expected.not_to permit(user, EnquiryContext.new(fund, proposal)) - end - end - - context 'eligible and subscribed' do - let(:eligibility) { 1 } - let(:subscribed) { true } + context 'incomplete' do + let(:eligibility) { INCOMPLETE } + it { is_expected.not_to permit(user, context) } + end - it 'grants access' do - is_expected.to permit(user, EnquiryContext.new(fund, proposal)) - end - end + context 'ineligible' do + let(:eligibility) { INELIGIBLE } + it { is_expected.not_to permit(user, context) } + end - context 'eligible and revealed' do - let(:eligibility) { 1 } - let(:reveals) { [fund.slug] } + context 'eligible and subscribed' do + let(:subscribed) { true } + it { is_expected.to permit(user, context) } + end - it 'grants access' do - is_expected.to permit(user, EnquiryContext.new(fund, proposal)) - end - end + context 'eligible and revealed' do + let(:reveals) { [assessment.fund.slug] } + it { is_expected.to permit(user, context) } + end - context 'fund not revealed' do - it 'denies access' do - is_expected.not_to permit(user, EnquiryContext.new(fund, proposal)) - end - end + context 'fund not revealed' do + it { is_expected.not_to permit(user, context) } end end + end diff --git a/spec/policies/fund_policy_spec.rb b/spec/policies/fund_policy_spec.rb index 17e0dd6b..bba11a23 100644 --- a/spec/policies/fund_policy_spec.rb +++ b/spec/policies/fund_policy_spec.rb @@ -4,96 +4,37 @@ describe FundPolicy do subject { described_class } - let(:version) { 1 } - let(:subscribed) { false } - let(:suitability) { {} } - - let(:fund) { Fund.new(slug: 'fund', state: 'active') } - let(:proposal) { Proposal.new(suitability: suitability) } + let(:fund) { build(:fund) } let(:reveals) { [] } let(:user) do - instance_double( - User, - subscription_active?: subscribed, - subscription_version: version, - reveals: reveals - ) + instance_double(User, subscription_active?: subscribed, reveals: reveals) end + let(:context) { FundContext.new(fund, build(:proposal)) } - context 'v1' do - permissions :show? do - it 'denies access if no fund supplied' do - is_expected.not_to permit(user, FundContext.new(nil, proposal)) - end - - it 'denies access if no proposal supplied' do - is_expected.not_to permit(user, FundContext.new(fund, nil)) - end - - context 'user subscribed' do - let(:subscribed) { true } - - it 'grants access' do - is_expected.to permit(user, FundContext.new(fund, proposal)) - end - end - - context 'fund recommended' do - let(:suitability) { { fund.slug => { 'total' => 1 } } } - - it 'grants access' do - is_expected.to permit(user, FundContext.new(fund, proposal)) - end - end - - context 'fund not recommended' do - it 'denies access' do - is_expected.not_to permit(user, FundContext.new(fund, proposal)) - end - end + permissions :show? do + it 'denies access if no fund supplied' do + is_expected.not_to permit(user, context) end - end - context 'v2' do - let(:version) { 2 } - let(:context) { FundContext.new(fund, proposal) } # TODO: refactor - - permissions :show? do - it 'denies access if no fund supplied' do - is_expected.not_to permit(user, context) - end - - context 'user subscribed' do - let(:subscribed) { true } - - it 'grants access' do - is_expected.to permit(user, context) - end - end - - context 'fund revealed' do - let(:reveals) { [fund.slug] } - - it 'grants access' do - is_expected.to permit(user, context) - end - end + context 'user subscribed' do + let(:subscribed) { true } + it { is_expected.to permit(user, context) } + end - context 'fund revealed and subscribed' do - let(:subscribed) { true } - let(:reveals) { [fund.slug] } + context 'fund revealed' do + let(:reveals) { [fund.slug] } + it { is_expected.to permit(user, context) } + end - it 'grants access' do - is_expected.to permit(user, context) - end - end + context 'fund revealed and subscribed' do + let(:subscribed) { true } + let(:reveals) { [fund.slug] } + it { is_expected.to permit(user, context) } + end - context 'fund not revealed' do - it 'denies access' do - is_expected.not_to permit(user, context) - end - end + context 'fund not revealed' do + it { is_expected.not_to permit(user, context) } end end end From c01f299b7f74d29b1b31afeaa8e1c1b8cac7d2dd Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Tue, 13 Feb 2018 16:29:13 +0000 Subject: [PATCH 08/15] Resolve #605 - Update privacy policy to comply with GDPR --- app/cells/terms_notice/show.slim | 6 ++ app/cells/terms_notice_cell.rb | 5 ++ app/controllers/signup_controller.rb | 2 +- app/controllers/users_controller.rb | 7 +- app/models/user.rb | 4 +- app/views/funds/index.haml | 10 ++- app/views/pages/privacy.haml | 26 +++++-- config/initializers/global_constants.rb | 5 +- config/routes.rb | 1 + ...180213142055_add_terms_version_to_users.rb | 5 ++ db/schema.rb | 3 +- spec/cells/terms_notice_cell_spec.rb | 23 ++++++ spec/features/match_spec.rb | 2 + spec/features/user_spec.rb | 24 ++++++ spec/models/user_spec.rb | 74 ++++++++----------- 15 files changed, 134 insertions(+), 63 deletions(-) create mode 100644 app/cells/terms_notice/show.slim create mode 100644 app/cells/terms_notice_cell.rb create mode 100644 db/migrate/20180213142055_add_terms_version_to_users.rb create mode 100644 spec/cells/terms_notice_cell_spec.rb create mode 100644 spec/features/user_spec.rb diff --git a/app/cells/terms_notice/show.slim b/app/cells/terms_notice/show.slim new file mode 100644 index 00000000..ef616ac5 --- /dev/null +++ b/app/cells/terms_notice/show.slim @@ -0,0 +1,6 @@ +.mb40.pb20.rounded.border.flex.items-center.justify-between.flex-wrap.border-soft-blue + .pt20.px20 + h5.night We've added a 'Your rights' section to our privacy policy. + .pt20.px20.mx-auto + = link_to 'Agree', agree_path(model), method: :post, class: 'btn blue' + = link_to 'Review', privacy_path(anchor: 'your-rights'), class: 'btn bg-blue white', 'data-turbolinks' => false \ No newline at end of file diff --git a/app/cells/terms_notice_cell.rb b/app/cells/terms_notice_cell.rb new file mode 100644 index 00000000..6902ebd2 --- /dev/null +++ b/app/cells/terms_notice_cell.rb @@ -0,0 +1,5 @@ +class TermsNoticeCell < Cell::ViewModel + def show + render if model && model.terms_version != TERMS_VERSION + end +end diff --git a/app/controllers/signup_controller.rb b/app/controllers/signup_controller.rb index f62268e5..bffe9fc2 100644 --- a/app/controllers/signup_controller.rb +++ b/app/controllers/signup_controller.rb @@ -11,7 +11,7 @@ def user end def create_user - @user = User.new(user_params) + @user = User.new(user_params.merge(terms_version: TERMS_VERSION)) session[:org_type] = @user.org_type session[:charity_number] = @user.charity_number session[:company_number] = @user.company_number diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index b9640d88..26e650f0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,8 +1,6 @@ class UsersController < ApplicationController before_action :ensure_logged_in - def edit; end - def update if @current_user.update(user_params) redirect_to account_path, notice: 'Updated' @@ -10,4 +8,9 @@ def update render :edit end end + + def agree + @current_user.update_column(:terms_version, TERMS_VERSION) + redirect_back(fallback_location: funds_path(@proposal)) + end end diff --git a/app/models/user.rb b/app/models/user.rb index 58644590..0de9eef4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,14 +2,14 @@ class User < ApplicationRecord extend SetterToInteger include RegNoValidations + attr_accessor :org_type, :charity_number, :company_number + scope :recipient, -> { where organisation_type: 'Recipient' } scope :funder, -> { where organisation_type: 'Funder' } belongs_to :organisation, polymorphic: true, optional: true has_many :feedbacks - attr_accessor :org_type, :charity_number, :company_number - validates :org_type, inclusion: { in: (ORG_TYPES.pluck(1) - [-1]), message: 'Please select a valid option' }, on: :create diff --git a/app/views/funds/index.haml b/app/views/funds/index.haml index 240ff81d..e32fe859 100644 --- a/app/views/funds/index.haml +++ b/app/views/funds/index.haml @@ -1,9 +1,5 @@ = content_for :title, 'Funds' -%header - = cell :v2_navbar, @current_user - = cell :breadcrumb, (@proposal ? 'Current proposal' : nil) => nil, 'Funds' => funds_path(@proposal) - - if @proposal # TODO: refactor :javascript mixpanel.track('View Funds Page'); @@ -30,6 +26,10 @@ %div{ style: "display:inline;" } %img{ alt: "", height: "1", src: "//www.googleadservices.com/pagead/conversion/937505505/?label=LPzOCKWprGMQ4eWEvwM&guid=ON&script=0", style: "border-style:none;", width: "1" } +%header + = cell :v2_navbar, @current_user + = cell :breadcrumb, (@proposal ? 'Current proposal' : nil) => nil, 'Funds' => funds_path(@proposal) + %main %h1= params[:pid] .maxw1080.mx-auto.px20.flex.flex-wrap @@ -37,6 +37,8 @@ = render partial: 'sidebar' %section.perc75.md.mb80.px20 + = cell(:terms_notice, @current_user) + %h1.mb20 Funds .mb30 diff --git a/app/views/pages/privacy.haml b/app/views/pages/privacy.haml index 678a9755..ecc4c290 100644 --- a/app/views/pages/privacy.haml +++ b/app/views/pages/privacy.haml @@ -10,9 +10,11 @@ %main %section.maxw790.mx-auto .mx40 + = cell(:terms_notice, @current_user) + %h1.center.mb20 Privacy - %a.blue{ href: '#general-information' } + = link_to('#general-information', data: { turbolinks: false }) do %h3.py20#general-information General Information %p @@ -26,7 +28,7 @@ = succeed ').' do %span.bold "us" - %a.blue{ href: '#information-gathering-and-usage' } + = link_to('#information-gathering-and-usage', data: { turbolinks: false }) do %h3.py20#information-gathering-and-usage Information Gathering and Usage %p.mb20 @@ -49,14 +51,26 @@ %li.mb5 If it is necessary to share information in order to investigate, prevent, or take action regarding illegal activities, suspected fraud, situations involving potential threats to the physical safety of any person, violations of Terms of Service, or as otherwise required by law. %li If we transfer information about you if Beehive is acquired by or merged with another organisation, other than an organisation established by us and controlled by us. In this event, Beehive will notify you before information about you is transferred and becomes subject to a different privacy policy. - %a.blue{ href: '#cookies' } + = link_to('#your-rights', data: { turbolinks: false }) do + %h3.py20#your-rights Your rights + + %p.mb20 + You have the right to delete your account (unless we need to keep details for auditing purposes). You also have the right to access the information we hold on you - this can be done by emailing + = succeed '.' do + = mail_to('support@beehivegiving.org') + + %p.mb20 The data we hold on you will be removed from our systems after a reasonable period following from your last interaction with the service, unless we need to keep the information for legal or auditing purposes. + + %p Beehive uses automated decision making to determine your eligibility and suitability for the funds we hold information on, but the ultimate decision on whether you receive funding continues to be made by the grant-makers themselves through their own processes. + + = link_to('#cookies', data: { turbolinks: false }) do %h3.py20#cookies Cookies %p.mb20 We use cookies to ensure that we give you the best experience on our website. A cookie is a small amount of data, which often includes an anonymous unique identifier, that is sent to your browser from a web site and stored on your computer's hard drive. %p Cookies are required to use the Beehive service. We use cookies to record current session information. If you continue without changing your browser settings, we'll assume that you are happy to receive all cookies on the Beehive website. - %a.blue{ href: '#data-storage' } + = link_to('#data-storage', data: { turbolinks: false }) do %h3.py20#data-storage Data Storage %p.mb20 Beehive uses third-party vendors and hosting partners to provide the Beehive service. Data relating to Users and their organisations is transferred to or mirrored on servers within the European Union. Beehive reserves the right to transfer or mirror data to servers outside the European Union. @@ -65,12 +79,12 @@ %p Beehive will, when applicable, use secure third-party payment service providers and so will not have access to any User’s bank account or payment card information. - %a.blue{ href: '#changes' } + = link_to('#changes', data: { turbolinks: false }) do %h3.py20#changes Changes %p Beehive may periodically update this policy. We will notify you about significant changes in the way we treat personal information by sending a notice to the primary email address specified in your Beehive primary account holder account or by placing a prominent notice on our site. - %a.blue{ href: '#questions' } + = link_to('#questions', data: { turbolinks: false }) do %h3.py20#questions Questions %p diff --git a/config/initializers/global_constants.rb b/config/initializers/global_constants.rb index 860a707d..87cb7e31 100644 --- a/config/initializers/global_constants.rb +++ b/config/initializers/global_constants.rb @@ -1,7 +1,6 @@ HASHID = Hashids.new(ENV['HASHIDS_SALT'] || 'salt', 8, Hashids::DEFAULT_ALPHABET + '-') -RECOMMENDATION_THRESHOLD = 1 -MAX_FREE_LIMIT = 3 -RECOMMENDATION_LIMIT = 6 +MAX_FREE_LIMIT = 3 # TODO: refactor +TERMS_VERSION = Date.new(2018, 02, 13) UNASSESSED = nil ELIGIBLE = 1 diff --git a/config/routes.rb b/config/routes.rb index d535cba6..d62fe998 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -53,6 +53,7 @@ match '/granted_access/(:unlock_token)', to: 'signup#granted_access', via: :get, as: 'granted_access' # Account + post '/agree/:id', to: 'users#agree', as: 'agree' get '/account', to: 'users#edit', as: 'account' patch '/account', to: 'users#update' get '/account/:id', to: 'recipients#edit', as: 'account_organisation' diff --git a/db/migrate/20180213142055_add_terms_version_to_users.rb b/db/migrate/20180213142055_add_terms_version_to_users.rb new file mode 100644 index 00000000..f93554e9 --- /dev/null +++ b/db/migrate/20180213142055_add_terms_version_to_users.rb @@ -0,0 +1,5 @@ +class AddTermsVersionToUsers < ActiveRecord::Migration[5.1] + def change + add_column :users, :terms_version, :date + end +end diff --git a/db/schema.rb b/db/schema.rb index 88d78d26..e1708452 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.define(version: 20180209113135) do +ActiveRecord::Schema.define(version: 20180213142055) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -507,6 +507,7 @@ t.boolean "agree_to_terms" t.boolean "authorised", default: true t.string "unlock_token" + t.date "terms_version" t.index ["organisation_id"], name: "index_users_on_organisation_id" t.index ["organisation_type"], name: "index_users_on_organisation_type" end diff --git a/spec/cells/terms_notice_cell_spec.rb b/spec/cells/terms_notice_cell_spec.rb new file mode 100644 index 00000000..379e01cb --- /dev/null +++ b/spec/cells/terms_notice_cell_spec.rb @@ -0,0 +1,23 @@ +require 'rails_helper' + +describe TermsNotice do + controller ApplicationController + + subject { cell(:terms_notice, user).call(:show) } + + let(:user) { build(:user, id: 1) } + + context 'outdated agreement' do + it { is_expected.to have_text('Your rights') } + end + + context 'logged out' do + let(:user) { nil } + it { is_expected.not_to have_text('Your rights') } + end + + context 'already agreed' do + let(:user) { build(:user, id: 1, terms_version: TERMS_VERSION) } + it { is_expected.not_to have_text('Your rights') } + end +end diff --git a/spec/features/match_spec.rb b/spec/features/match_spec.rb index 748447dc..c2741cab 100644 --- a/spec/features/match_spec.rb +++ b/spec/features/match_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' require_relative '../support/match_helper' +# TODO: refactor feature 'Match' do let(:helper) { MatchHelper.new } @@ -215,6 +216,7 @@ def expect_company_lookup(count: 1) so I feel I've found suitable funding opportunities" do Fund.last.update(max_org_income_limited: false) helper.submit_user_form! + expect(User.last.terms_version).to eq(TERMS_VERSION) expect(current_path).to eq new_signup_recipient_path { diff --git a/spec/features/user_spec.rb b/spec/features/user_spec.rb new file mode 100644 index 00000000..15cbb838 --- /dev/null +++ b/spec/features/user_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +feature 'User' do + # TODO: refactor + before do + @app.seed_test_db + .create_recipient + .with_user + .create_registered_proposal + .sign_in + end + + it 'can agree to new terms' do + visit funds_path + click_link('Agree') + expect(page).not_to have_text('Your rights') + end + + it 'already agreed to new terms' do + User.last.update_column(:terms_version, TERMS_VERSION) + visit funds_path + expect(page).not_to have_text('Your rights') + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 3cdc9187..b9a2628f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -3,66 +3,52 @@ require 'shared/setter_to_integer' describe User do - before(:each) do - @app.create_recipient.with_user - @db = @app.instances - @user = @db[:user] - @recipient = @db[:recipient] - end + subject { build(:user) } - include_examples 'reg no validations' do - subject { @user } - end + include_examples 'reg no validations' include_examples 'setter to integer' do let(:attributes) { [:org_type] } end - it 'belongs to Recipient' do - expect(@user.organisation).to eq @recipient - expect(@user.organisation_type).to eq 'Recipient' - end - - it 'belongs to Funder' do - funder = create(:funder) - @user.update(organisation: funder) - expect(@user.organisation).to eq funder - expect(@user.organisation_type).to eq 'Funder' + it('belongs to Organisation') do + assoc(:organisation, :belongs_to, polymorphic: true, optional: true) end - it 'has many feedbacks' do - 2.times { create(:feedback, user: @user) } - expect(@user.feedbacks.count).to eq 2 - end + it('has many Feedbacks') { assoc(:feedbacks, :has_many) } - it 'email is unique' do - expect(build(:user, email: @user.email)) - end + it { is_expected.to be_valid } - it 'a valid user' do - expect(@user).to be_valid - end + context 'email' do + it 'unique' do + subject.save + expect(build(:user, email: subject.email)).not_to be_valid + end - it 'invalid name' do - expect(build(:user, first_name: ':Name!')).not_to be_valid - expect(build(:user, last_name: ':Name!')).not_to be_valid + it 'downcase' do + subject.send(:email=, 'UPCASE@email.com') + expect(subject.email).to eq('upcase@email.com') + end end - it 'capitalize name and strip whitespace' do - @user.first_name = ' john ' - @user.save! - expect(@user.first_name).to eq 'John' - end + context 'name' do + it 'invalid' do + %i[first_name last_name].each do |col| + subject.send("#{col}=", ':Name!') + expect(subject).not_to be_valid + end + end - it 'email is downcased' do - @user.email = 'UPCASE@email.com' - @user.save! - expect(@user.email).to eq 'upcase@email.com' + it 'capitalize name and strip whitespace' do + %i[first_name last_name].each do |col| + subject.send("#{col}=", ' john ') + expect(subject[col]).to eq('John') + end + end end it 'org_type converted to integer' do - @user.org_type = '0' - @user.save! - expect(@user.org_type).to eq 0 + subject.send(:org_type=, '0') + expect(subject.org_type).to eq(0) end end From cafcc82acb38a899fe522dd2e2b28bdabd725ce5 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Fri, 16 Feb 2018 17:42:57 +0000 Subject: [PATCH 09/15] Style current_proposal and filter cells on funds#index --- app/assets/stylesheets/v2/custom.sass | 6 +-- app/assets/stylesheets/v2/modules/colors.scss | 4 ++ app/cells/current_proposal/show.slim | 6 +++ app/cells/current_proposal_cell.rb | 32 +++++++++++++++ app/cells/filter/show.slim | 6 +-- app/cells/progress/show.slim | 7 +--- app/cells/progress_cell.rb | 22 ---------- app/cells/terms_notice/show.slim | 9 ++-- app/javascript/__tests__/filter.test.js | 34 +++++++++------ app/javascript/modules/filter.js | 31 ++++++++------ app/javascript/packs/application.js | 6 +-- app/views/funds/_sidebar.haml | 41 +++++++------------ app/views/funds/index.haml | 34 +++++++++++---- spec/cells/current_proposal_cell_spec.rb | 33 +++++++++++++++ spec/cells/filter_cell_spec.rb | 6 +-- spec/features/proposal_spec.rb | 15 +++---- 16 files changed, 176 insertions(+), 116 deletions(-) create mode 100644 app/cells/current_proposal/show.slim create mode 100644 app/cells/current_proposal_cell.rb create mode 100644 spec/cells/current_proposal_cell_spec.rb diff --git a/app/assets/stylesheets/v2/custom.sass b/app/assets/stylesheets/v2/custom.sass index 30081591..ff00ac73 100644 --- a/app/assets/stylesheets/v2/custom.sass +++ b/app/assets/stylesheets/v2/custom.sass @@ -115,18 +115,18 @@ article.featured .copy text-align: left -#filter +.filter select appearance: none -webkit-appearance: none -moz-appearance: none border: none - overflow: hidden outline: none font-size: 15px + line-height: 15px color: map-get($colors, 'blue') background: none - min-width: 63px + max-width: 80px margin: 0 5px &:hover cursor: pointer diff --git a/app/assets/stylesheets/v2/modules/colors.scss b/app/assets/stylesheets/v2/modules/colors.scss index 297ac798..e9af089a 100644 --- a/app/assets/stylesheets/v2/modules/colors.scss +++ b/app/assets/stylesheets/v2/modules/colors.scss @@ -4,6 +4,7 @@ $colors: ( night: #515E70, slate: #7A8799, 'grey': #ACB2BF, + mist: #DFE2E6, 'silver': #E9ECED, ice: #F6F9FC, 'white': $base-bg-color, @@ -18,6 +19,9 @@ $colors: ( soft-blue: #94BEF7, soft-yellow: #FFE3A6, + // Pale + pale-blue: #D7EAFC, + // Light light-blue: #F2F9FF, light-yellow: #FFFDF9, diff --git a/app/cells/current_proposal/show.slim b/app/cells/current_proposal/show.slim new file mode 100644 index 00000000..30996d4a --- /dev/null +++ b/app/cells/current_proposal/show.slim @@ -0,0 +1,6 @@ +.p20 + .flex.justify-between.mb10 + h5.bold Current proposal + = link_to 'Change', proposals_path, class: 'fs15' + .fs15 + = proposal_summary \ No newline at end of file diff --git a/app/cells/current_proposal_cell.rb b/app/cells/current_proposal_cell.rb new file mode 100644 index 00000000..927daf30 --- /dev/null +++ b/app/cells/current_proposal_cell.rb @@ -0,0 +1,32 @@ +class CurrentProposalCell < Cell::ViewModel + include ActionView::Helpers::NumberHelper + + private + def total_costs + number_to_currency(model&.total_costs, unit: '£', precision: 0) + end + + def funding_type + { 1 => 'Capital', 2 => 'Revenue' }[model.funding_type] + end + + def title + model.title.truncate_words(3) if model.complete? + end + + def incompelte + incomplete = { INELIGIBLE => 0, INCOMPLETE => 0, ELIGIBLE => 0 }.merge( + model.assessments.group(:eligibility_status).size + )[INCOMPLETE] + str = pluralize(incomplete, 'fund') + ' unchecked' + link_to(str, funds_path(model, { eligibility: 'to_check' })) + end + + def proposal_summary + if model + [total_costs, funding_type, title, incompelte].compact.join(' • ') + else + 'Assessment missing!' + end + end +end diff --git a/app/cells/filter/show.slim b/app/cells/filter/show.slim index 7a156f22..0d20b358 100644 --- a/app/cells/filter/show.slim +++ b/app/cells/filter/show.slim @@ -1,8 +1,4 @@ -form#filter.flex.flex-wrap - - .md.inline-block.mr10 - | Sort: - = select 'sort', %w[eligibility name] +form.filter.fs15.flex.flex-wrap .md.inline-block.mr10 | Eligibility: diff --git a/app/cells/progress/show.slim b/app/cells/progress/show.slim index 6391e246..bced5d66 100644 --- a/app/cells/progress/show.slim +++ b/app/cells/progress/show.slim @@ -1,10 +1,5 @@ .mx-auto.shadow.border.rounded.border-silver.maxw420 - .p20 - .flex.justify-between.mb10 - h5.bold Current proposal - = link_to 'Change', proposals_path, class: 'fs15' - .fs15 - = proposal_summary + = cell(:current_proposal, model&.proposal) - steps.each do |s| .border-top.border-silver class=s.highlight diff --git a/app/cells/progress_cell.rb b/app/cells/progress_cell.rb index 49a0ea66..574d42fb 100644 --- a/app/cells/progress_cell.rb +++ b/app/cells/progress_cell.rb @@ -1,6 +1,4 @@ class ProgressCell < Cell::ViewModel - include ActionView::Helpers::NumberHelper - private def fund @@ -15,26 +13,6 @@ def eligibility_status model&.eligibility_status end - def total_costs - number_to_currency(proposal&.total_costs, unit: '£', precision: 0) - end - - def funding_type - { 1 => 'Capital', 2 => 'Revenue' }[proposal.funding_type] - end - - def title - proposal.title.truncate_words(3) if proposal.complete? - end - - def proposal_summary - if model - [total_costs, funding_type, title].compact.join(' • ') - else - 'Assessment missing!' - end - end - def steps # TODO: refactor opts = { fund: fund, proposal: proposal, status: eligibility_status } if fund.stub? diff --git a/app/cells/terms_notice/show.slim b/app/cells/terms_notice/show.slim index ef616ac5..37405e52 100644 --- a/app/cells/terms_notice/show.slim +++ b/app/cells/terms_notice/show.slim @@ -1,6 +1,5 @@ -.mb40.pb20.rounded.border.flex.items-center.justify-between.flex-wrap.border-soft-blue - .pt20.px20 - h5.night We've added a 'Your rights' section to our privacy policy. - .pt20.px20.mx-auto +.mb25.px20.py10.border.border-pale-blue.rounded.bg-light-blue.heading.flex.items-center.justify-between + | We've added a 'Your rights' section to our privacy policy. + .flex = link_to 'Agree', agree_path(model), method: :post, class: 'btn blue' - = link_to 'Review', privacy_path(anchor: 'your-rights'), class: 'btn bg-blue white', 'data-turbolinks' => false \ No newline at end of file + = link_to 'Review', privacy_path(anchor: 'your-rights'), class: 'btn bg-blue white', 'data-turbolinks' => false diff --git a/app/javascript/__tests__/filter.test.js b/app/javascript/__tests__/filter.test.js index 9175c61f..125301dd 100644 --- a/app/javascript/__tests__/filter.test.js +++ b/app/javascript/__tests__/filter.test.js @@ -3,24 +3,34 @@ import Filter from '../modules/filter' const filter = new Filter() +const dom = ` +
+ + +
+` test('_parseInputs', () => { - document.body.innerHTML = ` -
- Sort: + document.body.innerHTML = dom + const $form = document.querySelector('.filter') + + expect(filter._parseInputs($form)) + .toEqual('eligibility=eligible&duration=up-to-2y') +}) + +test('_combinedQueryString', () => { + document.body.innerHTML = dom + ` + - -
` - const $form = document.getElementById('sort-filter') - expect(filter._parseInputs($form)) - .toEqual('?sort=name&eligibility=eligible&duration=up-to-2y') + expect(filter._combinedQueryString('.filter')) + .toEqual('?eligibility=eligible&duration=up-to-2y&sort=name') }) diff --git a/app/javascript/modules/filter.js b/app/javascript/modules/filter.js index 138d97c6..86d3c01c 100644 --- a/app/javascript/modules/filter.js +++ b/app/javascript/modules/filter.js @@ -1,23 +1,30 @@ export default class Filter { - init (form) { - this._filterOnChange(form) + init (cls) { + this._filterOnChange(cls) } - _filterOnChange (form) { - const $form = document.getElementById(form) - if (!$form) return + _filterOnChange (cls) { const self = this - $form.addEventListener('change', function (e) { - e.preventDefault() - if (window.Turbolinks) { - window.Turbolinks.visit(self._parseInputs(this)) - } else { - window.location = self._parseInputs(this) + document.addEventListener('change', function (e) { + const $form = e.target.form + if ($form.classList.contains(cls.substring(1))) { + e.preventDefault() + if (window.Turbolinks) { + window.Turbolinks.visit(self._combinedQueryString(cls)) + } else { + window.location = self._combinedQueryString(cls) + } } }) } _parseInputs (form) { - return '?' + [...form.elements].map((el) => `${el.id}=${el.value}`).join('&') + return [...form.elements].map((el) => `${el.id}=${el.value}`).join('&') + } + + _combinedQueryString (cls) { + return '?' + [ + ...document.querySelectorAll(cls) + ].map(f => this._parseInputs(f)).join('&') } } diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index cbb707e3..17cbde62 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -7,7 +7,7 @@ // To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate // layout file, like app/views/layouts/application.html.erb -import "babel-polyfill"; +import 'babel-polyfill' import Filter from '../modules/filter' import Select from '../modules/select' @@ -15,7 +15,7 @@ const filter = new Filter() const select = new Select() document.addEventListener('turbolinks:load', () => { - filter.init('filter') + filter.init('.filter') select.orgType(['user', 'basics_step', 'eligibility_step']) const eligibilityStepOpts = { @@ -45,4 +45,4 @@ window.trackOutboundLink = (url) => { 'transport': 'beacon', 'hitCallback': () => { window.open(url) } }) -} \ No newline at end of file +} diff --git a/app/views/funds/_sidebar.haml b/app/views/funds/_sidebar.haml index df68a3ca..11142b9b 100644 --- a/app/views/funds/_sidebar.haml +++ b/app/views/funds/_sidebar.haml @@ -1,28 +1,15 @@ --# TODO: refactor -- if @proposal - .border.rounded.mb20.border-blue - .px10.py10.bg-blue - .fs15.lh20.caps.bold.white - Current Proposal - = render partial: 'proposals/card', locals: { proposal: @proposal, context: 'fund' } -- else - .bg-mint.white.px10.py15.rounded-top - %h3 Check your eligibility and suitability for funds in seconds. - %a.button-wide.button-bottom.white.bg-olive.caps.shadow{ href: root_path } Sign up - - if @fund_stubs.present? - .mt40 - %h2.mb20.fs18 - Other potential leads - .fs12.lh16.mb20.slate - %p.mb10 - These are funds without the full set of data that - Beehive uses to check eligibility and suitability. - %p.mb10 - They have been selected at random. - %p - Follow the links below to request more information about these funds. - %ul - - @fund_stubs.each do |f| - %li.mb20.fs14.lh16 - = link_to f.funder.name, fund_path(f, @proposal), class: 'blue' \ No newline at end of file + %h2.mb20.fs18 + Other potential leads + .fs12.lh16.mb20.slate + %p.mb10 + These are funds without the full set of data that + Beehive uses to check eligibility and suitability. + %p.mb10 + They have been selected at random. + %p + Follow the links below to request more information about these funds. + %ul + - @fund_stubs.each do |f| + %li.mb20.fs14.lh16 + = link_to f.funder.name, fund_path(f, @proposal), class: 'blue' \ No newline at end of file diff --git a/app/views/funds/index.haml b/app/views/funds/index.haml index e32fe859..e0aae1ee 100644 --- a/app/views/funds/index.haml +++ b/app/views/funds/index.haml @@ -30,22 +30,40 @@ = cell :v2_navbar, @current_user = cell :breadcrumb, (@proposal ? 'Current proposal' : nil) => nil, 'Funds' => funds_path(@proposal) + .maxw1080.mx-auto.px20 + .px20.md + = cell(:terms_notice, @current_user) + %main - %h1= params[:pid] .maxw1080.mx-auto.px20.flex.flex-wrap + %section.perc50.md.px20.mb40 + .rounded.border.border-mist + = cell(:current_proposal, @proposal) + %section.perc50.md.px20.mb40 + .rounded.border.border-mist + .p20 + .flex.justify-between.mb10 + %h5.bold Filter by + = link_to 'Clear all filters', funds_path(@proposal), class: 'fs15' + = cell(:filter, params, funding_duration: @proposal&.funding_duration) + %aside.perc25.md.px20.mb40.to-end-md = render partial: 'sidebar' %section.perc75.md.mb80.px20 - = cell(:terms_notice, @current_user) - - %h1.mb20 Funds + %h1.mb10 Funds - .mb30 - = cell :filter, params, funding_duration: @proposal&.funding_duration - %small.slate + .flex.justify-between.mb40 + .night = pluralize(@fund_count, 'fund') - found + of + = Fund.active.size + + %form.filter.md + Sort: + %select#sort + %option{ value: 'eligibility' } Eligibility + %option{ value: 'name', selected: (params[:sort] == 'name') } Name - @funds.each do |fund| - if policy(FundContext.new(fund, @proposal)).show? diff --git a/spec/cells/current_proposal_cell_spec.rb b/spec/cells/current_proposal_cell_spec.rb new file mode 100644 index 00000000..cd65bcd9 --- /dev/null +++ b/spec/cells/current_proposal_cell_spec.rb @@ -0,0 +1,33 @@ +require 'rails_helper' + +describe CurrentProposalCell do + controller ApplicationController + + subject { cell(:current_proposal, proposal).call(:show) } + + let(:assessment) { create(:incomplete) } + let(:proposal) { assessment.proposal } + + context 'no proposal' do + let(:proposal) { nil } + it { is_expected.to have_text('Assessment missing!') } + end + + it('total_costs') { expect(subject).to have_text('£10,000') } + + it('funding_type') { expect(subject).to have_text('Capital') } + + it 'missing funding_type' do + proposal.funding_type = 0 + expect(subject).not_to have_text('Capital') + end + + context 'title' do + let(:proposal) { build(:complete_proposal) } + it { is_expected.to have_text('Title') } + end + + it('no title') { expect(subject).not_to have_text('Title') } + + it('incomplete count') { expect(subject).to have_text('1 fund unchecked') } +end diff --git a/spec/cells/filter_cell_spec.rb b/spec/cells/filter_cell_spec.rb index 70f82fa2..4bc4bb94 100644 --- a/spec/cells/filter_cell_spec.rb +++ b/spec/cells/filter_cell_spec.rb @@ -5,15 +5,13 @@ let(:filter) do cell( :filter, - { sort: 'eligibility' }, + { eligibility: 'eligible' }, funding_duration: funding_duration ).call(:show) end it 'has correct options' do [ - 'Eligibility', - 'Name', 'All', 'Eligible', 'Ineligible', @@ -35,6 +33,6 @@ end it 'selected option' do - expect(filter).to have_select('sort', selected: 'Eligibility') + expect(filter).to have_select('eligibility', selected: 'Eligible') end end diff --git a/spec/features/proposal_spec.rb b/spec/features/proposal_spec.rb index 42001802..0c999e67 100644 --- a/spec/features/proposal_spec.rb +++ b/spec/features/proposal_spec.rb @@ -52,14 +52,8 @@ scenario 'When I create my first proposal, I want to see options to change it or create a new one' do - [ - funds_path(@proposal) - ].each do |path| - visit path - expect(page).to have_link(nil, href: edit_proposal_path(@proposal)) - expect(page).to have_link 'Change' - expect(page).to have_link 'New' - end + visit funds_path(@proposal) + expect(page).to have_link 'Change' end scenario 'clicking new proposal requires initial proposal to be complete and @@ -69,6 +63,7 @@ stripe = StripeMock.create_test_helper visit root_path + click_link 'Change' click_link 'New' # expect first to be completed @@ -80,6 +75,7 @@ expect(current_path).to eq funds_path(@proposal) # expect upgrade prompt + click_link 'Change' click_link 'New' expect(current_path).to eq account_upgrade_path(@recipient) @@ -91,7 +87,8 @@ # can create multiple proposals click_link 'Continue' - expect(current_path).to eq funds_path(@proposal) + click_link 'New' + expect(current_path).to eq new_proposal_path StripeMock.stop end From f7f149863b4a2554e2d382dc91fd3f51751db979 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Mon, 19 Feb 2018 10:57:18 +0000 Subject: [PATCH 10/15] Add shortcuts to sidebar --- app/assets/stylesheets/v2/custom.sass | 25 +++++ app/assets/stylesheets/v2/modules/layout.sass | 12 --- app/cells/application/analysis.slim | 26 ----- app/cells/application/index.slim | 17 ---- app/cells/application_cell.rb | 95 ------------------- app/controllers/funds_controller.rb | 5 +- app/helpers/funds_helper.rb | 4 + app/views/funds/_sidebar.haml | 25 ++--- app/views/funds/index.haml | 4 +- app/views/funds/themed.haml | 2 +- app/views/proposals/index.html.haml | 2 +- spec/features/browse_spec.rb | 6 +- spec/helpers/funds_helper_spec.rb | 20 ++++ 13 files changed, 65 insertions(+), 178 deletions(-) delete mode 100644 app/cells/application/analysis.slim delete mode 100644 app/cells/application/index.slim delete mode 100644 app/cells/application_cell.rb diff --git a/app/assets/stylesheets/v2/custom.sass b/app/assets/stylesheets/v2/custom.sass index ff00ac73..3e796db9 100644 --- a/app/assets/stylesheets/v2/custom.sass +++ b/app/assets/stylesheets/v2/custom.sass @@ -307,3 +307,28 @@ article.featured bottom: 16px &.bot:after top: 16px + +// Sidebar +// ========================================================================= + +.pills > h4, .pills > h6 + padding: 0 map-get($scales, 5) +.pills a + display: block + width: 100% + padding: map-get($scales, 3) map-get($scales, 5) + margin-bottom: map-get($scales, 5) + color: map-get($colors, night) + border: 1px solid rgba(0, 0, 0, 0) + border-radius: $base-radius + font-size: map-get($scales, 15) + &:hover + background: map-get($colors, light-blue) + border-color: map-get($colors, pale-blue) + opacity: 1 + &.selected + background: map-get($colors, 'blue') + color: map-get($colors, 'white') + font-weight: bold + &:hover + border-color: map-get($colors, 'blue') diff --git a/app/assets/stylesheets/v2/modules/layout.sass b/app/assets/stylesheets/v2/modules/layout.sass index a3725419..c220875a 100644 --- a/app/assets/stylesheets/v2/modules/layout.sass +++ b/app/assets/stylesheets/v2/modules/layout.sass @@ -30,18 +30,6 @@ border-left-color: transparent !important padding-left: 0px !important padding-right: 0px !important -.hide-sm - @media screen and (max-width: 25rem) - display: none -.hide-md - @media screen and (max-width: 46.875rem) - display: none -.to-end-sm - @media screen and (max-width: 25rem) - order: 1 -.to-end-md - @media screen and (max-width: 46.875rem) - order: 1 .fit max-width: 100% diff --git a/app/cells/application/analysis.slim b/app/cells/application/analysis.slim deleted file mode 100644 index b1dbbe1d..00000000 --- a/app/cells/application/analysis.slim +++ /dev/null @@ -1,26 +0,0 @@ -.md.perc33.pl10.mb20.border-left.border-silver class=[muted] - h3.caps.fs14.bold 3. Apply - = model.suitability[options[:fund].slug]&.dig(:quiz, "score") - - .mt10 class=status[:colour] - = status[:symbol] - =<> status[:status] - - - if status[:apply] == 'pending' - span.mt20.button-wide.caps.bg-blue.white =status[:link_text] || "Apply ❯" - - elsif status[:apply] - - if options[:show_fund] - = link_to status[:link_text] || "Apply ❯", apply_proposal_fund_path(model, options[:fund]), class: 'mt20 button-wide caps bg-blue white' - - elsif options[:current_user].subscription_version == 2 - = link_to 'Reveal & Apply ❯', reveals_path(fund: options[:fund]), method: :post, class: 'mt20 button-wide caps bg-blue white' - - else - .mt20.py10.hide-md - |   - - .mt20.fs14.lh16 - = status[:message] - - - unless options[:fund].open_call? - .border-red.border-top-thick.border-bottom-thick.fs16.lh18.py10.mt20 - .bold.caps.mb10.fs15.red Unsolicited applications - | Please note this fund does not accept unsolicited applications. Please read the fund guidance for more information. \ No newline at end of file diff --git a/app/cells/application/index.slim b/app/cells/application/index.slim deleted file mode 100644 index 3db52e0a..00000000 --- a/app/cells/application/index.slim +++ /dev/null @@ -1,17 +0,0 @@ -div class=[muted] - - - if status[:apply] == 'pending' - .blue.mb10.fs14 - =<> status[:symbol] - | Apply - .mb5 - = status[:message] - - elsif status[:apply] - .blue.mb10.fs14 - =<> status[:symbol] - - if options[:show_fund] - = link_to status[:link_text] || 'Apply ❯', apply_proposal_fund_path(model, options[:fund]), class: 'blue' - - elsif options[:current_user].subscription_version == 2 - = link_to 'Reveal & Apply ❯', reveals_path(fund: options[:fund]), method: :post, class: 'blue' - - else - = link_to status[:link_text] || 'Apply ❯', fund_path(options[:fund], model), class: 'blue' \ No newline at end of file diff --git a/app/cells/application_cell.rb b/app/cells/application_cell.rb deleted file mode 100644 index d8fb9993..00000000 --- a/app/cells/application_cell.rb +++ /dev/null @@ -1,95 +0,0 @@ -class ApplicationCell < Cell::ViewModel - - def analysis - muted = (model.eligible_status(options[:fund].slug) == -1 || suitable_status == -1) ? 'muted' : nil - render locals: { fund: options[:fund], status: get_application_status, muted: muted } - end - - def index - muted = (model.eligible_status(options[:fund].slug) == -1 || suitable_status == -1) ? 'muted' : nil - render locals: { fund: options[:fund], status: get_application_status, muted: muted } - end - - private - - def suitable_status - # return -1 if options[:fund].priorities.present? && model.suitability[options[:fund].slug]&.dig(:quiz, "score") == nil - model.suitable_status(options[:fund].slug) - end - - def get_application_status - status = [model.eligible_status(options[:fund].slug), suitable_status] - case status - when [0, 0] # ineligble, unsuitable - # don't bother applying - { - message: "You're both ineligible and unsuitable for this fund", - apply: false, - colour: 'red', - symbol: "\u2718".html_safe, - status: 'Ineligible', - } - when [0, 1], [0, 2] # ineligible, suitable - # could tweak application profile - { - message: "You're ineligible for this fund, but if you changed your proposal you might be suitable", - apply: false, - link_to: "#", - link_text: "Edit my proposal", - colour: 'red', - symbol: "\u2718".html_safe, - status: 'Ineligible', - } - when [0, -1] # ineligible, pending - # don't both applying - { - message: "You're ineligible for this fund", - apply: false, - colour: 'red', - symbol: "\u2718".html_safe, - status: 'Ineligible', - } - when [1, 0] # eligible, unsuitable - { - message: "You're eligible for this fund, but it looks like your proposal is not what they're looking for", - apply: true, - colour: 'yellow', - symbol: "~", - status: 'Unsuitable' - } - when [1, 1], [1, 2] # eligible, suitable - { - message: "You're eligible and suitable for this fund", - apply: true, - colour: 'green', - symbol: "\u2714".html_safe, - status: 'Apply' - } - when [1, -1] # eligible, pending - { - message: "Pending suitability checks", - apply: 'pending', - symbol: "".html_safe, - colour: 'blue', - status: 'Pending' - } - when [-1, -1] # pending, pending - { - message: "Pending eligibility and suitability checks", - apply: 'pending', - symbol: "".html_safe, - colour: 'blue', - status: 'Pending' - } - when [-1, 0], [-1, 1], [-1, 2] # pending, xx - { - message: "Pending eligibility checks", - apply: 'pending', - symbol: "".html_safe, - colour: 'blue', - status: 'Pending' - } - end - end - -end \ No newline at end of file diff --git a/app/controllers/funds_controller.rb b/app/controllers/funds_controller.rb index b1ab507a..abe549a3 100644 --- a/app/controllers/funds_controller.rb +++ b/app/controllers/funds_controller.rb @@ -11,10 +11,7 @@ def show def index update_analysis(query) if @proposal @funds = query.page(params[:page]) - # TODO: refactor - @fund_count = query.size - @fund_stubs = Fund.stubs.includes(:funder).order('RANDOM()').limit(5) - # TODO: end + @fund_count = query.size # TODO: refactor end def themed diff --git a/app/helpers/funds_helper.rb b/app/helpers/funds_helper.rb index ed4261b9..88201cfa 100644 --- a/app/helpers/funds_helper.rb +++ b/app/helpers/funds_helper.rb @@ -1,4 +1,8 @@ module FundsHelper + def selected(value, params = {}) + params['eligibility'] == value ? 'selected' : nil + end + def redact(fund, field, opts = {}) placeholder = '**hidden**' diff --git a/app/views/funds/_sidebar.haml b/app/views/funds/_sidebar.haml index 11142b9b..aa539166 100644 --- a/app/views/funds/_sidebar.haml +++ b/app/views/funds/_sidebar.haml @@ -1,15 +1,10 @@ -- if @fund_stubs.present? - %h2.mb20.fs18 - Other potential leads - .fs12.lh16.mb20.slate - %p.mb10 - These are funds without the full set of data that - Beehive uses to check eligibility and suitability. - %p.mb10 - They have been selected at random. - %p - Follow the links below to request more information about these funds. - %ul - - @fund_stubs.each do |f| - %li.mb20.fs14.lh16 - = link_to f.funder.name, fund_path(f, @proposal), class: 'blue' \ No newline at end of file +.pills + %h4.bold.mb15 Shortcuts + + .mb15= link_to 'All funds', funds_path(@proposal), class: ('selected' unless params['eligibility']) + + %h6.bold.slate.mb10 Eligibility + + = link_to 'Incomplete', funds_path(@proposal, eligibility: 'to_check'), class: selected('to_check', params) + = link_to 'Eligible', funds_path(@proposal, eligibility: 'eligible'), class: selected('eligible', params) + = link_to 'Ineligible', funds_path(@proposal, eligibility: 'ineligible'), class: selected('ineligible', params) diff --git a/app/views/funds/index.haml b/app/views/funds/index.haml index e0aae1ee..a1e6f280 100644 --- a/app/views/funds/index.haml +++ b/app/views/funds/index.haml @@ -47,7 +47,7 @@ = link_to 'Clear all filters', funds_path(@proposal), class: 'fs15' = cell(:filter, params, funding_duration: @proposal&.funding_duration) - %aside.perc25.md.px20.mb40.to-end-md + %aside.perc25.md.px20.mb40 = render partial: 'sidebar' %section.perc75.md.mb80.px20 @@ -59,7 +59,7 @@ of = Fund.active.size - %form.filter.md + %form.filter Sort: %select#sort %option{ value: 'eligibility' } Eligibility diff --git a/app/views/funds/themed.haml b/app/views/funds/themed.haml index deb66514..aef9b6f9 100644 --- a/app/views/funds/themed.haml +++ b/app/views/funds/themed.haml @@ -6,7 +6,7 @@ %main .maxw1080.mx-auto.px20.flex.flex-wrap - %aside.perc25.md.px20.mb40.to-end-md + %aside.perc25.md.px20.mb40 = render partial: 'sidebar' %section.perc75.md.mb80.px20 diff --git a/app/views/proposals/index.html.haml b/app/views/proposals/index.html.haml index 533698bd..8db178fb 100644 --- a/app/views/proposals/index.html.haml +++ b/app/views/proposals/index.html.haml @@ -24,7 +24,7 @@ - @proposals.each do |proposal| .mb20.md .border.rounded.border-silver.proposal - = render partial: 'proposals/card', locals: { proposal: proposal, context: 'proposal' } + = render partial: 'card', locals: { proposal: proposal, context: 'proposal' } .mb20.md -# TODO: refactor diff --git a/spec/features/browse_spec.rb b/spec/features/browse_spec.rb index eb852217..44edab89 100644 --- a/spec/features/browse_spec.rb +++ b/spec/features/browse_spec.rb @@ -64,11 +64,7 @@ end end - scenario "Fund stub selection shown on proposal fund page" do - @proposal.update_column(:eligibility, @proposal.eligibility.merge( @fund_stubs.first.slug => {'location': true}) ) - visit funds_path(@proposal) - expect(page).to have_text @fund_stubs.first.funder.name - end + scenario 'fund stub selection shown on proposal fund page' scenario "When I visit a fund that doesn't exist, I want to be redirected to where I came from and see a message, diff --git a/spec/helpers/funds_helper_spec.rb b/spec/helpers/funds_helper_spec.rb index 6e5434d2..24642bfd 100644 --- a/spec/helpers/funds_helper_spec.rb +++ b/spec/helpers/funds_helper_spec.rb @@ -16,6 +16,26 @@ end let(:field) { :description } + context '#selected' do + let(:selected) { subject.selected(value, 'eligibility' => value) } + let(:value) { 'all' } + + context 'incomplete' do + let(:value) { 'to_check' } + it { expect(selected).to eq('selected') } + end + + context 'incomplete' do + let(:value) { 'eligible' } + it { expect(selected).to eq('selected') } + end + + context 'incomplete' do + let(:value) { 'ineligible' } + it { expect(selected).to eq('selected') } + end + end + context '#redact' do it 'fund name' do expect(subject.redact(fund, field)).not_to have_text('Name') From 67ed58016902fae5db5b990a2fde01b797c0fe6f Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Mon, 19 Feb 2018 13:26:54 +0000 Subject: [PATCH 11/15] Add categories to sidebar --- app/assets/stylesheets/v2/custom.sass | 60 +++++++++++++++++-- app/assets/stylesheets/v2/modules/base.sass | 5 ++ app/assets/stylesheets/v2/modules/border.sass | 8 +-- app/models/theme.rb | 3 + app/views/funds/_sidebar.haml | 17 +++++- spec/models/theme_spec.rb | 7 +++ 6 files changed, 88 insertions(+), 12 deletions(-) diff --git a/app/assets/stylesheets/v2/custom.sass b/app/assets/stylesheets/v2/custom.sass index 3e796db9..cbdc9a19 100644 --- a/app/assets/stylesheets/v2/custom.sass +++ b/app/assets/stylesheets/v2/custom.sass @@ -117,9 +117,7 @@ article.featured .filter select - appearance: none - -webkit-appearance: none - -moz-appearance: none + +prefix(appearance, none, webkit moz) border: none outline: none font-size: 15px @@ -313,7 +311,7 @@ article.featured .pills > h4, .pills > h6 padding: 0 map-get($scales, 5) -.pills a +.pills a, .pills label display: block width: 100% padding: map-get($scales, 3) map-get($scales, 5) @@ -332,3 +330,57 @@ article.featured font-weight: bold &:hover border-color: map-get($colors, 'blue') + +// Accordion +// ========================================================================= + +.accordion + position: relative + margin-bottom: map-get($scales, 5) + width: 100% + overflow: hidden + + input + position: absolute + visibility: hidden + z-index: -1 + + &:checked + label, &:checked + label:hover + background: map-get($colors, 'blue') + color: map-get($colors, 'white') !important + border-color: map-get($colors, 'blue') + + &:checked ~ .accordion-content + max-height: 30em + + &[type=checkbox] + label::after + content: '›' + transform: rotate(90deg) + &[type=checkbox]:checked + label::after + transform-origin: 1px + transform: rotate(270deg) + + label + position: relative + display: block + padding: map-get($scales, 3) map-get($scales, 5) + padding-right: 19px !important + color: map-get($colors, slate) !important + cursor: pointer + font-size: map-get($scales, 15) + border-radius: $base-radius + line-height: 20px + + &:after + position: absolute + right: 0 + top: 50% + display: block + margin: 1px 7px + line-height: 0 + +prefix(transition, all .40s, webkit o) + + .accordion-content + max-height: 0 + overflow: hidden + +prefix(transition, max-height .40s, webkit o) diff --git a/app/assets/stylesheets/v2/modules/base.sass b/app/assets/stylesheets/v2/modules/base.sass index 9f095ecd..5c643e70 100644 --- a/app/assets/stylesheets/v2/modules/base.sass +++ b/app/assets/stylesheets/v2/modules/base.sass @@ -13,6 +13,11 @@ $base-border-width: 1px !default // Mixins // ========================================================================= +=prefix($prop, $val, $prefixes: ()) + @each $prefix in $prefixes + #{'-' + $prefix + '-' + $prop}: $val + #{$prop}: $val + =responsive($prop, $val, $sm-perc-change: 1, $md-perc-change: 1) #{$prop}: $val @media all and (max-width: 46.875em) diff --git a/app/assets/stylesheets/v2/modules/border.sass b/app/assets/stylesheets/v2/modules/border.sass index 312d48f0..eec199e4 100644 --- a/app/assets/stylesheets/v2/modules/border.sass +++ b/app/assets/stylesheets/v2/modules/border.sass @@ -9,13 +9,9 @@ border-radius: 9999px .rounded overflow: hidden - border-radius: $base-radius - -moz-border-radius: $base-radius - -webkit-border-radius: $base-radius + +prefix(border-radius, $base-radius, webkit moz) .rounded-top - border-radius: $base-radius $base-radius 0 0 - -moz-border-radius: $base-radius $base-radius 0 0 - -webkit-border-radius: $base-radius $base-radius 0 0 + +prefix(border-radius, $base-radius $base-radius 0 0, webkit moz) @each $side in border, border-top, border-right, border-bottom, border-left .#{$side} diff --git a/app/models/theme.rb b/app/models/theme.rb index fc2c9c5c..602d90fa 100644 --- a/app/models/theme.rb +++ b/app/models/theme.rb @@ -1,6 +1,9 @@ class Theme < ApplicationRecord + scope :primary, -> { where('parent_id IS NULL') } + belongs_to :parent, class_name: 'Theme', optional: true + has_many :themes, foreign_key: 'parent_id' has_many :fund_themes, dependent: :destroy has_many :funds, through: :fund_themes diff --git a/app/views/funds/_sidebar.haml b/app/views/funds/_sidebar.haml index aa539166..9407e16a 100644 --- a/app/views/funds/_sidebar.haml +++ b/app/views/funds/_sidebar.haml @@ -1,10 +1,23 @@ -.pills +.pills.mb35 %h4.bold.mb15 Shortcuts - .mb15= link_to 'All funds', funds_path(@proposal), class: ('selected' unless params['eligibility']) + .mb15= link_to 'All funds', funds_path(@proposal) %h6.bold.slate.mb10 Eligibility = link_to 'Incomplete', funds_path(@proposal, eligibility: 'to_check'), class: selected('to_check', params) = link_to 'Eligible', funds_path(@proposal, eligibility: 'eligible'), class: selected('eligible', params) = link_to 'Ineligible', funds_path(@proposal, eligibility: 'ineligible'), class: selected('ineligible', params) + +.pills + %h4.bold.mb15 Categories + + - Theme.includes(:themes).primary.each do |theme| + .accordion + %input{ id: theme.slug, type: 'checkbox' } + %label.heading.bold{ for: theme.slug }= theme.name + .accordion-content.pills + = link_to theme.name, theme_path(theme.slug, @proposal) + - theme.themes.each do |child| + = link_to child.name, theme_path(child.slug, @proposal) + %hr.my10.mx5.border-mist diff --git a/spec/models/theme_spec.rb b/spec/models/theme_spec.rb index 91115b58..a9ab7056 100644 --- a/spec/models/theme_spec.rb +++ b/spec/models/theme_spec.rb @@ -5,6 +5,8 @@ it('belongs to parent Theme') { assoc(:parent, :belongs_to) } + it('has many to parent Themes') { assoc(:themes, :has_many) } + it 'has many FundThemes' do assoc(:fund_themes, :has_many, dependent: :destroy) end @@ -18,6 +20,11 @@ is_expected.to be_valid end + it 'self.primary' do + subject.save! + expect(Theme.primary.size).to eq(1) + end + it '#name is unique' do subject.save! expect(build(:theme, name: subject.name)).not_to be_valid From 03526baccd7d550e828bd3614d7a8c0cebb08b88 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Mon, 19 Feb 2018 14:13:38 +0000 Subject: [PATCH 12/15] Themed view new style --- app/controllers/funds_controller.rb | 2 -- app/views/funds/_context.haml | 10 ++++++++++ app/views/funds/_sort.haml | 8 ++++++++ app/views/funds/index.haml | 27 ++++---------------------- app/views/funds/themed.haml | 18 +++++++++-------- app/views/kaminari/_gap.html.erb | 8 ++++++++ app/views/kaminari/_next_page.html.erb | 11 +++++++++++ app/views/kaminari/_page.html.erb | 12 ++++++++++++ app/views/kaminari/_paginator.html.erb | 23 ++++++++++++++++++++++ app/views/kaminari/_prev_page.html.erb | 11 +++++++++++ 10 files changed, 97 insertions(+), 33 deletions(-) create mode 100644 app/views/funds/_context.haml create mode 100644 app/views/funds/_sort.haml create mode 100644 app/views/kaminari/_gap.html.erb create mode 100644 app/views/kaminari/_next_page.html.erb create mode 100644 app/views/kaminari/_page.html.erb create mode 100644 app/views/kaminari/_paginator.html.erb create mode 100644 app/views/kaminari/_prev_page.html.erb diff --git a/app/controllers/funds_controller.rb b/app/controllers/funds_controller.rb index abe549a3..761cca49 100644 --- a/app/controllers/funds_controller.rb +++ b/app/controllers/funds_controller.rb @@ -11,14 +11,12 @@ def show def index update_analysis(query) if @proposal @funds = query.page(params[:page]) - @fund_count = query.size # TODO: refactor end def themed @theme = Theme.find_by(slug: params[:theme]) redirect_to funds_path(@proposal), alert: 'Not found' unless @theme @funds = themed_query.page(params[:page]) - @fund_count = themed_query.size # TODO: refactor end def hidden diff --git a/app/views/funds/_context.haml b/app/views/funds/_context.haml new file mode 100644 index 00000000..2a0c3c61 --- /dev/null +++ b/app/views/funds/_context.haml @@ -0,0 +1,10 @@ +%section.perc50.md.px20.mb40 + .rounded.border.border-mist + = cell(:current_proposal, @proposal) +%section.perc50.md.px20.mb40 + .rounded.border.border-mist + .p20 + .flex.justify-between.mb10 + %h5.bold Filter by + = link_to 'Clear all filters', funds_path(@proposal), class: 'fs15' + = cell(:filter, params, funding_duration: @proposal&.funding_duration) \ No newline at end of file diff --git a/app/views/funds/_sort.haml b/app/views/funds/_sort.haml new file mode 100644 index 00000000..1ccc1b9a --- /dev/null +++ b/app/views/funds/_sort.haml @@ -0,0 +1,8 @@ +.flex.justify-between.mb40 + .night.truncate.pr30= page_entries_info @funds + + %form.filter.flex + Sort: + %select#sort + %option{ value: 'eligibility' } Eligibility + %option{ value: 'name', selected: (params[:sort] == 'name') } Name \ No newline at end of file diff --git a/app/views/funds/index.haml b/app/views/funds/index.haml index a1e6f280..5f8aad4e 100644 --- a/app/views/funds/index.haml +++ b/app/views/funds/index.haml @@ -36,34 +36,15 @@ %main .maxw1080.mx-auto.px20.flex.flex-wrap - %section.perc50.md.px20.mb40 - .rounded.border.border-mist - = cell(:current_proposal, @proposal) - %section.perc50.md.px20.mb40 - .rounded.border.border-mist - .p20 - .flex.justify-between.mb10 - %h5.bold Filter by - = link_to 'Clear all filters', funds_path(@proposal), class: 'fs15' - = cell(:filter, params, funding_duration: @proposal&.funding_duration) + = render partial: 'context' %aside.perc25.md.px20.mb40 = render partial: 'sidebar' - %section.perc75.md.mb80.px20 + %section.perc75.md.mb40.px20 %h1.mb10 Funds - .flex.justify-between.mb40 - .night - = pluralize(@fund_count, 'fund') - of - = Fund.active.size - - %form.filter - Sort: - %select#sort - %option{ value: 'eligibility' } Eligibility - %option{ value: 'name', selected: (params[:sort] == 'name') } Name + = render partial: 'sort' - @funds.each do |fund| - if policy(FundContext.new(fund, @proposal)).show? @@ -71,4 +52,4 @@ - else = render(partial: 'fund_redacted', locals: { fund: fund }) - = paginate @funds + .mb40.center= paginate @funds diff --git a/app/views/funds/themed.haml b/app/views/funds/themed.haml index aef9b6f9..18f37c51 100644 --- a/app/views/funds/themed.haml +++ b/app/views/funds/themed.haml @@ -4,19 +4,21 @@ = cell :v2_navbar, @current_user = cell :breadcrumb, (@proposal ? 'Current proposal' : nil) => nil, 'Funds' => funds_path(@proposal), @theme.name => theme_path(@theme, @proposal) + .maxw1080.mx-auto.px20 + .px20.md + = cell(:terms_notice, @current_user) + %main .maxw1080.mx-auto.px20.flex.flex-wrap + = render partial: 'context' + %aside.perc25.md.px20.mb40 = render partial: 'sidebar' - %section.perc75.md.mb80.px20 - %h1.mb20=@theme.name + %section.perc75.md.mb40.px20 + %h1.mb10= @theme.name - .mb30 - = cell :filter, params, funding_duration: @proposal&.funding_duration - %small.slate - = pluralize(@fund_count, 'fund') - found + = render partial: 'sort' - @funds.each do |fund| - if policy(FundContext.new(fund, @proposal)).show? @@ -24,4 +26,4 @@ - else = render(partial: 'fund_redacted', locals: { fund: fund }) - = paginate @funds + .mb40.center= paginate @funds diff --git a/app/views/kaminari/_gap.html.erb b/app/views/kaminari/_gap.html.erb new file mode 100644 index 00000000..bd7c7cd0 --- /dev/null +++ b/app/views/kaminari/_gap.html.erb @@ -0,0 +1,8 @@ +<%# Non-link tag that stands for skipped pages... + - available local variables + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> +<%= t('views.pagination.truncate').html_safe %> diff --git a/app/views/kaminari/_next_page.html.erb b/app/views/kaminari/_next_page.html.erb new file mode 100644 index 00000000..3c11f01a --- /dev/null +++ b/app/views/kaminari/_next_page.html.erb @@ -0,0 +1,11 @@ +<%# Link to the "Next" page + - available local variables + url: url to the next page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> + + <%= link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, rel: 'next', remote: remote %> + diff --git a/app/views/kaminari/_page.html.erb b/app/views/kaminari/_page.html.erb new file mode 100644 index 00000000..9f336759 --- /dev/null +++ b/app/views/kaminari/_page.html.erb @@ -0,0 +1,12 @@ +<%# Link showing page number + - available local variables + page: a page object for "this" page + url: url to this page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> + + <%= link_to_unless page.current?, page, url, {remote: remote, rel: page.rel} %> + diff --git a/app/views/kaminari/_paginator.html.erb b/app/views/kaminari/_paginator.html.erb new file mode 100644 index 00000000..4b9355be --- /dev/null +++ b/app/views/kaminari/_paginator.html.erb @@ -0,0 +1,23 @@ +<%# The container tag + - available local variables + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote + paginator: the paginator that renders the pagination tags inside +-%> +<%= paginator.render do -%> + +<% end -%> diff --git a/app/views/kaminari/_prev_page.html.erb b/app/views/kaminari/_prev_page.html.erb new file mode 100644 index 00000000..39a20ef7 --- /dev/null +++ b/app/views/kaminari/_prev_page.html.erb @@ -0,0 +1,11 @@ +<%# Link to the "Previous" page + - available local variables + url: url to the previous page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> + + <%= link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, rel: 'prev', remote: remote %> + From 9c20e99d6cb140ccee1bc708f5f8486f4f74d28d Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Mon, 19 Feb 2018 15:00:03 +0000 Subject: [PATCH 13/15] Style fund listings --- app/assets/stylesheets/v2/modules/button.sass | 6 +++-- app/assets/stylesheets/v2/modules/tag.sass | 27 +++++++++---------- app/cells/fund_insight_cell.rb | 2 +- app/views/funds/_fund.haml | 24 ++++++++--------- app/views/funds/_fund_redacted.haml | 18 ++++++------- 5 files changed, 37 insertions(+), 40 deletions(-) diff --git a/app/assets/stylesheets/v2/modules/button.sass b/app/assets/stylesheets/v2/modules/button.sass index 4530ca86..16097a17 100644 --- a/app/assets/stylesheets/v2/modules/button.sass +++ b/app/assets/stylesheets/v2/modules/button.sass @@ -42,6 +42,8 @@ .btn font-weight: bold padding: 6px 12px - &.wide - width: 100% \ No newline at end of file + width: 100% + +.btn-sm + padding: 4px 10px diff --git a/app/assets/stylesheets/v2/modules/tag.sass b/app/assets/stylesheets/v2/modules/tag.sass index c7043a72..c78d2e71 100644 --- a/app/assets/stylesheets/v2/modules/tag.sass +++ b/app/assets/stylesheets/v2/modules/tag.sass @@ -11,31 +11,28 @@ font-family: $heading-family font-size: 14px font-weight: bold - margin: 0 4px 4px 0 - padding: 1px 6px - border-radius: $base-radius .tag-red - background-color: #EF493F + color: #EF493F .tag-pink - background-color: #E42A65 + color: #E42A65 .tag-purple - background-color: #992FAC + color: #992FAC .tag-dark-purple - background-color: #663EB3 + color: #663EB3 .tag-indigo - background-color: #4153B1 + color: #4153B1 .tag-cyan - background-color: #28BBD1 + color: #28BBD1 .tag-teal - background-color: #1E9588 + color: #1E9588 .tag-green - background-color: #53AD57 + color: #53AD57 .tag-light-green - background-color: #8EC056 + color: #8EC056 .tag-amber - background-color: #FFC107 + color: #FFC107 .tag-orange - background-color: #FB992E + color: #FB992E .tag-blue-grey - background-color: #627D8A + color: #627D8A diff --git a/app/cells/fund_insight_cell.rb b/app/cells/fund_insight_cell.rb index 661e6c8b..63f03f12 100644 --- a/app/cells/fund_insight_cell.rb +++ b/app/cells/fund_insight_cell.rb @@ -36,7 +36,7 @@ def themes theme_path(theme, options[:proposal]), class: "tag #{theme.classes}" ) - end.join + end.join('') end def summary diff --git a/app/views/funds/_fund.haml b/app/views/funds/_fund.haml index 15f95b11..f9f80b52 100644 --- a/app/views/funds/_fund.haml +++ b/app/views/funds/_fund.haml @@ -1,22 +1,22 @@ .mb40.border.border-silver.rounded.shadow - .p20 - .flex.justify-between - .block - %h3.mb5= link_to fund.name, fund_path(fund, @proposal) - .fs15.night - by - = fund.funder.name - + .p20.bg-ice + .flex.items-center.mb5 - if fund.featured - .block - .btn.fs15.slate.border-silver.disabled Featured + .mr10 + .btn.btn-sm.fs15.slate.border-silver.disabled Featured + + %h3= link_to fund.name, fund_path(fund, @proposal) + + .fs15.night + by + = fund.funder.name .p20.border-top.border-silver %h6.bold.mb10= cell(:fund_insight, fund, proposal: @proposal).call(:summary) - .night= raw strip_tags(fund.description_html).truncate_words(30, omission: "... #{link_to 'more', fund_path(fund, @proposal)}") + .night= raw strip_tags(fund.description_html).truncate_words(30, omission: "... #{link_to 'more', fund_path(fund, @proposal), class: 'bold'}") - .truncate.mt10= cell(:fund_insight, fund, proposal: @proposal).call(:themes) + .truncate.mt12= cell(:fund_insight, fund, proposal: @proposal).call(:themes) = render(partial: 'status', locals: { fund: fund }) diff --git a/app/views/funds/_fund_redacted.haml b/app/views/funds/_fund_redacted.haml index 9f408d33..c2d02efa 100644 --- a/app/views/funds/_fund_redacted.haml +++ b/app/views/funds/_fund_redacted.haml @@ -1,17 +1,15 @@ -.mb40.border.border-silver.rounded - .p20 - .flex.justify-between - .block - %h3.mb5= link_to 'Hidden fund', fund_path(fund, @proposal) - .fs15.night - by - %span.grey.redacted= scramble_name fund.funder.name +.mb40.border.border-silver.rounded.shadow + .p20.bg-ice + %h3.mb5= link_to 'Hidden fund', fund_path(fund, @proposal) + .fs15.night + Actual fund identity hidden + -# TODO: %a • Why? .p20.border-top.border-silver %h6.bold.mb10= cell(:fund_insight, fund, proposal: @proposal).call(:summary) - .night= raw redact(fund, :description, trim: 30, omission: "... #{link_to 'more', fund_path(fund, @proposal)}") + .night= raw redact(fund, :description, trim: 30, omission: "... #{link_to 'more', fund_path(fund, @proposal), class: 'bold'}") - .truncate.mt10= cell(:fund_insight, fund, proposal: @proposal).call(:themes) + .truncate.mt12= cell(:fund_insight, fund, proposal: @proposal).call(:themes) = render(partial: 'status', locals: { fund: fund }) From dd53584e213a879a170080c99dea6dfa385987e6 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Mon, 19 Feb 2018 15:23:58 +0000 Subject: [PATCH 14/15] Missing proposal notification --- app/cells/current_proposal/show.slim | 9 ++++----- app/cells/current_proposal_cell.rb | 18 +++++++++++++----- spec/cells/current_proposal_cell_spec.rb | 6 +++++- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/app/cells/current_proposal/show.slim b/app/cells/current_proposal/show.slim index 30996d4a..71ee32b8 100644 --- a/app/cells/current_proposal/show.slim +++ b/app/cells/current_proposal/show.slim @@ -1,6 +1,5 @@ -.p20 +.p20 class=('bg-light-yellow' unless model) .flex.justify-between.mb10 - h5.bold Current proposal - = link_to 'Change', proposals_path, class: 'fs15' - .fs15 - = proposal_summary \ No newline at end of file + h5.bold = title + = link_to('Change', proposals_path, class: 'fs15') if model + .fs15.lh18 = proposal_summary \ No newline at end of file diff --git a/app/cells/current_proposal_cell.rb b/app/cells/current_proposal_cell.rb index 927daf30..a9c697f0 100644 --- a/app/cells/current_proposal_cell.rb +++ b/app/cells/current_proposal_cell.rb @@ -2,6 +2,15 @@ class CurrentProposalCell < Cell::ViewModel include ActionView::Helpers::NumberHelper private + + def title + if model + model.complete? ? model.title.truncate_words(3) : 'Current proposal' + else + 'No proposal' + end + end + def total_costs number_to_currency(model&.total_costs, unit: '£', precision: 0) end @@ -10,10 +19,6 @@ def funding_type { 1 => 'Capital', 2 => 'Revenue' }[model.funding_type] end - def title - model.title.truncate_words(3) if model.complete? - end - def incompelte incomplete = { INELIGIBLE => 0, INCOMPLETE => 0, ELIGIBLE => 0 }.merge( model.assessments.group(:eligibility_status).size @@ -26,7 +31,10 @@ def proposal_summary if model [total_costs, funding_type, title, incompelte].compact.join(' • ') else - 'Assessment missing!' + link_to('Sign in', sign_in_path, class: 'bold') + + ' or ' + + link_to('create an account', root_path, class: 'bold') + + ' to check eligibility & suitability.' end end end diff --git a/spec/cells/current_proposal_cell_spec.rb b/spec/cells/current_proposal_cell_spec.rb index cd65bcd9..c1982235 100644 --- a/spec/cells/current_proposal_cell_spec.rb +++ b/spec/cells/current_proposal_cell_spec.rb @@ -10,7 +10,11 @@ context 'no proposal' do let(:proposal) { nil } - it { is_expected.to have_text('Assessment missing!') } + it { is_expected.to have_text('No proposal') } + it { is_expected.to have_link('Sign in') } + it { is_expected.to have_link('create an account') } + it { is_expected.not_to have_link('Change') } + it { is_expected.to have_css('.bg-light-yellow') } end it('total_costs') { expect(subject).to have_text('£10,000') } From 74c1fa4dd1af0c8aea6404fa50ace2b88225c4fb Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Mon, 19 Feb 2018 15:57:36 +0000 Subject: [PATCH 15/15] Proposal notification style changes --- app/assets/stylesheets/v2/custom.sass | 1 - app/cells/current_proposal/show.slim | 2 +- app/cells/current_proposal_cell.rb | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/v2/custom.sass b/app/assets/stylesheets/v2/custom.sass index cbdc9a19..4f6d4856 100644 --- a/app/assets/stylesheets/v2/custom.sass +++ b/app/assets/stylesheets/v2/custom.sass @@ -121,7 +121,6 @@ article.featured border: none outline: none font-size: 15px - line-height: 15px color: map-get($colors, 'blue') background: none max-width: 80px diff --git a/app/cells/current_proposal/show.slim b/app/cells/current_proposal/show.slim index 71ee32b8..047a437d 100644 --- a/app/cells/current_proposal/show.slim +++ b/app/cells/current_proposal/show.slim @@ -2,4 +2,4 @@ .flex.justify-between.mb10 h5.bold = title = link_to('Change', proposals_path, class: 'fs15') if model - .fs15.lh18 = proposal_summary \ No newline at end of file + .fs15.lh20 = proposal_summary \ No newline at end of file diff --git a/app/cells/current_proposal_cell.rb b/app/cells/current_proposal_cell.rb index a9c697f0..d838d693 100644 --- a/app/cells/current_proposal_cell.rb +++ b/app/cells/current_proposal_cell.rb @@ -24,12 +24,12 @@ def incompelte model.assessments.group(:eligibility_status).size )[INCOMPLETE] str = pluralize(incomplete, 'fund') + ' unchecked' - link_to(str, funds_path(model, { eligibility: 'to_check' })) + link_to(str, funds_path(model, eligibility: 'to_check')) end def proposal_summary if model - [total_costs, funding_type, title, incompelte].compact.join(' • ') + [total_costs, funding_type, incompelte].compact.join(' • ') else link_to('Sign in', sign_in_path, class: 'bold') + ' or ' +