Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/controllers/dashboards_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class DashboardsController < ApplicationController
def index
@shared_patients = eager_loaded_patients.shared_patients(current_line)
@unconfirmed_support_patients = eager_loaded_patients.unconfirmed_practical_support(current_line)
@follow_up_patients = eager_loaded_patients.follow_ups_due.where(line: current_line)
end

def search
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/patients_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ def respond_to_update_for_json_format

PATIENT_DASHBOARD_PARAMS = [
:name, :last_menstrual_period_days, :last_menstrual_period_weeks,
:appointment_date, :primary_phone, :pronouns
:appointment_date, :primary_phone, :pronouns,
:follow_up_date, :follow_up_reason
].freeze

PATIENT_INFORMATION_PARAMS = [
Expand Down
5 changes: 5 additions & 0 deletions app/models/patient.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ class Patient < ApplicationRecord

validate :special_circumstances_length

# Follow-up
validates :follow_up_reason, length: { maximum: 200 }

scope :follow_ups_due, -> { where('follow_up_date <= ?', Date.current).where.not(follow_up_date: nil) }

# Methods
def self.pledged_status_summary(line)
plucked_attrs = [:fund_pledge, :pledge_sent, :id, :name, :appointment_date, :fund_pledged_at]
Expand Down
37 changes: 37 additions & 0 deletions app/views/dashboards/_follow_ups.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<% if patients.present? %>
<div class="card mt-3" id="follow-ups-due">
<div class="card-header bg-warning">
<h4 class="mb-0">
<span class="fas fa-bell mr-1"></span>
<%= t('dashboard.partial_titles.follow_ups') %>
<span class="badge badge-warning"><%= patients.count %></span>
</h4>
</div>
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead>
<tr>
<th><%= t('patient.shared.name') %></th>
<th><%= t('patient.dashboard.follow_up_date') %></th>
<th><%= t('patient.dashboard.follow_up_reason') %></th>
<th></th>
</tr>
</thead>
<tbody>
<% patients.each do |patient| %>
<tr class="<%= 'table-danger' if patient.follow_up_date < Date.current %>">
<td><%= link_to patient.name, edit_patient_path(patient) %></td>
<td><%= patient.follow_up_date %></td>
<td><%= patient.follow_up_reason %></td>
<td>
<%= link_to edit_patient_path(patient), class: 'btn btn-sm btn-outline-primary' do %>
<span class="fas fa-phone"></span> <%= t('dashboard.call') %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<% end %>
2 changes: 2 additions & 0 deletions app/views/dashboards/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
table_type: 'unconfirmed_support',
patients: @unconfirmed_support_patients,
autosortable: true } %>
<%= render partial: 'dashboards/follow_ups',
locals: { patients: @follow_up_patients } %>
<%= render partial: 'dashboards/modal' %>
<%= render partial: 'dashboards/activity_log' %>
</div>
Expand Down
14 changes: 14 additions & 0 deletions app/views/patients/_patient_dashboard.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@
</div>
</div>

<div class="row">
<div class="col-4">
<%= f.date_field :follow_up_date,
label: t('patient.dashboard.follow_up_date'),
autocomplete: 'off' %>
</div>
<div class="col">
<%= f.text_field :follow_up_reason,
label: t('patient.dashboard.follow_up_reason'),
autocomplete: 'off',
placeholder: t('patient.dashboard.follow_up_reason_placeholder') %>
</div>
</div>

<div class="row">
<div class="col-4">
<%= f.text_field :primary_phone,
Expand Down
5 changes: 5 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ en:
dashboard:
activity_log:
label: "%{current_line} Line Activity Log"
call: Call
budget_bar:
pledged: pledged
pledged_item: "%{amount} pledged"
Expand All @@ -234,6 +235,7 @@ en:
search_results: Search results
shared_cases: Shared cases
unconfirmed_support: Unconfirmed Practical Support
follow_ups: Follow-Ups Due
search:
header: Build your call list
input_placeholder: Name (Susan Smith) or Phone (555-555-5555)
Expand Down Expand Up @@ -428,6 +430,9 @@ en:
delete_label: 'Admin: Delete'
phone: Phone number
weeks_along: Weeks along at intake
follow_up_date: Follow-up date
follow_up_reason: Follow-up reason
follow_up_reason_placeholder: e.g. Check on funding status
data_entry:
patient_entry: Patient Entry
helper:
Expand Down
7 changes: 7 additions & 0 deletions db/migrate/20250101000002_add_follow_up_fields_to_patients.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AddFollowUpFieldsToPatients < ActiveRecord::Migration[8.1]
def change
add_column :patients, :follow_up_date, :date
add_column :patients, :follow_up_reason, :string, limit: 200
add_index :patients, :follow_up_date
end
end
42 changes: 42 additions & 0 deletions test/controllers/dashboards_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,48 @@ class DashboardsControllerTest < ActionDispatch::IntegrationTest
end
end

