diff --git a/app/components/find/courses/about_schools_component/view.html.erb b/app/components/find/courses/about_schools_component/view.html.erb
index fd856564eb..e724951a5a 100644
--- a/app/components/find/courses/about_schools_component/view.html.erb
+++ b/app/components/find/courses/about_schools_component/view.html.erb
@@ -49,19 +49,11 @@
<%= course.placements_heading %>
+
<%= t(".work_with_schools") %>
+
- We work with the following schools to provide your school placements.
+ <%= govuk_link_to(t(".view_list_of_school_placements"), placements_url) %>
-
-
- <% course.preview_site_statuses.each do |site_status| %>
- -
- <%= smart_quotes(site_status.site.location_name) %>
-
- <%= smart_quotes(site_status.site.decorate.full_address) %>
-
- <% end %>
-
<% end %>
diff --git a/app/components/find/courses/about_schools_component/view.rb b/app/components/find/courses/about_schools_component/view.rb
index bb6c95512d..0eb86d659b 100644
--- a/app/components/find/courses/about_schools_component/view.rb
+++ b/app/components/find/courses/about_schools_component/view.rb
@@ -38,6 +38,21 @@ def show_scitt_guidance?
course_information_config.show_placement_guidance?(:program_type)
end
+ def placements_url
+ if course.has_unpublished_changes? || (course.is_published? && course.is_running?)
+ URI.join(
+ Settings.search_ui.base_url,
+ find_placements_path(course.provider_code, course.course_code)
+ ).to_s
+ else
+ placements_publish_provider_recruitment_cycle_course_path(
+ course.provider_code,
+ course.recruitment_cycle_year,
+ course.course_code
+ )
+ end
+ end
+
private
def course_information_config
diff --git a/app/controllers/find/placements_controller.rb b/app/controllers/find/placements_controller.rb
new file mode 100644
index 0000000000..6ef9176e74
--- /dev/null
+++ b/app/controllers/find/placements_controller.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Find
+ class PlacementsController < ApplicationController
+ before_action -> { render_not_found if provider.nil? }
+
+ def index
+ @course = provider.courses.includes(
+ :enrichments,
+ subjects: [:financial_incentive],
+ site_statuses: [:site]
+ ).find_by!(course_code: params[:course_code]&.upcase).decorate
+
+ render_not_found unless @course.is_published?
+ end
+ end
+end
diff --git a/app/controllers/publish/courses/school_placements_controller.rb b/app/controllers/publish/courses/school_placements_controller.rb
index dc1e291ba4..61af4c6a15 100644
--- a/app/controllers/publish/courses/school_placements_controller.rb
+++ b/app/controllers/publish/courses/school_placements_controller.rb
@@ -8,6 +8,10 @@ class SchoolPlacementsController < PublishController
before_action :authorise_with_pundit
+ def index
+ @course = course
+ end
+
def edit
@course_school_placements_form = CourseSchoolPlacementsForm.new(course_enrichment)
@copied_fields = copy_content_check(::Courses::Copy::SCHOOL_PLACEMENTS_FIELDS)
diff --git a/app/models/course.rb b/app/models/course.rb
index 96fc7cbcac..ff3a051777 100644
--- a/app/models/course.rb
+++ b/app/models/course.rb
@@ -100,6 +100,7 @@ class Course < ApplicationRecord
belongs_to :provider
delegate :tda_active?, to: :provider, allow_nil: true
+ delegate :provider_name, :provider_code, to: :provider, allow_nil: true
belongs_to :accrediting_provider,
->(c) { where(recruitment_cycle: c.recruitment_cycle) },
diff --git a/app/views/find/placements/index.html.erb b/app/views/find/placements/index.html.erb
new file mode 100644
index 0000000000..41d7ef2642
--- /dev/null
+++ b/app/views/find/placements/index.html.erb
@@ -0,0 +1,21 @@
+<%= content_for :page_title do %>
+ <%= t(
+ "find.courses.placements.heading",
+ provider_name: @course.provider_name
+ ) %>
+<% end %>
+<% content_for :before_content do %>
+ <%= govuk_back_link(
+ href: search_ui_course_page_url(
+ provider_code: @course.provider_code,
+ course_code: @course.course_code
+ ),
+ text: t(
+ "find.courses.placements.back",
+ course_name: @course.name,
+ course_code: @course.course_code
+ )
+ ) %>
+<% end %>
+
+<%= render partial: "shared/courses/placements", locals: { course: @course } %>
diff --git a/app/views/publish/courses/school_placements/index.html.erb b/app/views/publish/courses/school_placements/index.html.erb
new file mode 100644
index 0000000000..0cc55d5b8f
--- /dev/null
+++ b/app/views/publish/courses/school_placements/index.html.erb
@@ -0,0 +1,22 @@
+<%= content_for :page_title do %>
+ <%= t(
+ "publish.providers.courses.placements.heading",
+ provider_name: @course.provider_name
+ ) %>
+<% end %>
+<% content_for :before_content do %>
+ <%= govuk_back_link(
+ href: preview_publish_provider_recruitment_cycle_course_path(
+ @course.provider_code,
+ @course.recruitment_cycle_year,
+ @course.course_code
+ ),
+ text: t(
+ "publish.providers.courses.placements.back",
+ course_name: @course.name,
+ course_code: @course.course_code
+ )
+ ) %>
+<% end %>
+
+<%= render partial: "shared/courses/placements", locals: { course: @course } %>
diff --git a/app/views/shared/courses/_placements.html.erb b/app/views/shared/courses/_placements.html.erb
new file mode 100644
index 0000000000..7e14fc8d87
--- /dev/null
+++ b/app/views/shared/courses/_placements.html.erb
@@ -0,0 +1,21 @@
+
+ <%= t(".heading", provider_name: course.provider_name) %>
+
+
+<%= govuk_warning_text(text: t(".warning")) %>
+
+
+ <%= t(".will_place_you", provider_name: course.provider_name) %>
+
+
+<%= t(".schools_to_be_placed") %>
+
+
+ <% course.preview_site_statuses.each do |site_status| %>
+ -
+ <%= smart_quotes(site_status.site.location_name) %>
+
+ <%= smart_quotes(site_status.site.decorate.full_address) %>
+
+ <% end %>
+
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 7b9cfea4b2..cd19855052 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1,4 +1,11 @@
en:
+ shared:
+ courses:
+ placements:
+ heading: School placements at %{provider_name}
+ warning: You can’t pick which schools you will be in.
+ will_place_you: '%{provider_name} will place you in different schools you can travel to during your training.'
+ schools_to_be_placed: 'The schools you could be placed in are:'
markdown_formatting:
summary_text: Help formatting your text
title: How to format your text
@@ -389,6 +396,9 @@ en:
publish:
providers:
courses:
+ placements:
+ heading: School placements at %{provider_name}
+ back: Back to %{course_name} (%{course_code})
description_content:
course_information_heading: Course information
about_course_label: About this course
diff --git a/config/locales/find.yml b/config/locales/find.yml
index 779a6ce4ce..ab17efd41d 100644
--- a/config/locales/find.yml
+++ b/config/locales/find.yml
@@ -70,6 +70,13 @@ en:
primary_title: Primary courses with subject specialisms
secondary_title: Which secondary subjects do you want to teach?
courses:
+ about_schools_component:
+ view:
+ view_list_of_school_placements: View list of school placements
+ work_with_schools: We work with the following schools to provide your school placements.
+ placements:
+ heading: School placements at %{provider_name}
+ back: Back to %{course_name} (%{course_code})
show:
back_to_search: Back to search results
scholarships:
diff --git a/config/routes/find.rb b/config/routes/find.rb
index a87780343e..42c9c1ddbf 100644
--- a/config/routes/find.rb
+++ b/config/routes/find.rb
@@ -13,6 +13,7 @@
get '/privacy', to: 'pages#privacy', as: :privacy
get '/terms-conditions', to: 'pages#terms', as: :terms
get '/course/:provider_code/:course_code', to: 'courses#show', as: 'course'
+ get '/course/:provider_code/:course_code/placements', to: 'placements#index', as: :placements
get '/course/:provider_code/:course_code/apply', to: 'courses#apply', as: :apply
get '/results', to: 'results#index', as: 'results'
get '/location-suggestions', to: 'location_suggestions#index'
diff --git a/config/routes/publish.rb b/config/routes/publish.rb
index 53ca283ae8..258ce4b18d 100644
--- a/config/routes/publish.rb
+++ b/config/routes/publish.rb
@@ -179,6 +179,7 @@
patch '/interview-process', on: :member, to: 'courses/interview_process#update'
get '/school-placements', on: :member, to: 'courses/school_placements#edit'
patch '/school-placements', on: :member, to: 'courses/school_placements#update'
+ get '/placements', on: :member, to: 'courses/school_placements#index', as: :placements
# This feature is deprecated and will be removed after the beginning of
# 2025 recruitment cycle.
diff --git a/spec/components/find/courses/about_schools_component/view_spec.rb b/spec/components/find/courses/about_schools_component/view_spec.rb
index df88c5fa78..21712caf1e 100644
--- a/spec/components/find/courses/about_schools_component/view_spec.rb
+++ b/spec/components/find/courses/about_schools_component/view_spec.rb
@@ -3,6 +3,8 @@
require 'rails_helper'
describe Find::Courses::AboutSchoolsComponent::View, type: :component do
+ include Rails.application.routes.url_helpers
+
context 'valid program_type' do
it 'renders the component' do
%w[higher_education_programme scitt_programme].each do |program_type|
@@ -172,4 +174,56 @@
end
end
end
+
+ describe '#placements_url' do
+ context 'when course is published' do
+ it 'returns the find url' do
+ provider = build(:provider)
+ course = build(
+ :course,
+ :published,
+ provider:,
+ site_statuses: [
+ build(:site_status, :findable, site: build(:site))
+ ]
+ ).decorate
+
+ result = render_inline(described_class.new(course))
+ url = URI.join(
+ Settings.search_ui.base_url,
+ find_placements_path(course.provider_code, course.course_code)
+ ).to_s
+
+ expect(result).to have_link(
+ 'View list of school placements',
+ href: url
+ )
+ end
+ end
+
+ context 'when course is not published' do
+ it 'returns the publish url' do
+ provider = create(:provider)
+ course = create(
+ :course,
+ provider:,
+ site_statuses: [
+ create(:site_status, :findable, site: create(:site))
+ ]
+ ).decorate
+
+ result = render_inline(described_class.new(course))
+ url = placements_publish_provider_recruitment_cycle_course_path(
+ course.provider_code,
+ course.recruitment_cycle_year,
+ course.course_code
+ )
+
+ expect(result).to have_link(
+ 'View list of school placements',
+ href: url
+ )
+ end
+ end
+ end
end
diff --git a/spec/controllers/find/placements_controller_spec.rb b/spec/controllers/find/placements_controller_spec.rb
new file mode 100644
index 0000000000..9352925d65
--- /dev/null
+++ b/spec/controllers/find/placements_controller_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+module Find
+ describe PlacementsController do
+ before do
+ Timecop.travel(Find::CycleTimetable.mid_cycle)
+ end
+
+ describe '#placements' do
+ context 'when provider is not pressent' do
+ it 'renders the not found page' do
+ get :index, params: {
+ provider_code: 'ABC',
+ course_code: '123'
+ }
+
+ expect(response).to render_template('errors/not_found')
+ end
+ end
+
+ context 'when course is not published' do
+ it 'renders the not found page' do
+ provider = create(:provider)
+ course = create(:course, provider:)
+
+ get :index, params: {
+ provider_code: provider.provider_code,
+ course_code: course.course_code
+ }
+
+ expect(response).to render_template('errors/not_found')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/find/search/viewing_a_course_spec.rb b/spec/features/find/search/viewing_a_course_spec.rb
index 5e5db760b7..4456eebf16 100644
--- a/spec/features/find/search/viewing_a_course_spec.rb
+++ b/spec/features/find/search/viewing_a_course_spec.rb
@@ -68,6 +68,13 @@
end
end
+ scenario 'user views school placements' do
+ given_there_is_a_findable_course
+ when_i_visit_the_course_page
+ when_i_click('View list of school placements')
+ then_i_should_be_on_the_school_placements_page
+ end
+
private
def given_there_is_a_findable_course
@@ -280,9 +287,7 @@ def then_i_should_see_the_course_information
expect(find_course_show_page.school_placements).to have_no_content('Suspended site with vacancies')
- @course.site_statuses.new_or_running.map(&:site).uniq.each do |site|
- expect(find_course_show_page).to have_content(smart_quotes(site.decorate.full_address))
- end
+ expect(find_course_show_page).to have_link('View list of school placements')
expect(find_course_show_page).to have_course_advice
@@ -345,4 +350,14 @@ def accrediting_provider
provider_name: 'Accrediting Provider 1'
)
end
+
+ def when_i_click(button)
+ click_on(button)
+ end
+
+ def then_i_should_be_on_the_school_placements_page
+ @course.site_statuses.new_or_running.map(&:site).uniq.each do |site|
+ expect(find_course_show_page).to have_content(smart_quotes(site.decorate.full_address))
+ end
+ end
end
diff --git a/spec/features/publish/viewing_a_course_preview_spec.rb b/spec/features/publish/viewing_a_course_preview_spec.rb
index 67a2d05e63..d58344597e 100644
--- a/spec/features/publish/viewing_a_course_preview_spec.rb
+++ b/spec/features/publish/viewing_a_course_preview_spec.rb
@@ -127,6 +127,13 @@
end
end
+ scenario 'user views school placements' do
+ given_i_am_authenticated(user: user_with_fee_based_course)
+ when_i_visit_the_publish_course_preview_page
+ when_i_click('View list of school placements')
+ then_i_should_be_on_the_school_placements_page
+ end
+
private
def then_i_see_custom_address
@@ -272,7 +279,7 @@ def then_i_see_the_course_preview_details
)
expect(publish_course_preview_page).to have_study_sites_table
- expect(publish_course_preview_page).to have_school_placements_table
+ expect(publish_course_preview_page).to have_link('View list of school placements')
expect(publish_course_preview_page).to have_course_advice
@@ -475,4 +482,12 @@ def and_i_do_not_see_financial_support
expect(publish_course_preview_page).not_to have_scholarship_amount
expect(publish_course_preview_page).not_to have_bursary_amount
end
+
+ def when_i_click(button)
+ click_on(button)
+ end
+
+ def then_i_should_be_on_the_school_placements_page
+ expect(publish_course_preview_page).to have_school_placements_table
+ end
end
diff --git a/spec/models/course_spec.rb b/spec/models/course_spec.rb
index 3f2dc6c9d8..830c89d259 100644
--- a/spec/models/course_spec.rb
+++ b/spec/models/course_spec.rb
@@ -22,6 +22,9 @@
its(:to_s) { is_expected.to eq("Biology (#{course.provider.provider_code}/3X9F) [#{course.recruitment_cycle}]") }
its(:modular) { is_expected.to eq('') }
+ it { is_expected.to delegate_method(:provider_name).to(:provider).allow_nil }
+ it { is_expected.to delegate_method(:provider_code).to(:provider).allow_nil }
+
describe '#campaign_name' do
it 'assigns the campaign' do
course.engineers_teach_physics!