diff --git a/app/components/task_list_items/assessment/meeting_component.rb b/app/components/task_list_items/assessment/meeting_component.rb new file mode 100644 index 0000000000..0e1e713d39 --- /dev/null +++ b/app/components/task_list_items/assessment/meeting_component.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module TaskListItems + module Assessment + class MeetingComponent < TaskListItems::BaseComponent + def initialize(planning_application:) + @planning_application = planning_application + end + + private + + attr_reader :planning_application + + delegate :meeting, to: :planning_application + + def link_text + "Meeting" + end + + def link_path + if planning_application + planning_application_assessment_meetings_path(@planning_application) + else + new_planning_application_assessment_meeting_path(@planning_application) + end + end + + def status_tag_component + StatusTags::BaseComponent.new( + status: @planning_application.meetings.last.status + ) + end + end + end +end diff --git a/app/controllers/planning_applications/assessment/meetings_controller.rb b/app/controllers/planning_applications/assessment/meetings_controller.rb new file mode 100644 index 0000000000..b923b3215a --- /dev/null +++ b/app/controllers/planning_applications/assessment/meetings_controller.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module PlanningApplications + module Assessment + class MeetingsController < AuthenticationController + before_action :set_planning_application + before_action :build_meeting, only: %i[new create] + + def index + @meetings = @planning_application.meetings + respond_to do |format| + format.html + end + end + + def show + respond_to do |format| + format.html + end + end + + def new + respond_to do |format| + format.html + end + end + + def create + respond_to do |format| + if @meeting.update(meeting_params) + format.html do + redirect_to planning_application_assessment_tasks_path(@planning_application), notice: t(".success") + end + else + format.html { render :new } + end + end + end + + private + + def meeting_params + params.require(:meeting) + .permit(:occurred_at, :comment) + .merge(created_by: current_user, status: "complete") + end + + def build_meeting + @meeting = @planning_application.meetings.new + end + end + end +end diff --git a/app/models/meeting.rb b/app/models/meeting.rb new file mode 100644 index 0000000000..130331b611 --- /dev/null +++ b/app/models/meeting.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class Meeting < ApplicationRecord + include DateValidateable + + belongs_to :created_by, class_name: "User" + + belongs_to :planning_application + + validates :status, presence: true + + validates :occurred_at, + presence: true, + date: { + on_or_before: :current + } + + enum :status, %i[ + not_started + complete + ].index_with(&:to_s) + + scope :by_created_at_desc, -> { order(created_at: :desc) } +end diff --git a/app/models/planning_application.rb b/app/models/planning_application.rb index a9bb0d2667..7756ddfa2c 100644 --- a/app/models/planning_application.rb +++ b/app/models/planning_application.rb @@ -52,6 +52,7 @@ class WithdrawOrCancelError < RuntimeError; end has_many :planning_application_constraints_queries has_many :constraints, through: :planning_application_constraints, source: :constraint has_many :site_histories + has_many :meetings, -> { by_created_at_desc } has_many :site_notices has_many :site_visits, -> { by_created_at_desc } has_many :policy_classes, -> { order(:section) } diff --git a/app/views/planning_applications/assessment/meetings/_form.html.erb b/app/views/planning_applications/assessment/meetings/_form.html.erb new file mode 100644 index 0000000000..ce428842ce --- /dev/null +++ b/app/views/planning_applications/assessment/meetings/_form.html.erb @@ -0,0 +1,18 @@ +<%= form_with( + model: @meeting, + class: "govuk-!-margin-top-5", + url: planning_application_assessment_meetings_path(@planning_application, @meeting), + method: :post + ) do |form| %> + + <%= form.govuk_date_field(:occurred_at, rows: 6, legend: {text: "Meeting date"}) %> + + <%= form.govuk_text_area(:comment, rows: 6, label: {text: "Add notes (optional)"}) %> + +
+ <%= form.submit "Save and mark as complete", class: "govuk-button govuk-button--primary" %> + + <%= back_link %> +
+ +<% end %> diff --git a/app/views/planning_applications/assessment/meetings/_overview.html.erb b/app/views/planning_applications/assessment/meetings/_overview.html.erb new file mode 100644 index 0000000000..a13068e62b --- /dev/null +++ b/app/views/planning_applications/assessment/meetings/_overview.html.erb @@ -0,0 +1,6 @@ +

Response created by: <%= meeting.created_by.name %>

+

Response created: <%= meeting.created_at.to_fs %>

+<% if meeting.valid? %> +

Meeting occured at: <%= meeting.occurred_at&.to_date&.to_fs %>

+<% end %> +

Comment: <%= meeting.comment %>