describe 'follow-up patients on dashboard' do
before do
@follow_up_patient = create :patient,
name: 'Follow Up Needed',
line: @line,
follow_up_date: 1.day.ago.to_date,
follow_up_reason: 'Check insurance status'
end

it 'should include follow-up patients in the dashboard' do
get dashboard_path
assert_response :success
assert_match(/Follow Up Needed/, response.body)
end

it 'should not include patients without a follow-up date' do
get dashboard_path
assert_response :success
assert_no_match(/Susie Everyteen/, response.body.scan(/follow-ups-due.*?<\/div>/m).join)
end

it 'should not include patients with future follow-up dates' do
@follow_up_patient.update! follow_up_date: 3.days.from_now.to_date
get dashboard_path
assert_response :success
assert_no_match(/Follow Up Needed/, response.body.scan(/follow-ups-due.*?<\/div>/m).join)
end

it 'should not include follow-up patients from a different line' do
other_line = create :line, name: 'Other Line'
create :patient,
name: 'Other Line Patient',
line: other_line,
follow_up_date: 1.day.ago.to_date,
follow_up_reason: 'Belongs to other line'
get dashboard_path
assert_response :success
follow_ups_html = response.body.scan(/follow-ups-due.*?<\/table>/m).join
assert_no_match(/Other Line Patient/, follow_ups_html)
end
end

describe 'search method' do
it 'should return on name, primary phone, and other phone' do
['Susie Everyteen', '123-456-7890', '333-444-5555'].each do |searcher|
Expand Down
40 changes: 40 additions & 0 deletions test/controllers/patients_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,46 @@ class PatientsControllerTest < ActionDispatch::IntegrationTest
end
end

describe 'update follow-up fields' do
before do
@payload = {
follow_up_date: 3.days.from_now.to_date.strftime('%Y-%m-%d'),
follow_up_reason: 'Waiting on insurance callback'
}
end

it 'should update follow-up fields via XHR' do
patch patient_path(@patient), params: { patient: @payload }, xhr: true
@patient.reload
assert_equal 3.days.from_now.to_date, @patient.follow_up_date
assert_equal 'Waiting on insurance callback', @patient.follow_up_reason
end

it 'should update follow-up fields via JSON' do
patch patient_path(@patient), params: { patient: @payload }, as: :json
@patient.reload
assert_equal 3.days.from_now.to_date, @patient.follow_up_date
assert_equal 'Waiting on insurance callback', @patient.follow_up_reason
end

it 'should clear follow-up fields' do
@patient.update! follow_up_date: 1.day.from_now.to_date,
follow_up_reason: 'Old reason'
patch patient_path(@patient),
params: { patient: { follow_up_date: '', follow_up_reason: '' } },
xhr: true
@patient.reload
assert_nil @patient.follow_up_date
assert_equal '', @patient.follow_up_reason
end

it 'should not allow unauthenticated access' do
delete destroy_user_session_path
patch patient_path(@patient), params: { patient: @payload }, xhr: true
assert_response :redirect
end
end

describe 'pledge method' do
it 'should respond success on completion' do
get submit_pledge_path(@patient), xhr: true
Expand Down
58 changes: 58 additions & 0 deletions test/models/patient_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -727,4 +727,62 @@ def patient_to_hash(patient)
pledge_sent_at: patient.pledge_sent_at
}
end

describe 'follow_ups_due scope' do
before do
@patient_due = create :patient
@patient_due.update_columns(follow_up_date: 1.day.ago)

@patient_future = create :patient
@patient_future.update_columns(follow_up_date: 1.day.from_now)

@patient_today = create :patient
@patient_today.update_columns(follow_up_date: Date.current)

@patient_nil = create :patient
end

it 'should include patients with past follow_up_date' do
assert_includes Patient.follow_ups_due, @patient_due
end

it 'should include patients with follow_up_date of today' do
assert_includes Patient.follow_ups_due, @patient_today
end

it 'should exclude patients with future follow_up_date' do
refute_includes Patient.follow_ups_due, @patient_future
end

it 'should exclude patients with nil follow_up_date' do
refute_includes Patient.follow_ups_due, @patient_nil
end
end

describe 'follow_up validations' do
it 'should allow follow_up_reason up to 200 chars' do
patient = build :patient
patient.follow_up_reason = 'a' * 200
assert patient.valid?
end

it 'should reject follow_up_reason over 200 chars' do
patient = build :patient
patient.follow_up_reason = 'a' * 201
refute patient.valid?
end

it 'should allow blank follow_up_reason' do
patient = build :patient
patient.follow_up_reason = ''
assert patient.valid?
end

it 'should allow follow_up_date without a reason' do
patient = build :patient
patient.follow_up_date = 2.days.from_now.to_date
patient.follow_up_reason = nil
assert patient.valid?
end
end
end