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
10 changes: 5 additions & 5 deletions app/controllers/product_drive_participants_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
class ProductDriveParticipantsController < ApplicationController
include Importable

# TODO: Should there be a :destroy action for this?

def index
@product_drive_participants = current_organization.product_drive_participants.includes(:donations).with_volumes.order(:business_name)
@products = View::ProductDriveParticipants.new(params: params, organization: current_organization)

respond_to do |format|
format.html
format.csv { send_data ProductDriveParticipant.generate_csv(@product_drive_participants), filename: "ProductDriveParticipants-#{Time.zone.today}.csv" }
format.csv { send_data ProductDriveParticipant.generate_csv(@products.participants), filename: "ProductDriveParticipants-#{Time.zone.today}.csv" }
end
end

Expand Down Expand Up @@ -65,6 +63,8 @@ def product_drive_participant_params

helper_method \
def filter_params
{}
return {} unless params.key?(:filters)

params.require(:filters).permit(:by_business_name, :by_contact_name)
end
end
5 changes: 4 additions & 1 deletion app/models/product_drive_participant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@

class ProductDriveParticipant < ApplicationRecord
has_paper_trail
include Provideable
include Filterable
include Geocodable
include Provideable

has_many :donations, inverse_of: :product_drive_participant, dependent: :destroy

Expand All @@ -30,6 +31,8 @@ class ProductDriveParticipant < ApplicationRecord
validates :comment, length: { maximum: 500 }

