Skip to content
Merged
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
36 changes: 18 additions & 18 deletions app/services/event_dashboard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,16 @@ def scholarship_applicants
FUNDER_UNFUNDED_LABEL = "Unfunded".freeze

# One funder bucket for the recipients page "group by funder" view: the funder
# name, the donor record behind it (an Organization or Person — nil for the
# unfunded / no-scholarship buckets), that donor's "City, State", and the
# name, the funder record behind it (an Organization or Person — nil for the
# unfunded / no-scholarship buckets), that funder's "City, State", and the
# applicants in the bucket.
FunderGroup = Struct.new(:name, :donor, :location, :people, keyword_init: true) do
FunderGroup = Struct.new(:name, :funder, :location, :people, keyword_init: true) do
def count = people.size
end

# Scholarship applicants bucketed by their scholarship's funder (the grant's
# donor), as ordered FunderGroups — alphabetical by funder with the "Unfunded"
# and "No scholarship yet" buckets pinned last. Grants from the same donor
# funder), as ordered FunderGroups — alphabetical by funder with the "Unfunded"
# and "No scholarship yet" buckets pinned last. Grants from the same funder
# share a bucket. People within a group keep #scholarship_applicants'
# display-name order.
def scholarship_applicants_by_funder
Expand Down Expand Up @@ -1050,38 +1050,38 @@ def scholarship_applicant_ids
@scholarship_applicant_ids ||= active_registrations.where(scholarship_requested: true).pluck(:registrant_id)
end

# Grouping key for an applicant's funder: the donor identity when the
# scholarship is drawn from a grant (so a donor's grants share a bucket), else
# Grouping key for an applicant's funder: the funder identity when the
# scholarship is drawn from a grant (so a funder's grants share a bucket), else
# the unfunded / no-scholarship bucket.
def funder_key_for(person)
scholarship = scholarship_by_recipient[person.id]
return :none unless scholarship
donor = scholarship.grant&.donor
return :unfunded unless donor
[ donor.class.name, donor.id ]
funder = scholarship.grant&.funder
return :unfunded unless funder
[ funder.class.name, funder.id ]
end

# Builds a FunderGroup from a bucket of applicants that share a funder, reading
# the funder name, donor, and location from any member's scholarship (they're
# the funder name, funder, and location from any member's scholarship (they're
# identical across the bucket).
def build_applicant_funder_group(people)
scholarship = scholarship_by_recipient[people.first.id]
grant = scholarship&.grant
donor = grant&.donor
funder = grant&.funder
name = if scholarship.nil?
FUNDER_NONE_LABEL
else
grant&.funder_name.presence || FUNDER_UNFUNDED_LABEL
end
FunderGroup.new(name: name, donor: donor, location: donor_location(donor), people: people)
FunderGroup.new(name: name, funder: funder, location: funder_location(funder), people: people)
end

# "City, State" from the donor's first active address — works for either an
# Organization or a Person funder (both are addressable). Nil when the donor
# "City, State" from the funder's first active address — works for either an
# Organization or a Person funder (both are addressable). Nil when the funder
# has no address or isn't addressable.
def donor_location(donor)
return unless donor.respond_to?(:addresses)
address = donor.addresses.active.first
def funder_location(funder)
return unless funder.respond_to?(:addresses)
address = funder.addresses.active.first
return unless address
[ address.city, address.state ].compact_blank.join(", ").presence
end
Expand Down
8 changes: 4 additions & 4 deletions app/views/events/recipients.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
</span>
<span>Show scholarship status</span>
</button>
<%# Group the roster by funder (the scholarship's grant donor), with a
<%# Group the roster by funder (the scholarship's grant funder), with a
header per funder — or return to the flat, name-ordered list. %>
<% if params[:group_by] == "funder" %>
<%= link_to recipients_event_path(@event),
Expand Down Expand Up @@ -101,12 +101,12 @@
<% @dashboard.scholarship_applicants_by_funder.each do |group| %>
<section class="break-inside-avoid">
<%# Bold, dark-fuchsia funder band so each group's header stands out.
The funder name links to the donor's profile (org or person). %>
The funder name links to the funder's profile (org or person). %>
<div class="flex flex-wrap items-center gap-x-3 gap-y-1 rounded-lg bg-fuchsia-800 px-4 py-2.5 mb-4 text-white">
<i class="fa-solid fa-hand-holding-heart text-fuchsia-200"></i>
<h2 class="text-lg font-bold tracking-tight">
<% if group.donor && allowed_to?(:show?, group.donor) %>
<%= link_to polymorphic_path(group.donor), data: { turbo_frame: "_top" },
<% if group.funder && allowed_to?(:show?, group.funder) %>
<%= link_to polymorphic_path(group.funder), data: { turbo_frame: "_top" },
class: "inline-flex items-center gap-1.5 hover:underline" do %>
<%= group.name %>
<i class="fa-solid fa-arrow-up-right-from-square text-xs text-fuchsia-200"></i>
Expand Down
4 changes: 2 additions & 2 deletions spec/models/event_registration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ def registration_with_scholarship
describe ".funder_name" do
it "matches registrations funded by a scholarship whose grant funder name matches" do
matching_reg = create(:event_registration)
grant = create(:grant, funder: create(:organization, name: "Big Donor Foundation"))
grant = create(:grant, funder: create(:organization, name: "Big Funder Foundation"))
scholarship = create(:scholarship, grant: grant, recipient: matching_reg.registrant)
create(:allocation, source: scholarship, allocatable: matching_reg, amount: 0)

