Skip to content

Commit

Permalink
Merge pull request rubyforgood#5414 from rubyforgood/5015-draft-suppo…
Browse files Browse the repository at this point in the history
…rt-nov

Draft and Multi-page Support for Case Contact Form
  • Loading branch information
schoork authored Jan 15, 2024
2 parents df79c32 + 959a3e2 commit f535df1
Show file tree
Hide file tree
Showing 57 changed files with 1,716 additions and 1,523 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ gem "sprockets-rails" # The original asset pipeline for Rails [https://github.co
gem "stimulus-rails"
gem "strong_migrations"
gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby] # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "wicked"
gem "rswag-api"
gem "rswag-ui"
gem "blueprinter" # for JSON serialization
Expand Down
5 changes: 4 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,8 @@ GEM
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
wicked (2.0.0)
railties (>= 3.0.7)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.12)
Expand Down Expand Up @@ -611,9 +613,10 @@ DEPENDENCIES
view_component
web-console (>= 3.3.0)
webmock
wicked

RUBY VERSION
ruby 3.2.2p53

BUNDLED WITH
2.4.19
2.4.22
30 changes: 30 additions & 0 deletions app/components/form/title_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<div class="title-wrapper pt-30">
<div class="title mb-30">
<div class="align-items-baseline d-md-flex gap-3">
<h1><%= @title %></h1>
<% if @autosave %>
<small data-autosave-target="alert"></small>
<% end %>
</div>

<div class="row align-items-center mt-5">
<h2 class="col-12 col-md-6"><%= @subtitle %></h2>
<% if @progress %>
<div class="col-12 col-md-6 align-items-center d-flex gap-2 mt-2 mt-md-0">
<p class="col-auto">
<%= @steps_in_text %>
</p>
<div class="col">
<div class="progress">
<div class="progress-bar primary-bg" role="progressbar" style="width: <%= @progress %>%" aria-valuenow="<%= @progress %>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
<% end %>
</div>

<p class="mt-2">
<%= @notes %>
</p>
</div>
</div>
15 changes: 15 additions & 0 deletions app/components/form/title_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

class Form::TitleComponent < ViewComponent::Base
def initialize(title:, subtitle:, step: nil, total_steps: nil, notes: nil, autosave: false)
@title = title
@subtitle = subtitle
@notes = notes
@autosave = autosave

if step && total_steps
@steps_in_text = "Step #{step} of #{total_steps}"
@progress = (step.to_d / total_steps.to_d) * 100
end
end
end
145 changes: 145 additions & 0 deletions app/controllers/case_contacts/form_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
class CaseContacts::FormController < ApplicationController
include Wicked::Wizard

before_action :set_progress
before_action :require_organization!
after_action :verify_authorized

steps(*CaseContact::FORM_STEPS)

# wizard_path
def show
@case_contact = CaseContact.find(params[:case_contact_id])
authorize @case_contact
get_cases_and_contact_types
@page = wizard_steps.index(step) + 1
@total_pages = steps.count

render_wizard
wizard_path
end

def update
@case_contact = CaseContact.find(params[:case_contact_id])
authorize @case_contact
params[:case_contact][:status] = step.to_s unless @case_contact.active?
remove_unwanted_contact_types
remove_nil_draft_ids
if @case_contact.update(case_contact_params)
respond_to do |format|
format.html {
if step == steps.last
finish_editing
else
render_wizard @case_contact, {}, {case_contact_id: @case_contact.id}
end
}
format.json { head :ok }
end
else
respond_to do |format|
format.html {
get_cases_and_contact_types
render step
}
format.json { head :internal_server_error }
end
end
end

private

def get_cases_and_contact_types
@casa_cases = policy_scope(current_organization.casa_cases)
@casa_cases = @casa_cases.where(id: @case_contact.casa_case_id) if @case_contact.active?

@selected_case_contact_types = @casa_cases.flat_map(&:contact_types)

@current_organization_groups =
if @selected_case_contact_types.present?
@selected_case_contact_types.map(&:contact_type_group).uniq
else
current_organization.contact_types_by_group
end
end

def finish_editing
message = ""
send_reimbursement_email(@case_contact)
if @case_contact.active?
message = @case_contact.decorate.form_updated_message
else
message = "Case #{"contact".pluralize(@case_contact.draft_case_ids.count)} successfully created."
create_additional_case_contacts(@case_contact)
first_casa_case_id = @case_contact.draft_case_ids.slice(0)
@case_contact.update(status: "active", draft_case_ids: [first_casa_case_id], casa_case_id: first_casa_case_id)
end
update_volunteer_address(@case_contact)
flash[:notice] = message
redirect_back_to_referer(fallback_location: case_contacts_path(success: true))
end

def send_reimbursement_email(case_contact)
if case_contact.should_send_reimbursement_email?
SupervisorMailer.reimbursement_request_email(case_contact.creator, case_contact.supervisor).deliver_later
end
end

def update_volunteer_address(case_contact)
return unless case_contact.volunteer_address.present? && !case_contact.address_field_disabled?

address = case_contact.volunteer.address || case_contact.volunteer.build_address
address.update(content: case_contact.volunteer_address)
end

# Makes a copy of the draft for all selected cases not including the first one. The draft becomes the contact for
# the first case.
#
# Duplication does not duplicate associated records, so if other associations are made in the form, they need to be
# added here, explicitly (ie. case_contact_contact_type, additional_expenses). Alternatively, could look at a gem
# that does deep associations.
def create_additional_case_contacts(case_contact)
case_contact.draft_case_ids.drop(1).each do |casa_case_id|
new_case_contact = case_contact.dup
new_case_contact.status = "active"
new_case_contact.draft_case_ids = [casa_case_id]
new_case_contact.casa_case_id = casa_case_id
case_contact.case_contact_contact_type.each do |ccct|
new_case_contact.case_contact_contact_type.new(contact_type_id: ccct.contact_type_id)
end
case_contact.additional_expenses.each do |ae|
new_case_contact.additional_expenses.new(
other_expense_amount: ae.other_expense_amount,
other_expenses_describe: ae.other_expenses_describe
)
end
new_case_contact.save!
end
end

def case_contact_params
CaseContactParameters.new(params)
end

# Deletes the current associations (from the join table) only if the submitted form body has the parameters for
# the contact_type ids.
def remove_unwanted_contact_types
if params.dig(:case_contact, :case_contact_contact_type_attributes)
@case_contact.case_contact_contact_type.destroy_all
end
end

def remove_nil_draft_ids
if params.dig(:case_contact, :draft_case_ids)
params[:case_contact][:draft_case_ids] -= [""]
end
end

def set_progress
@progress = if wizard_steps.any? && wizard_steps.index(step).present?
((wizard_steps.index(step) + 1).to_d / wizard_steps.count.to_d) * 100
else
0
end
end
end
Loading

0 comments on commit f535df1

Please sign in to comment.