diff --git a/app/controllers/support/cases/evaluation_due_dates_controller.rb b/app/controllers/support/cases/evaluation_due_dates_controller.rb new file mode 100644 index 000000000..855470b6f --- /dev/null +++ b/app/controllers/support/cases/evaluation_due_dates_controller.rb @@ -0,0 +1,38 @@ +module Support + class Cases::EvaluationDueDatesController < Cases::ApplicationController + before_action :set_current_case + before_action { @back_url = support_case_path(@current_case, anchor: "tasklist") } + + include HasDateParams + def edit + @case_evaluation_due_date_form = CaseEvaluationDueDateForm.new(**current_case.to_h) + end + + def update + @case_evaluation_due_date_form = CaseEvaluationDueDateForm.from_validation(validation) + + if validation.success? + @current_case.update!(@case_evaluation_due_date_form.to_h) + redirect_to @back_url + else + render :edit + end + end + + private + + def set_current_case + @current_case = Support::Case.find(params[:case_id]) + end + + def validation + @validation ||= CaseEvaluationDueDateFormSchema.new.call(**evaluation_due_date_params) + end + + def evaluation_due_date_params + form_params = params.require(:case_evaluation_due_date_form).permit + form_params[:evaluation_due_date] = date_param(:case_evaluation_due_date_form, :evaluation_due_date) + form_params + end + end +end diff --git a/app/forms/support/case_evaluation_due_date_form.rb b/app/forms/support/case_evaluation_due_date_form.rb new file mode 100644 index 000000000..a17629528 --- /dev/null +++ b/app/forms/support/case_evaluation_due_date_form.rb @@ -0,0 +1,5 @@ +module Support + class CaseEvaluationDueDateForm < Form + option :evaluation_due_date, Types::DateField, optional: true + end +end diff --git a/app/forms/support/case_evaluation_due_date_form_schema.rb b/app/forms/support/case_evaluation_due_date_form_schema.rb new file mode 100644 index 000000000..834dd5bd9 --- /dev/null +++ b/app/forms/support/case_evaluation_due_date_form_schema.rb @@ -0,0 +1,26 @@ +module Support + class CaseEvaluationDueDateFormSchema < ::Support::Schema + config.messages.top_namespace = :case_evaluation_due_date_form + + params do + required(:evaluation_due_date).value(:hash) + end + + rule(:evaluation_due_date) do + key.failure(:missing) if value.values.all?(&:blank?) + end + + rule :evaluation_due_date do + key.failure(:invalid) unless value.values.all?(&:blank?) || hash_to_date.call(value) + end + + rule :evaluation_due_date do + if value.present? + date = hash_to_date.call(value) + if date && date <= Time.zone.today + key.failure(:must_be_in_the_future) + end + end + end + end +end diff --git a/app/views/support/cases/evaluation_due_dates/edit.html.erb b/app/views/support/cases/evaluation_due_dates/edit.html.erb new file mode 100644 index 000000000..0df2aca94 --- /dev/null +++ b/app/views/support/cases/evaluation_due_dates/edit.html.erb @@ -0,0 +1,12 @@ +<%= render partial: "support/cases/components/case_header", locals: { current_case: @current_case } %> +

<%= I18n.t("support.cases.evaluation_due_date.header") %>

