diff --git a/app/controllers/concerns/has_date_params.rb b/app/controllers/concerns/has_date_params.rb new file mode 100644 index 000000000..260a6af7f --- /dev/null +++ b/app/controllers/concerns/has_date_params.rb @@ -0,0 +1,15 @@ +# Parse date fields as Dates +# +# Useful for extracting dates out of govuk_date_fields +module HasDateParams + extend ActiveSupport::Concern + + # @param form_param [Symbol] + # @param date_field [Symbol] + # + # @return [Hash] + def date_param(form_param, date_field) + date = params.fetch(form_param, {}).permit(date_field) + { day: date["#{date_field}(3i)"], month: date["#{date_field}(2i)"], year: date["#{date_field}(1i)"] } + end +end diff --git a/app/controllers/framework_requests/contract_start_dates_controller.rb b/app/controllers/framework_requests/contract_start_dates_controller.rb index 63dc0794d..b1195283a 100644 --- a/app/controllers/framework_requests/contract_start_dates_controller.rb +++ b/app/controllers/framework_requests/contract_start_dates_controller.rb @@ -1,6 +1,6 @@ module FrameworkRequests class ContractStartDatesController < BaseController - include Support::Concerns::HasDateParams + include HasDateParams skip_before_action :authenticate_user! diff --git a/app/controllers/frameworks/evaluations/quick_edits_controller.rb b/app/controllers/frameworks/evaluations/quick_edits_controller.rb new file mode 100644 index 000000000..cf782b8a2 --- /dev/null +++ b/app/controllers/frameworks/evaluations/quick_edits_controller.rb @@ -0,0 +1,38 @@ +class Frameworks::Evaluations::QuickEditsController < Frameworks::ApplicationController + include HasDateParams + + before_action :evaluation, only: %i[edit update] + + def edit + @quick_edit = @evaluation.quick_editor + end + + def update + @quick_edit = @evaluation.quick_editor(**quick_edit_params) + + if @quick_edit.valid? + @quick_edit.save! + redirect_to frameworks_root_path(anchor: "evaluations"), notice: "Evaluation updated" + else + render :edit + end + end + +private + + def evaluation + @evaluation = Frameworks::Evaluation.find(params[:evaluation_id]) + end + + def quick_edit_params + form_params + .except("next_key_date(3i)", "next_key_date(2i)", "next_key_date(1i)") + .merge(next_key_date: date_param(:quick_edit, :next_key_date).compact_blank) + .to_h + .symbolize_keys + end + + def form_params + params.require(:quick_edit).permit(:note, :next_key_date, :next_key_date_description) + end +end diff --git a/app/controllers/support/cases/contracts_controller.rb b/app/controllers/support/cases/contracts_controller.rb index fae5114e4..ee97f377a 100644 --- a/app/controllers/support/cases/contracts_controller.rb +++ b/app/controllers/support/cases/contracts_controller.rb @@ -2,7 +2,7 @@ module Support class Cases::ContractsController < Cases::ApplicationController before_action :set_back_url, only: %i[edit update] - include Concerns::HasDateParams + include HasDateParams include Concerns::HasInteraction def edit diff --git a/app/controllers/support/cases/procurement_details_controller.rb b/app/controllers/support/cases/procurement_details_controller.rb index eb1db7af7..98251ab05 100644 --- a/app/controllers/support/cases/procurement_details_controller.rb +++ b/app/controllers/support/cases/procurement_details_controller.rb @@ -2,7 +2,7 @@ module Support class Cases::ProcurementDetailsController < Cases::ApplicationController before_action :set_back_url, :set_enums - include Concerns::HasDateParams + include HasDateParams include Concerns::HasInteraction def edit diff --git a/app/controllers/support/cases/quick_edits_controller.rb b/app/controllers/support/cases/quick_edits_controller.rb index 5bf4ba25f..593399022 100644 --- a/app/controllers/support/cases/quick_edits_controller.rb +++ b/app/controllers/support/cases/quick_edits_controller.rb @@ -1,7 +1,7 @@ module Support module Cases class QuickEditsController < Cases::ApplicationController - include Concerns::HasDateParams + include HasDateParams before_action :back_url, only: %i[edit update] helper_method :back_to_param diff --git a/app/controllers/support/cases/summaries_controller.rb b/app/controllers/support/cases/summaries_controller.rb index 75273c6fe..1a0b21cbe 100644 --- a/app/controllers/support/cases/summaries_controller.rb +++ b/app/controllers/support/cases/summaries_controller.rb @@ -2,7 +2,7 @@ module Support class Cases::SummariesController < ::Support::Cases::ApplicationController before_action :set_back_url - include Concerns::HasDateParams + include HasDateParams def edit @case_summary = fields_pre_filled_in_params? ? current_case.summary(summary_params) : current_case.summary diff --git a/app/controllers/support/concerns/has_date_params.rb b/app/controllers/support/concerns/has_date_params.rb deleted file mode 100644 index 4ace263d9..000000000 --- a/app/controllers/support/concerns/has_date_params.rb +++ /dev/null @@ -1,19 +0,0 @@ -# Parse date fields as Dates -# -# Useful for extracting dates out of govuk_date_fields -module Support - module Concerns - module HasDateParams - extend ActiveSupport::Concern - - # @param form_param [Symbol] - # @param date_field [Symbol] - # - # @return [Hash] - def date_param(form_param, date_field) - date = params.fetch(form_param, {}).permit(date_field) - { day: date["#{date_field}(3i)"], month: date["#{date_field}(2i)"], year: date["#{date_field}(1i)"] } - end - end - end -end diff --git a/app/models/support/case/validation/has_next_key_date.rb b/app/models/concerns/validation/has_next_key_date.rb similarity index 91% rename from app/models/support/case/validation/has_next_key_date.rb rename to app/models/concerns/validation/has_next_key_date.rb index ee957aa31..4322f4b64 100644 --- a/app/models/support/case/validation/has_next_key_date.rb +++ b/app/models/concerns/validation/has_next_key_date.rb @@ -1,4 +1,4 @@ -module Support::Case::Validation::HasNextKeyDate +module Validation::HasNextKeyDate extend ActiveSupport::Concern included do diff --git a/app/models/frameworks/activity_event.rb b/app/models/frameworks/activity_event.rb index cb19aba88..e0689d030 100644 --- a/app/models/frameworks/activity_event.rb +++ b/app/models/frameworks/activity_event.rb @@ -1,6 +1,8 @@ class Frameworks::ActivityEvent < ApplicationRecord include Frameworks::Activity + scope :added_notes, ->(subject) { joins(:activity_log_item).where(event: "note_added", activity_log_item: { subject: }) } + def loaded_data OpenStruct.new(**data, **activity_log_item.subject.try(:activity_event_data_for, self).presence || {}) end diff --git a/app/models/frameworks/evaluation.rb b/app/models/frameworks/evaluation.rb index e1d2c67d5..3d22f83d6 100644 --- a/app/models/frameworks/evaluation.rb +++ b/app/models/frameworks/evaluation.rb @@ -6,6 +6,8 @@ class Frameworks::Evaluation < ApplicationRecord include StatusChangeable include Presentable include ActivityLogPresentable + include Noteable + include QuickEditable belongs_to :framework has_one :provider, through: :framework diff --git a/app/models/frameworks/evaluation/noteable.rb b/app/models/frameworks/evaluation/noteable.rb new file mode 100644 index 000000000..9d5e1b941 --- /dev/null +++ b/app/models/frameworks/evaluation/noteable.rb @@ -0,0 +1,18 @@ +module Frameworks::Evaluation::Noteable + extend ActiveSupport::Concern + + def add_note(note) + log_activity_event("note_added", body: note) + end + + def latest_note + event = Frameworks::ActivityEvent.added_notes(self).last + return if event.nil? + + OpenStruct.new( + body: event.loaded_data.body, + author: event.activity_log_item.actor.initials, + date: event.created_at.strftime("%d %b %y"), + ) + end +end diff --git a/app/models/frameworks/evaluation/presentable.rb b/app/models/frameworks/evaluation/presentable.rb index 969e71c59..52e45f1d2 100644 --- a/app/models/frameworks/evaluation/presentable.rb +++ b/app/models/frameworks/evaluation/presentable.rb @@ -40,4 +40,10 @@ def contact_email def contact_phone contact.try(:phone) end + + def next_key_date_formatted + return "Not set" if next_key_date.blank? + + next_key_date.strftime("%d/%m/%Y") + end end diff --git a/app/models/frameworks/evaluation/quick_editable.rb b/app/models/frameworks/evaluation/quick_editable.rb new file mode 100644 index 000000000..af6fa99d0 --- /dev/null +++ b/app/models/frameworks/evaluation/quick_editable.rb @@ -0,0 +1,14 @@ +module Frameworks::Evaluation::QuickEditable + extend ActiveSupport::Concern + + def quick_editor(note: latest_note&.body, next_key_date: self.next_key_date, next_key_date_description: self.next_key_date_description) + Frameworks::Evaluation::QuickEditor.new(frameworks_evaluation: self, note:, next_key_date:, next_key_date_description:) + end + + def quick_edit(details) + transaction do + update!(details.except(:note)) + add_note(details[:note]) if details[:note].present? && details[:note] != latest_note&.body + end + end +end diff --git a/app/models/frameworks/evaluation/quick_editor.rb b/app/models/frameworks/evaluation/quick_editor.rb new file mode 100644 index 000000000..e05b9f831 --- /dev/null +++ b/app/models/frameworks/evaluation/quick_editor.rb @@ -0,0 +1,15 @@ +class Frameworks::Evaluation::QuickEditor + include ActiveModel::Model + include ActiveModel::Attributes + include ActiveModel::Validations + include Validation::HasNextKeyDate + + attribute :frameworks_evaluation + attribute :note + attribute :next_key_date + attribute :next_key_date_description + + def save! + frameworks_evaluation.quick_edit(note:, next_key_date:, next_key_date_description:) + end +end diff --git a/app/models/support/case/quick_editor.rb b/app/models/support/case/quick_editor.rb index de99e267e..049362825 100644 --- a/app/models/support/case/quick_editor.rb +++ b/app/models/support/case/quick_editor.rb @@ -1,7 +1,7 @@ class Support::Case::QuickEditor include ActiveModel::Model include ActiveModel::Validations - include Support::Case::Validation::HasNextKeyDate + include Validation::HasNextKeyDate attr_accessor( :support_case, diff --git a/app/models/support/case/summary.rb b/app/models/support/case/summary.rb index 7530f0c54..e197ac557 100644 --- a/app/models/support/case/summary.rb +++ b/app/models/support/case/summary.rb @@ -2,7 +2,7 @@ class Support::Case::Summary include ActiveModel::Model include ActiveModel::Attributes include ActiveModel::Validations - include Support::Case::Validation::HasNextKeyDate + include Validation::HasNextKeyDate attribute :support_case attribute :request_type, :boolean diff --git a/app/views/frameworks/activity_log_items/activity/activity_event/evaluations/_note_added.html.erb b/app/views/frameworks/activity_log_items/activity/activity_event/evaluations/_note_added.html.erb new file mode 100644 index 000000000..99afdee5a --- /dev/null +++ b/app/views/frameworks/activity_log_items/activity/activity_event/evaluations/_note_added.html.erb @@ -0,0 +1,7 @@ +<% content_for :"#{activity.id}_subject" do %> + Note Added +<% end %> + +<% content_for :"#{activity.id}_description" do %> + <%= activity.loaded_data.body %> +<% end %> diff --git a/app/views/frameworks/evaluations/_evaluation.html.erb b/app/views/frameworks/evaluations/_evaluation.html.erb index 2d85d247d..75815dfb6 100644 --- a/app/views/frameworks/evaluations/_evaluation.html.erb +++ b/app/views/frameworks/evaluations/_evaluation.html.erb @@ -1,44 +1,86 @@
-
-
-
-
Case ID
-
<%= link_to evaluation.reference, frameworks_evaluation_path(evaluation, back_to: current_url_b64(:evaluations)), class: "govuk-link", "data-turbo" => false %>
-
+
+
+
+
+
+
Case ID
+
<%= link_to evaluation.reference, frameworks_evaluation_path(evaluation, back_to: current_url_b64(:evaluations)), class: "govuk-link", "data-turbo" => false %>
+
-
-
Framework Name
-
<%= evaluation.framework_name %>
-
+
+
Framework Name
+
<%= evaluation.framework_name %>
+
-
-
Framework Provider
-
<%= evaluation.framework_provider_name %>
-
+
+
Framework Provider
+
<%= evaluation.framework_provider_name %>
+
-
-
Categories
-
<%= evaluation.framework_category_names %>
+
+
Categories
+
<%= evaluation.framework_category_names %>
+
+
-
-
-
-
-
-
Status
-
<%= evaluation.display_status %>
+
+
+
+
Status
+
<%= evaluation.display_status %>
+
+ +
+
Assignee
+
<%= evaluation.display_assignee %>
+
+ +
+
Updated
+
<%= evaluation.display_last_updated %>
+
+
+
-
-
Assignee
-
<%= evaluation.display_assignee %>
+
+
+