Expand All @@ -269,7 +269,7 @@ def registration_with_scholarship
recipient: other_reg.registrant)
create(:allocation, source: other, allocatable: other_reg, amount: 0)

results = EventRegistration.funder_name("big donor")
results = EventRegistration.funder_name("big funder")
expect(results).to include(matching_reg)
expect(results).not_to include(other_reg)
end
Expand Down
4 changes: 2 additions & 2 deletions spec/requests/events_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2924,15 +2924,15 @@ def ce_chip_text
registration = event.event_registrations.find_by(registrant: applicant)
org = create(:organization, name: "Joyful Heart Foundation")
create(:address, addressable: org, city: "Los Angeles", state: "CA")
grant = create(:grant, name: "Healing Arts Fund", donor: org, amount_cents: 100_000)
grant = create(:grant, name: "Healing Arts Fund", funder: org, amount_cents: 100_000)
scholarship = create(:scholarship, recipient: applicant, grant: grant, amount_cents: 1_000)
create(:allocation, source: scholarship, allocatable: registration, amount: 1_000)

get recipients_event_path(event, group_by: "funder")

expect(response).to have_http_status(:ok)
page = Capybara.string(response.body)
# The funder name renders as a section header linking to the donor's profile,
# The funder name renders as a section header linking to the funder's profile,
# with its city/state alongside.
expect(page).to have_css("h2 a[href='#{organization_path(org)}']", text: "Joyful Heart Foundation")
expect(page).to have_content("Los Angeles, CA")
Expand Down
12 changes: 6 additions & 6 deletions spec/services/event_dashboard_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -511,12 +511,12 @@
end

describe "#scholarship_applicants_by_funder" do
it "buckets applicants by their scholarship's grant funder, unfunded last, carrying the donor and its city/state" do
it "buckets applicants by their scholarship's grant funder, unfunded last, carrying the funder and its city/state" do
embedded_reg = event.event_registrations.find_by(registrant: embedded_applicant)
separate_reg = event.event_registrations.find_by(registrant: separate_applicant)
donor = create(:organization, name: "Joyful Heart Foundation")
create(:address, addressable: donor, city: "Los Angeles", state: "CA")
grant = create(:grant, name: "Healing Arts", donor: donor, amount_cents: 100_000)
funder = create(:organization, name: "Joyful Heart Foundation")
create(:address, addressable: funder, city: "Los Angeles", state: "CA")
grant = create(:grant, name: "Healing Arts", funder: funder, amount_cents: 100_000)
funded = create(:scholarship, recipient: embedded_applicant, grant: grant, amount_cents: 1_000)
create(:allocation, source: funded, allocatable: embedded_reg, amount: 1_000)
unfunded = create(:scholarship, recipient: separate_applicant, amount_cents: 1_000)
Expand All @@ -526,10 +526,10 @@

expect(groups.map(&:name)).to eq([ "Joyful Heart Foundation", "Unfunded" ])
expect(groups.first.people).to eq([ embedded_applicant ])
expect(groups.first.donor).to eq(donor)
expect(groups.first.funder).to eq(funder)
expect(groups.first.location).to eq("Los Angeles, CA")
expect(groups.last.people).to eq([ separate_applicant ])
expect(groups.last.donor).to be_nil
expect(groups.last.funder).to be_nil
end

it "collects applicants with no awarded scholarship under 'No scholarship yet'" do
Expand Down