+ +<%= form_with model: @case_evaluation_due_date_form, scope: :case_evaluation_due_date_form, url: support_case_evaluation_due_dates_path(@current_case), method: :patch do |form| %> + <%= form.govuk_error_summary %> + <%= form.govuk_date_field :evaluation_due_date, legend: { text: I18n.t("support.cases.evaluation_due_date.date.label"), }, hint: { text: I18n.t("support.cases.evaluation_due_date.date.hint") } %> + +
+ <%= form.submit I18n.t("support.cases.evaluation_due_date.submit"), class: "govuk-button" %> + <%= link_to I18n.t("support.cases.evaluation_due_date.cancel"), @back_url, class: "govuk-link govuk-link--no-visited-state" %> +
+<% end %> diff --git a/app/views/support/cases/show/_tasklist.html.erb b/app/views/support/cases/show/_tasklist.html.erb index c0058c74f..c79ea52e1 100644 --- a/app/views/support/cases/show/_tasklist.html.erb +++ b/app/views/support/cases/show/_tasklist.html.erb @@ -9,7 +9,7 @@ <%= govuk_task_list(id_prefix: "complete-evaluation") do |task_list| task_list.with_item(title: I18n.t("support.case.label.tasklist.item.add_evaluators"), href: '#', status: govuk_tag(text: I18n.t("support.case.label.tasklist.status.to_do"))) - task_list.with_item(title: I18n.t("support.case.label.tasklist.item.set_due_date"), href: '#', status: govuk_tag(text: I18n.t("support.case.label.tasklist.status.to_do"))) + task_list.with_item(title: I18n.t("support.case.label.tasklist.item.set_due_date"), href: edit_support_case_evaluation_due_dates_path(@current_case), status: @current_case.evaluation_due_date ? govuk_tag(text: I18n.t("support.case.label.tasklist.status.complete"), colour: "green") : govuk_tag(text: I18n.t("support.case.label.tasklist.status.to_do"))) task_list.with_item(title: I18n.t("support.case.label.tasklist.item.upload_documents"), href: '#', status: govuk_tag(text: I18n.t("support.case.label.tasklist.status.to_do"))) task_list.with_item(title: I18n.t("support.case.label.tasklist.item.email_evaluators")) do | item | item.with_status(text: I18n.t("support.case.label.tasklist.status.cannot_start"), cannot_start_yet: true) diff --git a/config/locales/en.yml b/config/locales/en.yml index 245e39612..fb06d85c5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1773,6 +1773,13 @@ en: cases: my_cases: header_link: My cases + evaluation_due_date: + header: Set due date + date: + label: When does the evaluation need to be completed by? + hint: For example, 27 3 2025 + submit: Continue + cancel: Cancel emails: attachments: attach_files: Attach files diff --git a/config/locales/validation/support/en.yml b/config/locales/validation/support/en.yml index 453ff07cb..4105b6ac2 100644 --- a/config/locales/validation/support/en.yml +++ b/config/locales/validation/support/en.yml @@ -290,3 +290,13 @@ en: infected: One or more of the files you uploaded contained a virus and have been rejected incorrect_file_type: One or more of the files you uploaded was an incorrect file type + case_evaluation_due_date_form: + rules: + evaluation_due_date: "" + errors: + rules: + evaluation_due_date: + missing: Enter valid evaluation due date + invalid: Enter valid evaluation due date + must_be_in_the_future: Evaluation due date must be in the future + diff --git a/config/routes.rb b/config/routes.rb index f7347ef9e..091660488 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -235,6 +235,7 @@ resources :contracts, only: %i[edit update] resources :additional_contacts resources :evaluators, except: %i[show] + resource :evaluation_due_dates, only: %i[edit update] resource :email, only: %i[create] do scope module: :emails do resources :content, only: %i[show], param: :template diff --git a/db/migrate/20241126122758_add_evaluation_due_date_to_support_cases.rb b/db/migrate/20241126122758_add_evaluation_due_date_to_support_cases.rb new file mode 100644 index 000000000..8c7bf6090 --- /dev/null +++ b/db/migrate/20241126122758_add_evaluation_due_date_to_support_cases.rb @@ -0,0 +1,5 @@ +class AddEvaluationDueDateToSupportCases < ActiveRecord::Migration[7.2] + def change + add_column :support_cases, :evaluation_due_date, :date + end +end diff --git a/db/schema.rb b/db/schema.rb index 99e48e973..09646b58e 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[7.2].define(version: 2024_11_27_115728) do +ActiveRecord::Schema[7.2].define(version: 2024_11_26_122758) do create_sequence "evaluation_refs" create_sequence "framework_refs" @@ -623,6 +623,7 @@ t.string "project" t.string "other_school_urns", default: [], array: true t.boolean "is_evaluator", default: false + t.date "evaluation_due_date" t.index ["category_id"], name: "index_support_cases_on_category_id" t.index ["existing_contract_id"], name: "index_support_cases_on_existing_contract_id" t.index ["new_contract_id"], name: "index_support_cases_on_new_contract_id" diff --git a/spec/features/support/agent_can_add_evaluation_due_date_spec.rb b/spec/features/support/agent_can_add_evaluation_due_date_spec.rb new file mode 100644 index 000000000..79a537cd3 --- /dev/null +++ b/spec/features/support/agent_can_add_evaluation_due_date_spec.rb @@ -0,0 +1,28 @@ +require "rails_helper" + +describe "Edit evaluation due date", :js do + include_context "with an agent" + + let(:support_case) { create(:support_case) } + + specify "Update evaluation due date evaluators" do + visit edit_support_case_evaluation_due_dates_path(case_id: support_case) + + expect(page).to have_text("Set due date") + expect(page).to have_text("When does the evaluation need to be completed by?") + + fill_in "Day", with: "" + fill_in "Month", with: "" + fill_in "Year", with: "" + click_button "Continue" + + expect(page).to have_text("Enter valid evaluation due date") + + fill_in "Day", with: Date.yesterday.day + fill_in "Month", with: Date.yesterday.month + fill_in "Year", with: Date.yesterday.year + click_button "Continue" + + expect(page).to have_text("Evaluation due date must be in the future") + end +end diff --git a/spec/models/support/case_spec.rb b/spec/models/support/case_spec.rb index eda6d2af1..ac1912a4d 100644 --- a/spec/models/support/case_spec.rb +++ b/spec/models/support/case_spec.rb @@ -59,7 +59,7 @@ describe "#to_csv" do it "includes headers" do expect(described_class.to_csv).to eql( - "id,ref,category_id,request_text,support_level,status,state,created_at,updated_at,agent_id,first_name,last_name,email,phone_number,source,organisation_id,existing_contract_id,new_contract_id,procurement_id,savings_status,savings_estimate_method,savings_actual_method,savings_estimate,savings_actual,action_required,organisation_type,value,closure_reason,extension_number,other_category,other_query,procurement_amount,confidence_level,special_requirements,query_id,exit_survey_sent,detected_category_id,creation_source,user_selected_category,created_by_id,procurement_stage_id,initial_request_text,with_school,next_key_date,next_key_date_description,discovery_method,discovery_method_other_text,project,other_school_urns,is_evaluator\n", + "id,ref,category_id,request_text,support_level,status,state,created_at,updated_at,agent_id,first_name,last_name,email,phone_number,source,organisation_id,existing_contract_id,new_contract_id,procurement_id,savings_status,savings_estimate_method,savings_actual_method,savings_estimate,savings_actual,action_required,organisation_type,value,closure_reason,extension_number,other_category,other_query,procurement_amount,confidence_level,special_requirements,query_id,exit_survey_sent,detected_category_id,creation_source,user_selected_category,created_by_id,procurement_stage_id,initial_request_text,with_school,next_key_date,next_key_date_description,discovery_method,discovery_method_other_text,project,other_school_urns,is_evaluator,evaluation_due_date\n", ) end end