From 11945600499c623c118de0bbaafd92179b2a1079 Mon Sep 17 00:00:00 2001 From: starswan Date: Mon, 7 Oct 2024 09:13:00 +0100 Subject: [PATCH] Relisting vacancy journey --- .../dashboard_component.html.slim | 17 +++-- .../vacancies/extend_deadline_controller.rb | 56 +++++++++------ .../publishers/job_listing/relist_form.rb | 36 ++++++++++ app/helpers/vacancy_forms_helper.rb | 2 + .../vacancies/base/_publish_date.html.slim | 10 +++ .../vacancies/build/important_dates.html.slim | 8 +-- .../vacancies/extend_deadline/show.html.slim | 18 +++-- .../review_banners/_closed.html.slim | 4 +- config/locales/forms.yml | 8 +++ config/locales/publishers.yml | 12 +++- config/routes.rb | 4 +- .../publishers_can_extend_a_deadline_spec.rb | 70 +++++++++++++++---- 12 files changed, 188 insertions(+), 57 deletions(-) create mode 100644 app/form_models/publishers/job_listing/relist_form.rb create mode 100644 app/views/publishers/vacancies/base/_publish_date.html.slim diff --git a/app/components/dashboard_component/dashboard_component.html.slim b/app/components/dashboard_component/dashboard_component.html.slim index f2cc46d32e..881a8198a6 100644 --- a/app/components/dashboard_component/dashboard_component.html.slim +++ b/app/components/dashboard_component/dashboard_component.html.slim @@ -118,10 +118,19 @@ p.govuk-body-xs.external-notice = t("jobs.manage.external_notice") - unless vacancy.external? || vacancy.draft? - - row.with_action(text: t("buttons.copy_listing"), - href: organisation_job_copy_path(vacancy.id), - visually_hidden_text: "for #{vacancy.job_title}", - html_attributes: { "data-method": "post" }) + - if vacancy.expired? + - row.with_action(text: t("buttons.relist_vacancy"), + href: organisation_job_extend_deadline_path(vacancy.id), + visually_hidden_text: "for #{vacancy.job_title}") + - row.with_action(text: t("buttons.copy_expired_listing"), + href: organisation_job_copy_path(vacancy.id), + visually_hidden_text: "for #{vacancy.job_title}", + html_attributes: { "data-method": "post" }) + - else + - row.with_action(text: t("buttons.copy_listing"), + href: organisation_job_copy_path(vacancy.id), + visually_hidden_text: "for #{vacancy.job_title}", + html_attributes: { "data-method": "post" }) - unless @organisation.school_group? .govuk-grid-column-one-quarter diff --git a/app/controllers/publishers/vacancies/extend_deadline_controller.rb b/app/controllers/publishers/vacancies/extend_deadline_controller.rb index 55f132c00a..3ea4a3388c 100644 --- a/app/controllers/publishers/vacancies/extend_deadline_controller.rb +++ b/app/controllers/publishers/vacancies/extend_deadline_controller.rb @@ -1,9 +1,21 @@ class Publishers::Vacancies::ExtendDeadlineController < Publishers::Vacancies::BaseController - helper_method :form, :vacancy + helper_method :vacancy + + def show + form_class = vacancy.expired? ? Publishers::JobListing::RelistForm : Publishers::JobListing::ExtendDeadlineForm + @form = form_class.new( + start_date_type: vacancy.start_date_type, + starts_on: vacancy.starts_on, + earliest_start_date: vacancy.earliest_start_date, + latest_start_date: vacancy.latest_start_date, + other_start_date_details: vacancy.other_start_date_details, + ) + end def update - if form.valid? - vacancy.update(form.attributes_to_save) + @form = Publishers::JobListing::ExtendDeadlineForm.new(form_params) + if @form.valid? + vacancy.update(@form.attributes_to_save) update_google_index(vacancy) redirect_to organisation_jobs_with_type_path(:published), success: t(".success", job_title: vacancy.job_title) else @@ -11,30 +23,32 @@ def update end end - private - - def form - @form ||= Publishers::JobListing::ExtendDeadlineForm.new(form_attributes) + def relist + @form = Publishers::JobListing::RelistForm.new(relist_params) + if @form.valid? + vacancy.update(@form.attributes_to_save) + update_google_index(vacancy) + redirect_to organisation_job_summary_path(vacancy.id), success: t(".success", job_title: vacancy.job_title) + else + render :show + end end - def form_attributes - case action_name - when "show" - { - start_date_type: vacancy.start_date_type, - starts_on: vacancy.starts_on, - earliest_start_date: vacancy.earliest_start_date, - latest_start_date: vacancy.latest_start_date, - other_start_date_details: vacancy.other_start_date_details, - } - when "update" - form_params - end + private + + def common_params + %i[expires_at expiry_time start_date_type starts_on earliest_start_date latest_start_date other_start_date_details extension_reason other_extension_reason_details] end def form_params params.require(:publishers_job_listing_extend_deadline_form) - .permit(:expires_at, :expiry_time, :start_date_type, :starts_on, :earliest_start_date, :latest_start_date, :other_start_date_details, :extension_reason, :other_extension_reason_details) + .permit(*common_params) + .merge(previous_deadline: vacancy.expires_at) + end + + def relist_params + params.require(:publishers_job_listing_relist_form) + .permit(*(common_params + %i[publish_on publish_on_day])) .merge(previous_deadline: vacancy.expires_at) end diff --git a/app/form_models/publishers/job_listing/relist_form.rb b/app/form_models/publishers/job_listing/relist_form.rb new file mode 100644 index 0000000000..bf40862f05 --- /dev/null +++ b/app/form_models/publishers/job_listing/relist_form.rb @@ -0,0 +1,36 @@ +class Publishers::JobListing::RelistForm < Publishers::JobListing::ExtendDeadlineForm + attr_reader :publish_on + attr_writer :publish_on_day + + validates(:publish_on, date: { on_or_after: :today, on_or_before: :far_future }, unless: lambda do + publish_on_day.blank? || (publish_on.is_a?(Date) && (publish_on.today? || publish_on.tomorrow?)) + end) + validates :publish_on_day, inclusion: { in: %w[today tomorrow another_day] } + + def initialize(params = {}) + @params = params + super + end + + def attributes_to_save + super.merge( + publish_on: publish_on, + ) + end + + def publish_on_day + return "today" if @publish_on_day == "today" || publish_on == Date.today + return "tomorrow" if @publish_on_day == "tomorrow" || publish_on == Date.tomorrow + + "another_day" if @publish_on_day == "another_day" || publish_on.is_a?(Date) + end + + def publish_on=(value) + @publish_on = + case @params[:publish_on_day] + when "today" then Date.today + when "tomorrow" then Date.tomorrow + else date_from_multiparameter_hash(value) + end + end +end diff --git a/app/helpers/vacancy_forms_helper.rb b/app/helpers/vacancy_forms_helper.rb index c4a037482e..81150cbaab 100644 --- a/app/helpers/vacancy_forms_helper.rb +++ b/app/helpers/vacancy_forms_helper.rb @@ -55,6 +55,8 @@ def vacancy_review_form_heading_action_link(vacancy, action) # rubocop:disable M govuk_link_to(t("publishers.vacancies.show.heading_component.action.close_early"), organisation_job_end_listing_path(vacancy.id), class: "govuk-!-margin-bottom-0") when "extend_closing_date" govuk_link_to(t("publishers.vacancies.show.heading_component.action.extend_closing_date"), organisation_job_extend_deadline_path(vacancy.id), class: "govuk-!-margin-bottom-0") + when "relist" + govuk_link_to(t("publishers.vacancies.show.heading_component.action.relist"), organisation_job_extend_deadline_path(vacancy.id), class: "govuk-!-margin-bottom-0") when "publish" govuk_button_link_to(t("publishers.vacancies.show.heading_component.action.publish"), organisation_job_publish_path(vacancy.id), class: "govuk-!-margin-bottom-0") when "preview" diff --git a/app/views/publishers/vacancies/base/_publish_date.html.slim b/app/views/publishers/vacancies/base/_publish_date.html.slim new file mode 100644 index 0000000000..d55e0e3efa --- /dev/null +++ b/app/views/publishers/vacancies/base/_publish_date.html.slim @@ -0,0 +1,10 @@ += f.govuk_radio_buttons_fieldset :publish_on_day, legend: { size: "m", tag: nil } do + = f.govuk_radio_button :publish_on_day, :today, link_errors: true, + label: { text: t("helpers.label.publishers_job_listing_important_dates_form.publish_on_day_options.today", date: Time.now.strftime("%d %m %Y")) } + = f.govuk_radio_button :publish_on_day, :tomorrow, + label: { text: t("helpers.label.publishers_job_listing_important_dates_form.publish_on_day_options.tomorrow", date: 1.day.from_now.strftime("%d %m %Y")) } + = f.govuk_radio_button :publish_on_day, :another_day, + label: { text: t("helpers.label.publishers_job_listing_important_dates_form.publish_on_day_options.another_day") } do + = f.govuk_date_field :publish_on, + legend: { tag: nil, text: t("helpers.label.publishers_job_listing_important_dates_form.publish_on") }, + hint: -> { t("helpers.hint.date", date: 1.week.from_now.to_fs(:publish_on_date)) } diff --git a/app/views/publishers/vacancies/build/important_dates.html.slim b/app/views/publishers/vacancies/build/important_dates.html.slim index 217537ef73..7aea20462a 100644 --- a/app/views/publishers/vacancies/build/important_dates.html.slim +++ b/app/views/publishers/vacancies/build/important_dates.html.slim @@ -15,13 +15,7 @@ br = f.govuk_date_field :publish_on, class: "govuk-!-display-none" - else - = f.govuk_radio_buttons_fieldset :publish_on_day, legend: { size: "m", tag: nil } do - = f.govuk_radio_button :publish_on_day, :today, link_errors: true, label: { text: t("helpers.label.publishers_job_listing_important_dates_form.publish_on_day_options.today", date: Time.now.strftime("%-e %-B %Y")) } - = f.govuk_radio_button :publish_on_day, :tomorrow, label: { text: t("helpers.label.publishers_job_listing_important_dates_form.publish_on_day_options.tomorrow", date: 1.day.from_now.strftime("%-e %-B %Y")) } - = f.govuk_radio_button :publish_on_day, :another_day do - = f.govuk_date_field :publish_on, - legend: { tag: nil }, - hint: -> { t("helpers.hint.date", date: 1.week.from_now.strftime("%-d %-m %Y")) } + = render "publish_date", f: f = f.govuk_date_field :expires_at, hint: -> { t("helpers.hint.date", date: 1.month.from_now.strftime("%-d %-m %Y")) }, diff --git a/app/views/publishers/vacancies/extend_deadline/show.html.slim b/app/views/publishers/vacancies/extend_deadline/show.html.slim index 7b308f25b2..d1fc8d63b5 100644 --- a/app/views/publishers/vacancies/extend_deadline/show.html.slim +++ b/app/views/publishers/vacancies/extend_deadline/show.html.slim @@ -1,20 +1,26 @@ -- content_for :page_title_prefix, t(".title", job_title: vacancy.job_title) +- content_for :page_title_prefix, vacancy.expired? ? t(".relist_title", job_title: vacancy.job_title) : t(".extend_title", job_title: vacancy.job_title) - content_for :breadcrumbs do = govuk_back_link text: t("buttons.back"), href: organisation_job_path(vacancy.id) .govuk-grid-row .govuk-grid-column-two-thirds - span.govuk-caption-l = vacancy.job_title - h1.govuk-heading-xl = t(".heading") + - if vacancy.expired? + h1.govuk-heading-xl = t(".relist_heading", job_title: vacancy.job_title) + - else + span.govuk-caption-l = vacancy.job_title + h1.govuk-heading-xl = t(".extend_heading") = govuk_inset_text do = vacancy.expired? ? t(".deadline.past") : t(".deadline.future") strong =< format_time_to_datetime_at(vacancy.expires_at) - = form_for form, url: organisation_job_extend_deadline_path(vacancy.id), method: :patch do |f| + = form_for @form, url: vacancy.expired? ? relist_organisation_job_extend_deadline_path(vacancy.id) : organisation_job_extend_deadline_path(vacancy.id), method: :patch do |f| = f.govuk_error_summary + - if vacancy.expired? + = render "publish_date", f: f + = f.govuk_date_field :expires_at, hint: -> { t("helpers.hint.date", date: 1.month.from_now.strftime("%d %m %Y")) } @@ -45,7 +51,7 @@ hint: -> { "For example 'Easter term'" }, data: { "form-target": "inputText" } - = f.govuk_radio_buttons_fieldset :extension_reason, legend: { text: t(".extension_reason.label") } do + = f.govuk_radio_buttons_fieldset :extension_reason, legend: { text: vacancy.expired? ? t(".reason_relist_label") : t(".reason_extend_label") } do = f.govuk_radio_button :extension_reason, :no_applications, link_errors: true, label: { text: t(".extension_reason.no_applications") } = f.govuk_radio_button :extension_reason, :didnt_find_right_candidate, link_errors: true, label: { text: t(".extension_reason.didnt_find_right_candidate") } = f.govuk_radio_button :extension_reason, :other_extension_reason, label: { text: t(".extension_reason.other_extension_reason") } do @@ -53,6 +59,6 @@ label: { text: t(".extension_reason.other_details") }, rows: 5 - = f.govuk_submit t("buttons.extend_closing_date"), class: "govuk-!-margin-bottom-5" + = f.govuk_submit vacancy.expired? ? t("buttons.relist_vacancy") : t("buttons.extend_closing_date"), class: "govuk-!-margin-bottom-5" = govuk_link_to(t("buttons.cancel"), organisation_jobs_with_type_path, class: "govuk-link--no-visited-state govuk-!-font-size-19") diff --git a/app/views/publishers/vacancies/review_banners/_closed.html.slim b/app/views/publishers/vacancies/review_banners/_closed.html.slim index 56893089f8..f486135c75 100644 --- a/app/views/publishers/vacancies/review_banners/_closed.html.slim +++ b/app/views/publishers/vacancies/review_banners/_closed.html.slim @@ -4,5 +4,5 @@ h1.govuk-heading-l class="govuk-!-display-inline" = govuk_inset_text do = vacancy_review_form_heading_inset_text(vacancy, "closed") .govuk-button-group class="govuk-!-margin-top-4" - - %w[view copy extend_closing_date].each do |action| - = vacancy_review_form_heading_action_link(vacancy, action) unless vacancy.legacy? && action == "extend_closing_date" + - %w[view copy relist].each do |action| + = vacancy_review_form_heading_action_link(vacancy, action) unless vacancy.legacy? && action == "relist" diff --git a/config/locales/forms.yml b/config/locales/forms.yml index 46368bffb6..bde0dad83b 100644 --- a/config/locales/forms.yml +++ b/config/locales/forms.yml @@ -36,6 +36,7 @@ en: continue: Continue continue_to_account: Continue to your account continue_to_dsi: Continue to DfE Sign-in + copy_expired_listing: Copy copy_listing: Copy job listing copy_url: Copy URL create_account: Create an account @@ -64,6 +65,7 @@ en: profile_update: logo: Update organisation logo photo: Update organisation photo + relist_vacancy: Relist reject: Reject reminder_continue: Continue to create a job request_dsi_account: Request a DfE Sign-in account @@ -773,6 +775,7 @@ en: email: By email website: Through a website publishers_job_listing_important_dates_form: + publish_on: Another date publish_on_day_options: another_day: Another date today: Today (%{date}) @@ -1024,6 +1027,11 @@ en: expiry_time: Closing time publish_on: Another date publish_on_day: Publish date + publishers_job_listing_relist_form: + expires_at: Closing date + expiry_time: Closing time + publish_on_day: Publish date + another_day: Another date publishers_job_listing_include_additional_documents_form: include_additional_documents: Do you want to upload any additional documents? publishers_job_listing_start_date_form: diff --git a/config/locales/publishers.yml b/config/locales/publishers.yml index b7376abe08..e93854d7a3 100644 --- a/config/locales/publishers.yml +++ b/config/locales/publishers.yml @@ -470,16 +470,21 @@ en: deadline: future: The current deadline is past: The previous deadline was - heading: Extend closing date - title: Extend closing date - %{job_title} + extend_heading: Extend closing date + relist_heading: Relist %{job_title} + extend_title: Extend closing date - %{job_title} + relist_title: Relist - %{job_title} + reason_extend_label: Why are you extending this job? + reason_relist_label: Why are you relisting this job? extension_reason: - label: Why are you extending this job? no_applications: No applications didnt_find_right_candidate: Didn't find the right candidate other_extension_reason: Other other_details: Can you provide more details? (optional) update: success: "The deadline for %{job_title} has been extended" + relist: + success: "%{job_title} has been relisted" expired_feedbacks: new: description: >- @@ -602,6 +607,7 @@ en: copy: Copy delete: Delete extend_closing_date: Extend closing date + relist: Relist preview: Preview publish: Publish job listing scheduled_complete_draft: Schedule job listing diff --git a/config/routes.rb b/config/routes.rb index 5c268c51ac..f84af8d264 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -356,7 +356,9 @@ resource :statistics, only: %i[show update], controller: "publishers/vacancies/statistics" resource :copy, only: %i[create], controller: "publishers/vacancies/copy" resource :end_listing, only: %i[show update], controller: "publishers/vacancies/end_listing" - resource :extend_deadline, only: %i[show update], controller: "publishers/vacancies/extend_deadline" + resource :extend_deadline, only: %i[show update], controller: "publishers/vacancies/extend_deadline" do + patch :relist + end resources :job_applications, only: %i[index show], controller: "publishers/vacancies/job_applications" do resources :notes, only: %i[index create destroy], controller: "publishers/vacancies/job_applications/notes" diff --git a/spec/system/publishers/publishers_can_extend_a_deadline_spec.rb b/spec/system/publishers/publishers_can_extend_a_deadline_spec.rb index 15966c555c..7e86f711c7 100644 --- a/spec/system/publishers/publishers_can_extend_a_deadline_spec.rb +++ b/spec/system/publishers/publishers_can_extend_a_deadline_spec.rb @@ -11,18 +11,16 @@ login_publisher(publisher: publisher, organisation: organisation) visit organisation_jobs_with_type_path(vacancy_type) click_on vacancy.job_title - click_on I18n.t("publishers.vacancies.show.heading_component.action.extend_closing_date") + click_on extend_expires_at end after { logout } context "when the vacancy has not expired" do let(:vacancy_type) { :published } + let(:extend_expires_at) { I18n.t("publishers.vacancies.show.heading_component.action.extend_closing_date") } - it "submits form, renders error, then extends" do + it "can be extended" do choose I18n.t("publishers.vacancies.extend_deadline.show.extension_reason.other_extension_reason"), name: "publishers_job_listing_extend_deadline_form[extension_reason]" - click_on I18n.t("buttons.extend_closing_date") - - expect(page).to have_content("There is a problem") fill_in "publishers_job_listing_extend_deadline_form[expires_at(1i)]", with: expires_at.year fill_in "publishers_job_listing_extend_deadline_form[expires_at(2i)]", with: expires_at.month @@ -41,19 +39,65 @@ context "when the vacancy has expired" do let(:vacancy_type) { :expired } + let(:extend_expires_at) { I18n.t("publishers.vacancies.show.heading_component.action.relist") } - scenario "the closing date can be extended" do - fill_in "publishers_job_listing_extend_deadline_form[expires_at(1i)]", with: expires_at.year - fill_in "publishers_job_listing_extend_deadline_form[expires_at(2i)]", with: expires_at.month - fill_in "publishers_job_listing_extend_deadline_form[expires_at(3i)]", with: expires_at.day - choose "9am", name: "publishers_job_listing_extend_deadline_form[expiry_time]" + before do + fill_in "publishers_job_listing_relist_form[expires_at(1i)]", with: expires_at.year + fill_in "publishers_job_listing_relist_form[expires_at(2i)]", with: expires_at.month + fill_in "publishers_job_listing_relist_form[expires_at(3i)]", with: expires_at.day + choose "9am", name: "publishers_job_listing_relist_form[expiry_time]" choose I18n.t("publishers.vacancies.extend_deadline.show.extension_reason.didnt_find_right_candidate") + end - click_on I18n.t("buttons.extend_closing_date") + it "returns an error without a publish date" do + click_on I18n.t("buttons.relist_vacancy") - expect(current_path).to eq(organisation_jobs_with_type_path(:published)) - expect(vacancy.reload).to have_attributes(extension_reason: "didnt_find_right_candidate", expires_at: expires_at) + expect(page).to have_content("There is a problem") + end + + it "can be re-listed for publishing today" do + choose I18n.t("helpers.label.publishers_job_listing_important_dates_form.publish_on_day_options.today", date: Time.now.strftime("%d %m %Y")), + name: "publishers_job_listing_relist_form[publish_on_day]" + + click_on I18n.t("buttons.relist_vacancy") + + expect(current_path).to eq(organisation_job_summary_path(vacancy.id)) + expect(vacancy.reload).to have_attributes(extension_reason: "didnt_find_right_candidate", + publish_on: Date.today, + expires_at: expires_at) + end + + it "can be re-listed for publishing tomorrow" do + choose I18n.t("helpers.label.publishers_job_listing_important_dates_form.publish_on_day_options.tomorrow", date: 1.day.from_now.strftime("%d %m %Y")), + name: "publishers_job_listing_relist_form[publish_on_day]" + + click_on I18n.t("buttons.relist_vacancy") + + expect(current_path).to eq(organisation_job_summary_path(vacancy.id)) + expect(vacancy.reload).to have_attributes(extension_reason: "didnt_find_right_candidate", + publish_on: Date.tomorrow, + expires_at: expires_at) + end + + context "when choosing another publish date" do + let(:publish_date) { 1.week.from_now.to_date } + + it "can be re-listed for publishing on another date" do + choose I18n.t("helpers.label.publishers_job_listing_important_dates_form.publish_on_day_options.another_day"), + name: "publishers_job_listing_relist_form[publish_on_day]" + + fill_in "publishers_job_listing_relist_form[publish_on(1i)]", with: publish_date.year + fill_in "publishers_job_listing_relist_form[publish_on(2i)]", with: publish_date.month + fill_in "publishers_job_listing_relist_form[publish_on(3i)]", with: publish_date.day + + click_on I18n.t("buttons.relist_vacancy") + + expect(current_path).to eq(organisation_job_summary_path(vacancy.id)) + expect(vacancy.reload).to have_attributes(extension_reason: "didnt_find_right_candidate", + publish_on: publish_date, + expires_at: expires_at) + end end end end