Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new campaign bespoke landing page #7137

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added app/assets/images/campaigns/etc_teacher.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/images/campaigns/primary_part_time.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/images/campaigns/secondary_teacher.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/images/campaigns/subjects.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/images/primary_school_uncropped.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ $govuk-assets-path: "/";
@import 'layouts/support_users/feedbacks';
@import 'layouts/updates/index';
@import 'layouts/vacancies/index';
@import 'layouts/vacancies/campaign_landing_page';
@import 'layouts/vacancies/show';

// View Components
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
.vacancies_campaign_landing_page {
@import '../shared/search-results';

.custom-banner-background {
background-color: govuk-colour('light-grey');
width: 100vw;
position: relative;
margin-left: calc(-50vw + 50%);
margin-right: calc(-50vw + 50%);
padding: govuk-spacing(4) 0;
margin-bottom: 30px;
}

.custom-banner {
padding: govuk-spacing(4) 0;
}

.search-results-controls {

@include govuk-media-query($from: tablet) {
margin-bottom: govuk-spacing(4);

.autocomplete, .govuk-form-group {
margin-bottom: 20px;
}

.location-search__radius {
.govuk-select[name='radius'] {
width: min-content;
min-width: 0;
}
}

.autocomplete__menu {
@include govuk-media-query($from: desktop) {
position: absolute;
}
}

&__submit {
.govuk-button {
margin-bottom: 0;
}
}

.govuk-hint {
display: none;
}
}

@include govuk-media-query($until: tablet) {
&__submit {
.govuk-button {
margin-bottom: govuk-spacing(3);
}
}
}

.location-search__radius {
margin-top: govuk-spacing(6);
flex-basis: 200px;

@include govuk-media-query($until: tablet) {
.govuk-select[name='radius'] {
width: 100%;
}
}

@include govuk-media-query($from: tablet) {
margin-top: 0;
}
}
}
}
43 changes: 43 additions & 0 deletions app/controllers/vacancies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ def show
@vacancy = VacancyPresenter.new(vacancy)
end

def campaign_landing_page
@campaign_page = CampaignPage[params[:utm_content]]
@form ||= Jobseekers::SearchForm.new(campaign_search_params.merge(landing_page: @campaign_page))
@jobseeker_name = params[:email_name] || "Jobseeker"
@subject = params[:email_subject] || ""

@vacancies_search = Search::VacancySearch.new(@form.to_hash, sort: @form.sort)
@pagy, @vacancies = pagy(@vacancies_search.vacancies, count: @vacancies_search.total_count)

set_search_coordinates unless do_not_show_distance?
trigger_search_performed_event
end

private

def form
Expand Down Expand Up @@ -48,6 +61,36 @@ def set_headers
response.set_header("X-Robots-Tag", "noarchive")
end

def campaign_search_params
params.permit(:email_name, :email_postcode, :email_location, :email_radius, :email_jobrole, :email_subject,
:email_phase, :email_ECT, :email_fulltime, :email_parttime, :email_jobshare, :email_contact)
.tap do |campaign_params|
map_location_and_radius(campaign_params)
map_teaching_job_roles_subjects_phases(campaign_params)
campaign_params[:working_patterns] = extract_working_patterns(campaign_params)
campaign_params[:ect_statuses] = [campaign_params.delete(:email_ECT)].compact
end
end

def map_location_and_radius(campaign_params)
campaign_params[:location] = campaign_params.delete(:email_location)
campaign_params[:radius] = campaign_params.delete(:email_radius)
end

def map_teaching_job_roles_subjects_phases(campaign_params)
campaign_params[:teaching_job_roles] = [campaign_params.delete(:email_jobrole)].compact
campaign_params[:subjects] = [campaign_params.delete(:email_subject)].compact
campaign_params[:phases] = [campaign_params.delete(:email_phase)].compact
end

def extract_working_patterns(campaign_params)
working_patterns = []
working_patterns << "full_time" if ActiveModel::Type::Boolean.new.cast(campaign_params.delete(:email_fulltime))
working_patterns << "part_time" if ActiveModel::Type::Boolean.new.cast(campaign_params.delete(:email_parttime))
working_patterns << "job_share" if ActiveModel::Type::Boolean.new.cast(campaign_params.delete(:email_jobshare))
working_patterns
end