diff --git a/app/views/planning_applications/assessment/meetings/index.html.erb b/app/views/planning_applications/assessment/meetings/index.html.erb new file mode 100644 index 0000000000..44481b789f --- /dev/null +++ b/app/views/planning_applications/assessment/meetings/index.html.erb @@ -0,0 +1,37 @@ +<% content_for :page_title do %> + Meetings - <%= t("page_title") %> +<% end %> + +<%= render( + partial: "shared/proposal_header", + locals: {heading: "View meetings"} + ) %> + +<% if @meetings.any? %> +

Meetings

+ +
+ + + See previous meetings + + +
+ <% @meetings.each do |meeting| %> + <%= render "overview", meeting: meeting %> +

<%= govuk_link_to "View", planning_application_assessment_meeting_path(@planning_application, meeting) %>

+
+ <% end %> +
+
+<% else %> +

+ No meetings have been added yet. +

+<% end %> + +<%= govuk_link_to "Add new meeting", new_planning_application_assessment_meeting_path(@planning_application) %> + +
+ <%= back_link %> +
diff --git a/app/views/planning_applications/assessment/meetings/new.html.erb b/app/views/planning_applications/assessment/meetings/new.html.erb new file mode 100644 index 0000000000..77d1f460f9 --- /dev/null +++ b/app/views/planning_applications/assessment/meetings/new.html.erb @@ -0,0 +1,23 @@ +<% content_for :page_title do %> + Meeting - <%= t("page_title") %> +<% end %> + +<%= render( + partial: "shared/assessment_task_breadcrumbs", + locals: {planning_application: @planning_application} + ) %> +<% content_for :title, "Meeting" %> + +<%= render( + partial: "shared/proposal_header", + locals: {heading: "Add a meeting"} + ) %> +
+ + + Warning + This is NOT public. + +
+ +<%= render "form" %> diff --git a/app/views/planning_applications/assessment/tasks/_additional_services.html.erb b/app/views/planning_applications/assessment/tasks/_additional_services.html.erb index 2841e16b29..3018e732cb 100644 --- a/app/views/planning_applications/assessment/tasks/_additional_services.html.erb +++ b/app/views/planning_applications/assessment/tasks/_additional_services.html.erb @@ -8,5 +8,13 @@ ) ) %> <% end %> + + <% if @planning_application.additional_services.find_by(name: "meeting") %> + <%= render( + TaskListItems::Assessment::MeetingComponent.new( + planning_application: @planning_application + ) + ) %> + <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 967abb3e73..d4987b6cda 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1292,6 +1292,9 @@ en: success: Informative was successfully saved update: success: Informatives were successfully saved + meetings: + create: + success: Meeting record was successfully added. ownership_certificates: update: success: Ownership certificate was checked diff --git a/config/routes.rb b/config/routes.rb index af78197691..2546805a7b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -156,6 +156,8 @@ resources :site_visits, except: %i[destroy] + resources :meetings, except: %i[destroy] + resources :heads_of_terms, only: %i[index new] do get :edit, on: :collection get :edit diff --git a/db/migrate/20250128164930_create_meetings.rb b/db/migrate/20250128164930_create_meetings.rb new file mode 100644 index 0000000000..bfdb587741 --- /dev/null +++ b/db/migrate/20250128164930_create_meetings.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CreateMeetings < ActiveRecord::Migration[7.2] + def change + create_table :meetings do |t| + t.references :created_by, null: false, foreign_key: {to_table: :users}, type: :bigint + t.references :planning_application, foreign_key: true + t.string :status, default: "not_started", null: false + t.text :comment + t.datetime :occurred_at, null: false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 0028c93b92..68bdc3419f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -586,6 +586,18 @@ t.index ["local_policy_id"], name: "ix_local_policy_areas_on_local_policy_id" end + create_table "meetings", force: :cascade do |t| + t.bigint "created_by_id", null: false + t.bigint "planning_application_id" + t.string "status", default: "not_started", null: false + t.text "comment" + t.datetime "occurred_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["created_by_id"], name: "ix_meetings_on_created_by_id" + t.index ["planning_application_id"], name: "ix_meetings_on_planning_application_id" + end + create_table "neighbour_letter_batches", force: :cascade do |t| t.bigint "consultation_id" t.string "text" @@ -1146,6 +1158,8 @@ add_foreign_key "local_authority_policy_references", "local_authorities" add_foreign_key "local_policies", "planning_applications" add_foreign_key "local_policy_areas", "local_policies" + add_foreign_key "meetings", "planning_applications" + add_foreign_key "meetings", "users", column: "created_by_id" add_foreign_key "neighbour_letter_batches", "consultations" add_foreign_key "neighbour_letters", "neighbour_letter_batches", column: "batch_id" add_foreign_key "neighbour_letters", "neighbours" diff --git a/spec/models/meeting_spec.rb b/spec/models/meeting_spec.rb new file mode 100644 index 0000000000..808bf3f200 --- /dev/null +++ b/spec/models/meeting_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe Meeting, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end