+ <%= link_to "Quick edit", edit_frameworks_evaluation_quick_edit_path(evaluation, back_to: current_url_b64(:evaluations)), class: "govuk-link govuk-link--no-visited-state", "target" => "_top" %> +

+
-
-
Updated
-
<%= evaluation.display_last_updated %>
+
+
+ <% if evaluation.latest_note.present? %> + <%= simple_format("Note: #{evaluation.latest_note.date} #{evaluation.latest_note.author} - " + evaluation.latest_note.body, class: "govuk-body") %> + <% end %> +
+
+ <% if evaluation.next_key_date.present? %> +
+
+
+ <%= I18n.t("support.case.label.next_key_date.label") %> +
+
+ <%= evaluation.next_key_date_formatted %> +
+
+
+
+ <%= I18n.t("support.case.label.next_key_date.description2") %> +
+
+ <%= simple_format(evaluation.next_key_date_description, class: "govuk-body") %> +
+
+
+ <% end %>
-
+
diff --git a/app/views/frameworks/evaluations/quick_edits/edit.html.erb b/app/views/frameworks/evaluations/quick_edits/edit.html.erb new file mode 100644 index 000000000..5a0bb358f --- /dev/null +++ b/app/views/frameworks/evaluations/quick_edits/edit.html.erb @@ -0,0 +1,22 @@ +<%= content_for :title, "GHBS | Frameworks | Evaluation #{@evaluation.reference} | Quick edit" %> + +
+
+