def trigger_search_performed_event
fail_safe do
event_data = {
Expand Down
24 changes: 24 additions & 0 deletions app/services/campaign_page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class CampaignPage
attr_reader :utm_content_code, :criteria, :banner_image

def self.exists?(utm_content)
Rails.application.config.campaign_pages.key?(utm_content.to_sym)
end

def self.[](utm_content_code)
raise "No such campaign page: '#{utm_content_code}'" unless exists?(utm_content_code)
Copy link
Collaborator

@scruti scruti Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we gracefully handle this so users don't get a service error when the param value is wrong?
I would expect a user to get a 404 page or get redirected to the generic vacancies page.

Oh this seems unreachable due to the route constraint bellow.

  get "campaigns/",
      to: "vacancies#campaign_landing_page",
      as: :campaign_landing_page,
      constraints: ->(request) { CampaignPage.exists?(request.params[:utm_content]) }

Forget my original comment then, this is a good enough safeguard!


criteria = Rails.application.config.campaign_pages[utm_content_code.to_sym].except(:banner_image)
new(utm_content_code, criteria)
end

def initialize(utm_content_code, criteria)
@utm_content_code = utm_content_code.to_s
@banner_image = Rails.application.config.campaign_pages[utm_content_code.to_sym][:banner_image]
@criteria = criteria
end

def banner_title(name, subject = nil)
I18n.t("campaign_pages.#{utm_content_code}.banner_title", name: name, subject: subject)
end
end
63 changes: 63 additions & 0 deletions app/views/vacancies/campaign_landing_page.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
- content_for :skip_links do
= govuk_skip_link(href: "#search-results", text: t("jobs.skip_link_list"))

- content_for :breadcrumbs do
= govuk_breadcrumbs breadcrumbs: organisation_landing_page_breadcrumbs(@vacancies_search.organisation_slug) if @vacancies_search.organisation_slug.present?

= render "vacancies/search/page_title_and_description", landing_page: @landing_page

h1 class="govuk-heading-l" role="heading" aria-level="1"
= t("jobs.search_result_heading", count: number_with_delimiter(@vacancies_search.total_count))

- any_vacancies = @vacancies_search.total_count.positive?
- if @vacancies_search.active_criteria? && any_vacancies
p = t("subscriptions.link.help_text_html", link: govuk_link_to(t("subscriptions.link.text"), new_subscription_path(search_criteria: @vacancies_search.active_criteria, coordinates_present: @vacancies_search.point_coordinates.present?)))

= form_for @form, as: "", url: jobs_path, method: :get, html: { data: { controller: "form" }, role: "search" } do |f|
.custom-banner-background
.govuk-width-container
.custom-banner
.govuk-grid-row
.govuk-grid-column-one-third-at-desktop
h1.govuk-heading-l class="govuk-!-margin-bottom-4" = @campaign_page.banner_title(@jobseeker_name, @subject)
= render "vacancies/search/fields_and_button", f: f, form: @form, show_keyword: false

.govuk-grid-column-two-thirds-at-desktop
img src=image_path(@campaign_page.banner_image) class="govuk-!-margin-top-2" width="90%" height="90%" alt="Primary school classroom with children"

.govuk-grid-row
.govuk-grid-column-one-third-at-desktop class="govuk-!-margin-bottom-3"
= render "vacancies/search/filters", f: f, form: @form, vacancies_search: @vacancies_search
/ When the user refines their search, this helps us figure out if they've changed the
/ keyword(s), or just adjusted the filters
= f.hidden_field :previous_keyword, value: @vacancies_search.keyword
= f.hidden_field :organisation_slug, value: @vacancies_search.organisation_slug

.govuk-grid-column-two-thirds-at-desktop
= render "vacancies/search/open_filters_button", form: @form

#search-results
- if any_vacancies
div class="sort-container"
div class="left"
- sort_by_value = @vacancies_search.sort.options.find { |option| option.by == @vacancies_search.sort.by }.display_name.downcase
- sorted_by_string = t("jobs.sort_by.jobs_page_label", sort_by_value: sort_by_value)
h2 class="govuk-heading-m govuk-!-margin-bottom-3 govuk-!-margin-top-1" = sorted_by_string
div class="right"
= render "vacancies/search/sort", form: @form, vacancies_search: @vacancies_search, vacancies: @vacancies, display: "inline-dropdown"
- if @vacancies_search.location
p = "Jobs in or near #{@vacancies_search.location}"
.govuk-grid-row
.govuk-grid-column-full
hr class="govuk-section-break govuk-section-break--s govuk-section-break--visible govuk-!-margin-bottom-3"

= render "vacancies/search/results", vacancies: @vacancies
- elsif @vacancies_search.organisation_slug
= render "vacancies/search/no_results_organisation", organisation_name: @vacancies_search.organisation.name
- elsif @vacancies_search.active_criteria?
= render "vacancies/search/no_results_suggestions", vacancies_search: @vacancies_search
- else
= render "vacancies/search/no_results"
= govuk_pagination(pagy: @pagy)
- if any_vacancies
= render "vacancies/search/stats"
2 changes: 1 addition & 1 deletion app/views/vacancies/index.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ h1 class="govuk-heading-l" role="heading" aria-level="1"
= f.hidden_field :organisation_slug, value: @vacancies_search.organisation_slug

.govuk-grid-column-two-thirds-at-desktop
= render "vacancies/search/fields_and_button", f: f, form: @form
= render "vacancies/search/fields_and_button", f: f, form: @form, show_keyword: true
= render "vacancies/search/open_filters_button", form: @form

#search-results
Expand Down
4 changes: 2 additions & 2 deletions app/views/vacancies/search/_fields_and_button.html.slim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.search-results-controls

= render "vacancies/search/keyword", f: f

- if local_assigns.fetch(:show_keyword, true)
= render "vacancies/search/keyword", f: f
- unless @vacancies_search.organisation_slug.present?
= render "vacancies/search/location", show_hint: false, f: f
= render "vacancies/search/current_location", target: "location-field"
Expand Down
1 change: 1 addition & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class Application < Rails::Application
config.geocoder_lookup = :default

config.landing_pages = config_for(:landing_pages)
config.campaign_pages = config_for(:campaign_pages)

config.maintenance_mode = ActiveModel::Type::Boolean.new.cast(ENV.fetch("MAINTENANCE_MODE", nil))

Expand Down
134 changes: 134 additions & 0 deletions config/campaign_pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
shared:
whentoapplybespoke+PRIMARY:
banner_image: "campaigns/primary_teacher.jpg"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

teaching_job_roles:
- teacher
phases:
- primary
radius:
- 15
whentoapplybespoke+SECONDARY:
banner_image: "campaigns/secondary_teacher.jpg"
teaching_job_roles:
- teacher
phases:
- secondary
radius:
- 15
ectrolesbespoke:
banner_image: "campaigns/ect_teacher.jpg"
ect_suitable: true
teaching_job_roles:
- teacher
radius:
- 15
mathsbespoke:
banner_image: "campaigns/subjects.jpg"
teaching_job_roles:
- teacher
radius:
- 15
dancedramamusicbespoke:
banner_image: "campaigns/subjects.jpg"
teaching_job_roles:
- teacher
radius:
- 15
dancedramamusicbespoke:
banner_image: "campaigns/subjects.jpg"
teaching_job_roles:
- teacher
radius:
- 15
computingbespoke:
banner_image: "campaigns/subjects.jpg"
teaching_job_roles:
- teacher
radius:
- 15
chemistrybespoke:
banner_image: "campaigns/subjects.jpg"
teaching_job_roles:
- teacher
radius:
- 15
businessandeconomicsbespoke:
banner_image: "campaigns/subjects.jpg"
teaching_job_roles:
- teacher
radius:
- 15
re:
banner_image: "campaigns/subjects.jpg"
teaching_job_roles:
- teacher
radius:
- 15
biologybespoke:
banner_image: "campaigns/subjects.jpg"
teaching_job_roles:
- teacher
radius:
- 15
englishbespoke:
banner_image: "campaigns/subjects.jpg"
teaching_job_roles:
- teacher
radius:
- 15
psychologybespoke:
banner_image: "campaigns/subjects.jpg"
teaching_job_roles:
- teacher
radius:
- 15
parttimebespoke+PRIMARY:
banner_image: "campaigns/primary_part_time.jpg"
teaching_job_roles:
- teacher
working_patterns:
- part_time
- job_share
radius:
- 15
parttimebespoke+SECONDARY:
banner_image: "campaigns/secondary_part_time.jpg"
teaching_job_roles:
- teacher
phases:
- secondary
working_patterns:
- part_time
- job_share
radius:
- 15
stilltimebespoke+PRIMARY:
banner_image: "campaigns/primary_not_too_late.jpg"
teaching_job_roles:
- teacher
phases:
- primary
radius:
- 15
stilltimebespoke+SECONDARY:
banner_image: "campaigns/secondary_not_too_late.jpg"
teaching_job_roles:
- teacher
phases:
- secondary
radius:
- 15

####################################################################################################
# The data below is only used in automated tests, make sure you add your campaign pages to the
# `shared` section above!
####################################################################################################
test:
FAKE1+CAMPAIGN:
banner_image: "campaigns/secondary_not_too_late.jpg"
working_patterns:
- part_time
subjects:
- Potions
- Sorcery

Loading
Loading