'
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index 556066bdf0..3e873fa48d 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -23,6 +23,7 @@ require('select2/dist/css/select2')
require('src/case_contact')
require('src/case_emancipation')
+require('src/casa_case')
require('src/emancipations')
require('src/select')
require('src/dashboard')
diff --git a/app/javascript/src/casa_case.js b/app/javascript/src/casa_case.js
new file mode 100644
index 0000000000..89cc7eb1ab
--- /dev/null
+++ b/app/javascript/src/casa_case.js
@@ -0,0 +1,15 @@
+function add_court_mandate_input () {
+ const list = '#mandates-list-container'
+ const index = $(`${list} textarea`).length
+
+ const textarea_html = ``
+
+ $(list).append(textarea_html)
+ $(list).children(':last').trigger('focus')
+}
+
+$('document').ready(() => {
+ $('button#add-mandate-button').on('click', add_court_mandate_input)
+})
diff --git a/app/javascript/src/dashboard.js b/app/javascript/src/dashboard.js
index 368697c747..07cb29c3c7 100644
--- a/app/javascript/src/dashboard.js
+++ b/app/javascript/src/dashboard.js
@@ -1,5 +1,5 @@
/* global $ */
-var defineCaseContactsTable = function () {
+const defineCaseContactsTable = function () {
$('table#case_contacts').DataTable(
{
scrollX: true,
@@ -9,7 +9,7 @@ var defineCaseContactsTable = function () {
)
}
-var defineSupervisorsDataTable = function () {
+const defineSupervisorsDataTable = function () {
$('table#supervisors').DataTable(
{
columnDefs: [
@@ -29,11 +29,11 @@ $('document').ready(() => {
return true
}
- var statusArray = []
- var assignedToVolunteerArray = []
- var assignedToMoreThanOneVolunteerArray = []
- var assignedToTransitionYouthArray = []
- var caseNumberPrefixArray = []
+ const statusArray = []
+ const assignedToVolunteerArray = []
+ const assignedToMoreThanOneVolunteerArray = []
+ const assignedToTransitionYouthArray = []
+ const caseNumberPrefixArray = []
$('.status-options').find('input[type="checkbox"]').each(function () {
if ($(this).is(':checked')) {
@@ -65,12 +65,12 @@ $('document').ready(() => {
}
})
- var status = data[3]
- var assignedToVolunteer = (data[5] !== '' && data[5].split(',').length >= 1) ? 'Yes' : 'No'
- var assignedToMoreThanOneVolunteer = (data[5] !== '' && data[5].split(',').length > 1) ? 'Yes' : 'No'
- var assignedToTransitionYouth = data[4]
- var regex = /^(CINA|TPR)/g
- var caseNumberPrefix = data[0].match(regex) ? data[0].match(regex)[0] : ''
+ const status = data[3]
+ const assignedToVolunteer = (data[5] !== '' && data[5].split(',').length >= 1) ? 'Yes' : 'No'
+ const assignedToMoreThanOneVolunteer = (data[5] !== '' && data[5].split(',').length > 1) ? 'Yes' : 'No'
+ const assignedToTransitionYouth = data[4]
+ const regex = /^(CINA|TPR)/g
+ const caseNumberPrefix = data[0].match(regex) ? data[0].match(regex)[0] : ''
if (statusArray.includes(status) &&
assignedToVolunteerArray.includes(assignedToVolunteer) &&
@@ -106,7 +106,7 @@ $('document').ready(() => {
const editSupervisorPath = id => `/supervisors/${id}/edit`
const editVolunteerPath = id => `/volunteers/${id}/edit`
const casaCasePath = id => `/casa_cases/${id}`
- var volunteersTable = $('table#volunteers').DataTable({
+ const volunteersTable = $('table#volunteers').DataTable({
autoWidth: false,
stateSave: false,
columns: [
@@ -226,12 +226,12 @@ $('document').ready(() => {
// Because the table saves state, we have to check/uncheck modal inputs based on what
// columns are visible
volunteersTable.columns().every(function (index) {
- var columnVisible = this.visible()
+ const columnVisible = this.visible()
if (columnVisible) { $('#visibleColumns input[data-column="' + index + '"]').prop('checked', true) } else { $('#visibleColumns input[data-column="' + index + '"]').prop('checked', false) }
})
- var casaCasesTable = $('table#casa-cases').DataTable({
+ const casaCasesTable = $('table#casa-cases').DataTable({
autoWidth: false,
stateSave: false,
columnDefs: [],
@@ -241,7 +241,7 @@ $('document').ready(() => {
})
casaCasesTable.columns().every(function (index) {
- var columnVisible = this.visible()
+ const columnVisible = this.visible()
if (columnVisible) {
$('#visibleColumns input[data-column="' + index + '"]').prop('checked', true)
@@ -279,11 +279,11 @@ $('document').ready(() => {
$('input.toggle-visibility').on('click', function (e) {
// Get the column API object and toggle the visibility
- var column = volunteersTable.column($(this).attr('data-column'))
+ const column = volunteersTable.column($(this).attr('data-column'))
column.visible(!column.visible())
volunteersTable.columns.adjust().draw()
- var caseColumn = casaCasesTable.column($(this).attr('data-column'))
+ const caseColumn = casaCasesTable.column($(this).attr('data-column'))
caseColumn.visible(!caseColumn.visible())
casaCasesTable.columns.adjust().draw()
})
diff --git a/app/javascript/src/emancipations.js b/app/javascript/src/emancipations.js
index d193ee3346..cb34391add 100644
--- a/app/javascript/src/emancipations.js
+++ b/app/javascript/src/emancipations.js
@@ -1,5 +1,5 @@
$('document').ready(() => {
- var emancipationsTable = $('table#all-case-emancipations').DataTable({
+ const emancipationsTable = $('table#all-case-emancipations').DataTable({
autoWidth: false,
searching: false,
stateSave: false,
diff --git a/app/javascript/src/stylesheets/pages/casa_cases.scss b/app/javascript/src/stylesheets/pages/casa_cases.scss
index dd0f4bd337..85ee36f0cd 100644
--- a/app/javascript/src/stylesheets/pages/casa_cases.scss
+++ b/app/javascript/src/stylesheets/pages/casa_cases.scss
@@ -19,4 +19,47 @@ body.casa_cases {
width: 130px;
padding-bottom: 15px;
}
+
+ .court-mandates {
+ textarea {
+ display: block;
+ margin-bottom: 5px;
+ }
+
+ textarea, .add-court-mandate {
+ width: 500px;
+ }
+ }
+
+ .add-court-mandate-container {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ gap: 10px;
+ }
+
+ #add-mandate-button {
+ background-color: #{$primary};
+ color: white;
+
+ font-size: 1.25em;
+
+ padding: 0.25em 1.5em;
+ border-radius: 10px;
+ border: none;
+
+ :hover {
+ cursor: pointer;
+ }
+ }
}
+
+@media only screen and (max-width: 640px) {
+ body.casa_cases {
+ .court-mandates {
+ textarea, .add-court-mandate {
+ width: 100%;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/models/casa_case.rb b/app/models/casa_case.rb
index 7fbd716742..e24672e5f5 100644
--- a/app/models/casa_case.rb
+++ b/app/models/casa_case.rb
@@ -38,6 +38,9 @@ class CasaCase < ApplicationRecord
has_many :contact_types, through: :casa_case_contact_types, source: :contact_type
accepts_nested_attributes_for :casa_case_contact_types
+ has_many :case_court_mandates, -> { order "id asc" }, dependent: :destroy
+ accepts_nested_attributes_for :case_court_mandates, reject_if: :all_blank
+
enum court_report_status: {not_submitted: 0, submitted: 1, in_review: 2, completed: 3}, _prefix: :court_report
scope :ordered, -> { order(updated_at: :desc) }
diff --git a/app/models/case_court_mandate.rb b/app/models/case_court_mandate.rb
new file mode 100644
index 0000000000..9fe3c91f1a
--- /dev/null
+++ b/app/models/case_court_mandate.rb
@@ -0,0 +1,24 @@
+class CaseCourtMandate < ApplicationRecord
+ belongs_to :casa_case
+
+ validates :mandate_text, presence: true
+end
+
+# == Schema Information
+#
+# Table name: case_court_mandates
+#
+# id :bigint not null, primary key
+# mandate_text :string
+# created_at :datetime not null
+# updated_at :datetime not null
+# casa_case_id :bigint not null
+#
+# Indexes
+#
+# index_case_court_mandates_on_casa_case_id (casa_case_id)
+#
+# Foreign Keys
+#
+# fk_rails_... (casa_case_id => casa_cases.id)
+#
diff --git a/app/policies/casa_case_policy.rb b/app/policies/casa_case_policy.rb
index a6eb887373..aebc533e1d 100644
--- a/app/policies/casa_case_policy.rb
+++ b/app/policies/casa_case_policy.rb
@@ -47,6 +47,7 @@ def can_see_filters?
alias_method :update_hearing_type?, :admin_or_supervisor?
alias_method :update_judge?, :admin_or_supervisor?
alias_method :update_court_report_due_date?, :admin_or_supervisor?
+ alias_method :update_court_mandates?, :admin_or_supervisor?
def permitted_attributes
common_attrs = [
@@ -58,8 +59,10 @@ def permitted_attributes
case @user
when CasaAdmin
common_attrs.concat(%i[case_number birth_month_year_youth court_date court_report_due_date hearing_type_id judge_id])
+ common_attrs << {case_court_mandates_attributes: %i[mandate_text id]}
when Supervisor
common_attrs.concat(%i[court_date court_report_due_date hearing_type_id judge_id])
+ common_attrs << {case_court_mandates_attributes: %i[mandate_text id]}
else
common_attrs
end
diff --git a/app/views/casa_cases/_form.html.erb b/app/views/casa_cases/_form.html.erb
index c0e3ba229b..39f26b6115 100644
--- a/app/views/casa_cases/_form.html.erb
+++ b/app/views/casa_cases/_form.html.erb
@@ -115,6 +115,32 @@
CasaCase.court_report_statuses&.map { |status| [status.first.humanize, status.first] } %>
+ <% if casa_case.persisted? %>
+
+ <% end %>
+
<% if Pundit.policy(current_user, casa_case).update_contact_types? %>
diff --git a/babel.config.js b/babel.config.js
index 915b26cb1b..2510c3ba05 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -1,9 +1,9 @@
module.exports = function (api) {
- var validEnv = ['development', 'test', 'production']
- var currentEnv = api.env()
- var isDevelopmentEnv = api.env('development')
- var isProductionEnv = api.env('production')
- var isTestEnv = api.env('test')
+ const validEnv = ['development', 'test', 'production']
+ const currentEnv = api.env()
+ const isDevelopmentEnv = api.env('development')
+ const isProductionEnv = api.env('production')
+ const isTestEnv = api.env('test')
if (!validEnv.includes(currentEnv)) {
throw new Error(
diff --git a/db/migrate/20210223133248_create_case_court_mandates.rb b/db/migrate/20210223133248_create_case_court_mandates.rb
new file mode 100644
index 0000000000..90835cea82
--- /dev/null
+++ b/db/migrate/20210223133248_create_case_court_mandates.rb
@@ -0,0 +1,10 @@
+class CreateCaseCourtMandates < ActiveRecord::Migration[6.1]
+ def change
+ create_table :case_court_mandates do |t|
+ t.string :mandate_text
+ t.references :casa_case, foreign_key: true, null: false
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 3ca2d0acb2..dd82bd9acf 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.define(version: 2021_01_17_185614) do
+ActiveRecord::Schema.define(version: 2021_02_23_133248) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -145,6 +145,14 @@
t.check_constraint "(miles_driven IS NOT NULL) OR (NOT want_driving_reimbursement)", name: "want_driving_reimbursement_only_when_miles_driven"
end
+ create_table "case_court_mandates", force: :cascade do |t|
+ t.string "mandate_text"
+ t.bigint "casa_case_id", null: false
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["casa_case_id"], name: "index_case_court_mandates_on_casa_case_id"
+ end
+
create_table "contact_type_groups", force: :cascade do |t|
t.bigint "casa_org_id", null: false
t.string "name", null: false
@@ -289,6 +297,7 @@
add_foreign_key "case_assignments", "users", column: "volunteer_id"
add_foreign_key "case_contacts", "casa_cases"
add_foreign_key "case_contacts", "users", column: "creator_id"
+ add_foreign_key "case_court_mandates", "casa_cases"
add_foreign_key "emancipation_options", "emancipation_categories"
add_foreign_key "followups", "users", column: "creator_id"
add_foreign_key "judges", "casa_orgs"
diff --git a/spec/factories/casa_case.rb b/spec/factories/casa_case.rb
index 59743bae81..f3efeb98f3 100644
--- a/spec/factories/casa_case.rb
+++ b/spec/factories/casa_case.rb
@@ -7,6 +7,7 @@
hearing_type
judge
court_report_status { :not_submitted }
+ case_court_mandates { [] }
trait :with_case_assignments do
after(:create) do |casa_case, _|
diff --git a/spec/factories/case_court_mandates.rb b/spec/factories/case_court_mandates.rb
new file mode 100644
index 0000000000..8a5ba0aa61
--- /dev/null
+++ b/spec/factories/case_court_mandates.rb
@@ -0,0 +1,5 @@
+FactoryBot.define do
+ factory :case_court_mandate do
+ mandate_text { Faker::Lorem.paragraph(sentence_count: 5, supplemental: true, random_sentences_to_add: 20) }
+ end
+end
diff --git a/spec/models/casa_case_spec.rb b/spec/models/casa_case_spec.rb
index 28e4c4ac67..525c0d8b82 100644
--- a/spec/models/casa_case_spec.rb
+++ b/spec/models/casa_case_spec.rb
@@ -13,6 +13,7 @@
it { is_expected.to belong_to(:judge).optional }
it { is_expected.to validate_presence_of(:case_number) }
it { is_expected.to validate_uniqueness_of(:case_number).scoped_to(:casa_org_id).case_insensitive }
+ it { is_expected.to have_many(:case_court_mandates).dependent(:destroy) }
it { is_expected.to have_many(:volunteers).through(:case_assignments) }
describe ".ordered" do
diff --git a/spec/models/case_court_mandate_spec.rb b/spec/models/case_court_mandate_spec.rb
new file mode 100644
index 0000000000..06dfa53553
--- /dev/null
+++ b/spec/models/case_court_mandate_spec.rb
@@ -0,0 +1,9 @@
+require "rails_helper"
+
+RSpec.describe CaseCourtMandate, type: :model do
+ subject { build(:case_court_mandate) }
+
+ it { is_expected.to belong_to(:casa_case) }
+
+ it { is_expected.to validate_presence_of(:mandate_text) }
+end
diff --git a/spec/requests/casa_cases_spec.rb b/spec/requests/casa_cases_spec.rb
index 3c84b73495..eafec9ecff 100644
--- a/spec/requests/casa_cases_spec.rb
+++ b/spec/requests/casa_cases_spec.rb
@@ -7,6 +7,8 @@
let(:valid_attributes) { {case_number: "1234", transition_aged_youth: true, casa_org_id: organization.id, hearing_type_id: hearing_type.id, judge_id: judge.id} }
let(:invalid_attributes) { {case_number: nil} }
let(:casa_case) { create(:casa_case, casa_org: organization, case_number: "111") }
+ let(:mandate_texts) { ["1-New Mandate Text One", "0-New Mandate Text Two"] }
+ let(:mandates_attributes) { {"0" => {mandate_text: mandate_texts[0]}, "1" => {mandate_text: mandate_texts[1]}} }
before { sign_in user }
@@ -120,17 +122,44 @@
)
end
- context "with invalid parameters" do
- it "does not create a new CasaCase" do
- expect { post casa_cases_url, params: {casa_case: invalid_attributes} }.to change(
- CasaCase,
- :count
- ).by(0)
+ describe "invalid request" do
+ context "with invalid parameters" do
+ it "does not create a new CasaCase" do
+ expect { post casa_cases_url, params: {casa_case: invalid_attributes} }.to change(
+ CasaCase,
+ :count
+ ).by(0)
+ end
+
+ it "renders a successful response (i.e. to display the 'new' template)" do
+ post casa_cases_url, params: {casa_case: invalid_attributes}
+ expect(response).to be_successful
+ end
end
- it "renders a successful response (i.e. to display the 'new' template)" do
- post casa_cases_url, params: {casa_case: invalid_attributes}
- expect(response).to be_successful
+ context "with case_court_mandates_attributes being passed as a parameter" do
+ let(:invalid_params) do
+ attributes = valid_attributes
+ attributes[:case_court_mandates_attributes] = mandates_attributes
+ {casa_case: attributes}
+ end
+
+ it "Creates a new CasaCase, but no CaseCourtMandate" do
+ expect { post casa_cases_url, params: invalid_params }.to change(
+ CasaCase,
+ :count
+ ).by(1)
+
+ expect { post casa_cases_url, params: invalid_params }.not_to change(
+ CaseCourtMandate,
+ :count
+ )
+ end
+
+ it "renders a successful response (i.e. to display the 'new' template)" do
+ post casa_cases_url, params: {casa_case: invalid_params}
+ expect(response).to be_successful
+ end
end
end
end
@@ -140,7 +169,8 @@
{
case_number: "12345",
hearing_type_id: hearing_type.id,
- judge_id: judge.id
+ judge_id: judge.id,
+ case_court_mandates_attributes: mandates_attributes
}
}
@@ -151,6 +181,8 @@
expect(casa_case.case_number).to eq "12345"
expect(casa_case.hearing_type).to eq hearing_type
expect(casa_case.judge).to eq judge
+ expect(casa_case.case_court_mandates[0].mandate_text).to eq mandate_texts[0]
+ expect(casa_case.case_court_mandates[1].mandate_text).to eq mandate_texts[1]
end
it "redirects to the casa_case" do
@@ -167,6 +199,43 @@
end
end
+ describe "court mandates" do
+ context "when the user tries to make an existing mandate empty" do
+ let(:mandates_updated) do
+ {
+ case_court_mandates_attributes: {
+ "0" => {
+ mandate_text: "New Mandate Text One Updated"
+ },
+ "1" => {
+ mandate_text: ""
+ }
+ }
+ }
+ end
+
+ before do
+ patch casa_case_url(casa_case), params: {casa_case: new_attributes}
+ casa_case.reload
+
+ mandates_updated[:case_court_mandates_attributes]["0"][:id] = casa_case.case_court_mandates[0].id
+ mandates_updated[:case_court_mandates_attributes]["1"][:id] = casa_case.case_court_mandates[1].id
+ end
+
+ it "does not update the first mandate" do
+ expect { patch casa_case_url(casa_case), params: {casa_case: mandates_updated} }.not_to(
+ change { casa_case.reload.case_court_mandates[0].mandate_text }
+ )
+ end
+
+ it "does not update the second mandate" do
+ expect { patch casa_case_url(casa_case), params: {casa_case: mandates_updated} }.not_to(
+ change { casa_case.reload.case_court_mandates[1].mandate_text }
+ )
+ end
+ end
+ end
+
it "does not update across organizations" do
other_org = create(:casa_org)
other_casa_case = create(:casa_case, case_number: "abc", casa_org: other_org)
@@ -296,7 +365,8 @@
case_number: "12345",
court_report_status: :submitted,
hearing_type_id: hearing_type.id,
- judge_id: judge.id
+ judge_id: judge.id,
+ case_court_mandates_attributes: mandates_attributes
}
}
@@ -310,6 +380,7 @@
expect(casa_case.case_number).to eq "111"
expect(casa_case.hearing_type).to_not eq hearing_type
expect(casa_case.judge).to_not eq judge
+ expect(casa_case.case_court_mandates.size).to be 0
end
it "redirects to the casa_case" do
@@ -392,14 +463,18 @@
end
describe "PATCH /update" do
- let(:new_attributes) { {case_number: "12345", court_report_status: :completed} }
+ let(:new_attributes) { {case_number: "12345", court_report_status: :completed, case_court_mandates_attributes: mandates_attributes} }
context "with valid parameters" do
it "updates fields (except case_number)" do
patch casa_case_url(casa_case), params: {casa_case: new_attributes}
casa_case.reload
+
expect(casa_case.case_number).to eq "111"
expect(casa_case.court_report_completed?).to be true
+
+ expect(casa_case.case_court_mandates[0].mandate_text).to eq mandate_texts[0]
+ expect(casa_case.case_court_mandates[1].mandate_text).to eq mandate_texts[1]
end
it "redirects to the casa_case" do
diff --git a/spec/system/casa_cases/edit_spec.rb b/spec/system/casa_cases/edit_spec.rb
index 0664749791..f393871519 100644
--- a/spec/system/casa_cases/edit_spec.rb
+++ b/spec/system/casa_cases/edit_spec.rb
@@ -21,12 +21,16 @@
expect(casa_case).not_to be_court_report_submitted
end
- it "edits case" do
+ it "edits case", js: true do
visit casa_case_path(casa_case.id)
click_on "Edit Case Details"
expect(page).to have_select("Hearing type")
expect(page).to have_select("Judge")
select "Submitted", from: "casa_case_court_report_status"
+
+ page.find("#add-mandate-button").click
+ find("#mandates-list-container").first("textarea").send_keys("Court Mandate Text One")
+
click_on "Update CASA Case"
expect(page).to have_text("Submitted")
expect(page).to have_text("Court Date")
@@ -34,6 +38,7 @@
expect(page).to have_text("Day")
expect(page).to have_text("Month")
expect(page).to have_text("Year")
+ expect(page).to have_text("Court Mandate Text One")
expect(page).not_to have_text("Deactivate Case")
end
@@ -81,7 +86,7 @@
sign_in supervisor
end
- it "edits case" do
+ it "edits case", js: true do
visit casa_case_path(casa_case)
expect(page).to have_text("Court Report Status: Not submitted")
visit edit_casa_case_path(casa_case)
@@ -95,6 +100,9 @@
select "September", from: "casa_case_court_report_due_date_2i"
select next_year, from: "casa_case_court_report_due_date_1i"
+ page.find("#add-mandate-button").click
+ find("#mandates-list-container").first("textarea").send_keys("Court Mandate Text One")
+
click_on "Update CASA Case"
has_checked_field? "Youth"
has_no_checked_field? "Supervisor"
@@ -106,6 +114,7 @@
expect(page).to have_text("Year")
expect(page).to have_text("November")
expect(page).to have_text("September")
+ expect(page).to have_text("Court Mandate Text One")
visit casa_case_path(casa_case)
@@ -362,6 +371,8 @@ def sign_in_and_assign_volunteer
expect(page).not_to have_text("Year")
expect(page).not_to have_text("Deactivate Case")
+ expect(page).not_to have_css("#add-mandate-button")
+
visit casa_case_path(casa_case)
expect(page).to have_text("Court Report Status: Submitted")
end