<%= I18n.t("frameworks.evaluation.quick_edit.header", reference: @evaluation.reference) %>

+ <%= form_with model: @quick_edit, + scope: :quick_edit, + url: frameworks_evaluation_quick_edit_path(back_to: Base64.encode64(@back_url)), + method: :patch do |form| %> + <%= form.govuk_error_summary %> + + <%= form.govuk_text_area :note, rows: 5, label: { text: I18n.t("frameworks.evaluation.quick_edit.add_note"), size: "m" } %> + + <%= render "support/cases/components/next_key_date", form: form %> + +
+ <%= form.submit I18n.t("generic.button.save"), class: "govuk-button", role: "button" %> + <%= link_to "Exit without saving", @back_url, class: "govuk-link govuk-link--no-visited-state" %> +
+ <% end %> +
+
diff --git a/config/locales/en.yml b/config/locales/en.yml index d8b608368..2a0d5ec26 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -688,6 +688,11 @@ en: email: Email full_name: Full name header: Enter your contact details + frameworks: + evaluation: + quick_edit: + header: Quick edit evaluation %{reference} + add_note: Add a note generic: button: back: Back diff --git a/config/locales/validation/support/en.yml b/config/locales/validation/support/en.yml index aa239b586..edf750c30 100644 --- a/config/locales/validation/support/en.yml +++ b/config/locales/validation/support/en.yml @@ -41,6 +41,10 @@ en: attributes: next_key_date: blank: Enter a date for the description + frameworks/evaluation/quick_editor: + attributes: + next_key_date: + blank: Enter a date for the description forms: rules: diff --git a/config/routes.rb b/config/routes.rb index 80f10bfba..3aad6bb12 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -334,6 +334,7 @@ resources :evaluations do scope module: :evaluations do resource :contacts, only: %i[edit update] + resource :quick_edit, only: %i[edit update] end end resources :frameworks do diff --git a/db/migrate/20231027145735_add_next_key_date_and_description_to_frameworks_evaluations.rb b/db/migrate/20231027145735_add_next_key_date_and_description_to_frameworks_evaluations.rb new file mode 100644 index 000000000..01bbb42e1 --- /dev/null +++ b/db/migrate/20231027145735_add_next_key_date_and_description_to_frameworks_evaluations.rb @@ -0,0 +1,8 @@ +class AddNextKeyDateAndDescriptionToFrameworksEvaluations < ActiveRecord::Migration[7.0] + def change + change_table :frameworks_evaluations, bulk: true do |t| + t.column :next_key_date, :date + t.column :next_key_date_description, :string + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 185a84ba6..9bccdd257 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.0].define(version: 2023_10_19_085509) do +ActiveRecord::Schema[7.0].define(version: 2023_10_27_145735) do create_sequence "evaluation_refs" create_sequence "framework_refs" @@ -289,6 +289,8 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.boolean "action_required", default: false + t.date "next_key_date" + t.string "next_key_date_description" end create_table "frameworks_framework_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| diff --git a/spec/features/frameworks/evaluations/agent_can_quick_edit_framework_evaluation_spec.rb b/spec/features/frameworks/evaluations/agent_can_quick_edit_framework_evaluation_spec.rb new file mode 100644 index 000000000..14e469a4a --- /dev/null +++ b/spec/features/frameworks/evaluations/agent_can_quick_edit_framework_evaluation_spec.rb @@ -0,0 +1,29 @@ +require "rails_helper" + +describe "Agent can quick edit a framework evaluation", js: true do + include_context "with a framework evaluation agent" + + let!(:framework_evaluation) { create(:frameworks_evaluation, reference: "FE1") } + + before do + visit frameworks_root_path(anchor: "evaluations") + within("#evaluations") { click_link "Quick edit" } + end + + context "when changing the note and next key date" do + before do + fill_in "Add a note", with: "New note" + fill_in "Day", with: "10" + fill_in "Month", with: "08" + fill_in "Year", with: "2023" + fill_in "Description of next key date", with: "Key event" + click_button "Save" + end + + it "persists the changes" do + expect(framework_evaluation.latest_note.body).to eq("New note") + expect(framework_evaluation.reload.next_key_date).to eq(Date.parse("2023-08-10")) + expect(framework_evaluation.reload.next_key_date_description).to eq("Key event") + end + end +end diff --git a/spec/models/support/case/validation/has_next_key_date_spec.rb b/spec/models/support/case/validation/has_next_key_date_spec.rb index f4316314e..a40e4e732 100644 --- a/spec/models/support/case/validation/has_next_key_date_spec.rb +++ b/spec/models/support/case/validation/has_next_key_date_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -describe Support::Case::Validation::HasNextKeyDate do +describe Validation::HasNextKeyDate do subject(:has_next_key_date) { Support::Case::QuickEditor.new(params) } let(:next_key_date) { nil }