scope :alphabetized, -> { order(:contact_name) }
scope :by_business_name, ->(business_name) { where("business_name ILIKE ?", "%#{business_name}%") }
scope :by_contact_name, ->(contact_name) { where("contact_name ILIKE ?", "%#{contact_name}%") }
scope :with_volumes, -> {
left_joins(donations: :line_items)
.select("product_drive_participants.*, SUM(COALESCE(line_items.quantity, 0)) AS volume")
Expand Down
33 changes: 33 additions & 0 deletions app/models/view/product_drive_participants.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module View
class ProductDriveParticipants
attr_reader :filters, :params, :participants

def initialize(params:, organization:)
@params = params
@filters = filter_params(params)

@participants = organization
.product_drive_participants
.includes(:donations)
.with_volumes
.class_filter(filters)
.order(:business_name)
end

def filter_params(params = {})
if params.key?(:filters)
params.require(:filters).permit(:by_business_name, :by_contact_name)
else
{}
end
end

def selected_business_name
filters[:by_business_name]
end

def selected_contact_name
filters[:by_contact_name]
end
end
end
36 changes: 33 additions & 3 deletions app/views/product_drive_participants/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,36 @@
</div><!-- /.container-fluid -->
</section>

<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Product Drive Participants Filters</h3>
</div>
<div class="card-body">
<%= form_tag(product_drive_participants_path, method: :get) do |f| %>
<div class="row">
<div class="form-group col-lg-3 col-md-4 col-sm-6 col-xs-12">
<%= filter_text(label: "Filter By Business Name", scope: :by_business_name, selected: @products.selected_business_name) %>
</div>
<div class="form-group col-lg-3 col-md-4 col-sm-6 col-xs-12">
<%= filter_text(label: "Filter By Contact Name", scope: :by_contact_name, selected: @products.selected_contact_name) %>
</div>
</div>
</div>
<div class="card-footer">
<%= filter_button %>
<%= clear_filter_button %>
</div>
<% end %>
</div>
</div>
<div class="col-md-6">
</div>
</div>
</div>

<section class="content">
<div class="container-fluid">
<div class="row">
Expand All @@ -31,8 +61,8 @@
<div class="card card-primary">
<div class="card-footer">
<div class="pull-right">
<%= modal_button_to("#csvImportModal", {icon: "upload", text: "Import Product Drive Participants", size: "md"}) if @product_drive_participants.empty? %>
<%= download_button_to(product_drive_participants_path(format: :csv, filters: filter_params.merge(date_range: date_range_params)), {text: "Export Product Drive Participants", size: "md"}) if @product_drive_participants.any? %>
<%= modal_button_to("#csvImportModal", {icon: "upload", text: "Import Product Drive Participants", size: "md"}) if @products.participants.empty? %>
<%= download_button_to(product_drive_participants_path(format: :csv, filters: filter_params.merge(date_range: date_range_params)), {text: "Export Product Drive Participants", size: "md"}) if @products.participants.any? %>

<%= new_button_to new_product_drive_participant_path, text: "New Product Drive Participant" %>
</div>
Expand Down Expand Up @@ -64,7 +94,7 @@
</tr>
</thead>
<tbody>
<%= render partial: "product_drive_participant_row", collection: @product_drive_participants %>
<%= render partial: "product_drive_participant_row", collection: @products.participants %>
</tbody>
</table>
</div>
Expand Down
22 changes: 22 additions & 0 deletions spec/models/product_drive_participant_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
RSpec.describe ProductDriveParticipant, type: :model do
it_behaves_like "provideable"

it { expect(ProductDriveParticipant).to respond_to :class_filter }

context "Validations" do
it "is invalid unless it has either a phone number or an email" do
expect(build(:product_drive_participant, :no_contact_name_or_email, contact_name: "George Henry")).not_to be_valid
Expand Down Expand Up @@ -49,6 +51,26 @@
expect(subject.first.volume).to eq(10)
end
end

describe ".by_business_name" do
it "returns the product drive participant with a given business name" do
cats_biz = create(:product_drive_participant, business_name: "Cats")
another_cats_biz = create(:product_drive_participant, business_name: "I like cats")
create(:product_drive_participant, business_name: "Dogs")

expect(ProductDriveParticipant.by_business_name("Cats")).to match_array([cats_biz, another_cats_biz])
end
end

describe ".by_contact_name" do
it "returns the product drive participant with a given contact name" do
eleanor = create(:product_drive_participant, contact_name: "Eleanor Shellstrop")
donna = create(:product_drive_participant, contact_name: "Donna Shellstrop")
create(:product_drive_participant, contact_name: "Jason Mendoza")

expect(ProductDriveParticipant.by_contact_name("Shellstrop")).to match_array([eleanor, donna])
end
end
end

context "Methods" do
Expand Down
23 changes: 23 additions & 0 deletions spec/models/view/product_drive_participants_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
RSpec.describe View::ProductDriveParticipants do
# filtering logic is covered by the ProductDriveParticipant model and ProductDriveParticipants request specs

describe "selected filter params" do
it "returns the given filter params" do

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test for the actual participants / filtering logic?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see it's more or less covered by the request spec - probably good with a comment to that effect then.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's also covered by the model scope tests. I added a comment to make that clear in 2602355. Let me know if you have other thoughts. Thanks for the review!

organization = build(:organization)
build(:product_drive_participant, organization:)
params = ActionController::Parameters.new(
{
filters: {
by_business_name: "The Good Place",
by_contact_name: "Jason Mendoza"
}
}
).permit!

requests = View::ProductDriveParticipants.new(params:, organization:)

expect(requests.selected_business_name).to eq("The Good Place")
expect(requests.selected_contact_name).to eq("Jason Mendoza")
end
end
end
40 changes: 39 additions & 1 deletion spec/requests/product_drive_participants_requests_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
let(:organization) { create(:organization) }
let(:user) { create(:user, organization: organization) }

context "While signed in" do
context "when signed in" do
before do
sign_in(user)
end
Expand All @@ -25,6 +25,44 @@
let(:response_format) { 'csv' }

it { is_expected.to be_successful }

it "exports the filtered results" do
create(:product_drive_participant, business_name: "A business named after cats")
create(:product_drive_participant, business_name: "Dogs are cool")

get product_drive_participants_path(format: :csv, params: {filters: { by_business_name: 'cats'}})

csv = CSV.parse(response.body, headers: true)

expect(csv.count).to eq(1)
expect(csv.first["Business Name"]).to eq("A business named after cats")
end
end

context "when filtering by business name" do
it 'displays only results that matches the business name' do
create(:product_drive_participant, business_name: "A business named after cats")
create(:product_drive_participant, business_name: "Dogs are cool")

get product_drive_participants_path(filters: {by_business_name: 'Cats'})

expect(response).to be_successful
expect(response.body).to include("Cats")
expect(response.body).not_to include("Dogs")
end
end

context "when filtering by contact name" do
it 'displays only results that matches the contact name' do
create(:product_drive_participant, contact_name: "Bob Cat")
create(:product_drive_participant, contact_name: "Freddie the dog")

get product_drive_participants_path(filters: {by_contact_name: 'Cats'})

expect(response).to be_successful
expect(response.body).to include("Cats")
expect(response.body).not_to include("Dogs")
end
end
end

Expand Down
Loading