Skip to content

Commit

Permalink
900 - AdopterFoster: Add adopted pets (#910)
Browse files Browse the repository at this point in the history
* Adopted Pets controller, Route, Policy setup

* Basic layout for adopted pets

* Added functionality to display files for adopted pets

* Test cases for Adopted pets

* Lint fix

* Code cleanup by adding files view in shared folder

* Removed unwanted helper file

* Moves attachmet files logic to new controller for code clean up

* Lint fix

* Updated adopted_pets query to fetch details from Match model

* Test fix to create match record

* Fix policy test

* Lint fix

* Removed integration and system test files for adopted_pets

* Controller spec

* Lint fix

* Fix failing test

* Lint fix
  • Loading branch information
sarvaiyanidhi authored Sep 6, 2024
1 parent 2f6d742 commit 3470658
Show file tree
Hide file tree
Showing 14 changed files with 277 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Organizations
module AdopterFosterer
module AdoptedPets
class FilesController < Organizations::BaseController
skip_verify_authorized only: %i[index]

def index
@pet = Pet.find(params[:adopted_pet_id])
respond_to do |format|
format.html # Regular HTML response
format.turbo_stream do
render turbo_stream: turbo_stream.replace(
"pet_files",
partial: "organizations/shared/file_attachment_table",
locals: {pet: @pet}
)
end
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Organizations
module AdopterFosterer
class AdoptedPetsController < Organizations::BaseController
before_action :context_authorize!
layout "adopter_foster_dashboard"

def index
@adopted_pets = authorized_scope(Match.adoptions, with: Organizations::AdopterFosterer::MatchPolicy)
end

private

def context_authorize!
authorize! with: Organizations::AdopterFosterer::MatchPolicy
end
end
end
end
21 changes: 21 additions & 0 deletions app/javascript/controllers/card_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ["card"]

connect() {
this.defaultBackgroundColor = "bg-white"
this.selectedBackgroundColor = "bg-gray-300"
}

selectCard(event) {
this.cardTargets.forEach(card => {
card.classList.remove(this.selectedBackgroundColor)
card.classList.add(this.defaultBackgroundColor)
})

const selectedCard = event.currentTarget.querySelector(".card")
selectedCard.classList.remove(this.defaultBackgroundColor)
selectedCard.classList.add(this.selectedBackgroundColor)
}
}
1 change: 1 addition & 0 deletions app/models/concerns/authorizable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def permission?(name)
withdraw_adopter_applications
purge_avatar
manage_likes
view_adopted_pets
].freeze

FOSTERER_PERMISSIONS = %i[
Expand Down
4 changes: 4 additions & 0 deletions app/policies/application_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ def verify_active_staff!
deny! if user.staff_account.deactivated?
end

def verify_adopter_foster_account!
deny! unless user.adopter_foster_account
end

def permission?(name)
return false unless user

Expand Down
15 changes: 15 additions & 0 deletions app/policies/organizations/adopter_fosterer/match_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Organizations
module AdopterFosterer
class MatchPolicy < ApplicationPolicy
pre_check :verify_adopter_foster_account!

relation_scope do |relation|
relation.where(adopter_foster_account_id: user.adopter_foster_account.id)
end

def index?
permission?(:view_adopted_pets)
end
end
end
end
6 changes: 6 additions & 0 deletions app/views/layouts/adopter_foster_dashboard.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@
<%= active_link_to adopter_fosterer_likes_path, class: "nav-link" do %>
<i class="fe fe-heart nav-icon"></i> Liked Pets
<% end %>
</li>
<!-- Nav item -->
<li class="nav-item">
<%= active_link_to adopter_fosterer_adopted_pets_path, class: "nav-link" do %>
<i class="fe fe-star nav-icon"></i> Adopted Pets
<% end %>
</li>
<!-- Nav item -->
<li class="nav-item">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<%= render DashboardPageComponent.new do |c| %>
<% c.with_header_title { "Adopted Pets" } %>
<% c.with_body do %>
<div data-controller="card">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 mb-5">
<% @adopted_pets.each_with_index do |adopted_pet, index| %>
<div class="col">
<%= link_to adopter_fosterer_adopted_pet_files_path(adopted_pet.pet), data: { turbo_stream: true, action: "click->card#selectCard" } do %>
<div class="card h-100 bg-white border-0 shadow-sm" data-card-target="card">
<div class="card-body d-flex align-items-center">
<div class="me-3 flex-shrink-0" style="width: 50px; height: 50px;">
<%= image_tag(adopted_pet.pet.images.attached? ? adopted_pet.pet.images.first : 'coming_soon.jpg',
class: 'rounded-circle',
style: "width: 100%; height: 100%; object-fit: cover;") %>
</div>
<h5 class="card-title mb-0"><%= adopted_pet.pet.name %></h5>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
</div>
<div id="pet_files">
<!-- This will be replaced with the pet files table when a pet is clicked -->
</div>
<% end %>
<% end %>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

<div id="pet_files">
<div class="card">
<!-- table -->
<div class="table-responsive overflow-y-hidden">
Expand All @@ -25,12 +25,14 @@
rails_blob_path(file, disposition: 'attachment'),
class: 'btn btn-outline-success' %>

<%= link_to t('general.delete'),
staff_purge_attachment_path(file.id),
data: {
turbo_confirm: "Do you want to delete this image?",
turbo_method: "delete" },
class: 'btn btn-outline-danger' %>
<% if current_user&.staff_account %>
<%= link_to t('general.delete'),
staff_purge_attachment_path(file.id),
data: {
turbo_confirm: "Do you want to delete this image?",
turbo_method: "delete" },
class: 'btn btn-outline-danger' %>
<% end %>
</td>
</tr>
<% end %>
Expand All @@ -45,3 +47,4 @@
</table>
</div>
</div>
</div>
2 changes: 1 addition & 1 deletion app/views/organizations/staff/pets/tabs/_files.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="mb-4">
<%= render "attachment_form", instance: @pet, title: 'Files', url: attach_files_staff_pet_path(@pet), attachment_type: 'files' %>
<%= render "file_attachment_table", pet: @pet %>
<%= render "organizations/shared/file_attachment_table", pet: @pet %>
</div>
3 changes: 3 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@
resources :dashboard, only: [:index]
resources :likes, only: [:index, :create, :destroy]
resources :adopter_applications, path: "applications", only: %i[index create update]
resources :adopted_pets, only: [:index] do
resources :files, only: [:index], module: :adopted_pets
end
end
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require "test_helper"
require "action_policy/test_helper"

class Organizations::AdopterFosterer::AdoptedPetsControllerTest < ActionDispatch::IntegrationTest
context "authorization" do
include ActionPolicy::TestHelper

setup do
@organization = ActsAsTenant.current_tenant
@adopter_foster_account = create(:adopter_foster_account)
sign_in @adopter_foster_account.user
end

context "#index" do
should "be authorized" do
get adopter_fosterer_adopted_pets_url
assert_response :success
end

should "have authorized scope" do
ActsAsTenant.with_tenant(@organization) do
assert_have_authorized_scope(
type: :active_record_relation,
with: Organizations::AdopterFosterer::MatchPolicy
) do
get adopter_fosterer_adopted_pets_url
end
end
end

should "return only adoption matches for the user's adopter_fosterer_account" do
ActsAsTenant.with_tenant(@organization) do
user_adoption_match = create(:match, :adoption, adopter_foster_account: @adopter_foster_account, organization: @organization)
other_account_adoption_match = create(:match, :adoption, organization: @organization)
other_org_adoption_match = create(:match, :adoption)

get adopter_fosterer_adopted_pets_url

assert_includes assigns(:adopted_pets), user_adoption_match
assert_not_includes assigns(:adopted_pets), other_account_adoption_match
assert_not_includes assigns(:adopted_pets), other_org_adoption_match
end
end
end
end
end
10 changes: 10 additions & 0 deletions test/factories/pets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,15 @@
)
end
end

trait :with_files do
after(:build) do |pet|
pet.files.attach(
io: File.open(Rails.root.join("test", "fixtures", "files", "test2.png")),
filename: "test2.png",
content_type: "image/png"
)
end
end
end
end
91 changes: 91 additions & 0 deletions test/policies/organizations/adopter_fosterer/match_policy_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
require "test_helper"

class Organizations::AdopterFosterer::MatchPolicyTest < ActiveSupport::TestCase
include PetRescue::PolicyAssertions

setup do
@organization = ActsAsTenant.current_tenant
@policy = -> {
Organizations::AdopterFosterer::MatchPolicy.new(
Match, user: @user, organization: @organization
)
}
end

context "relation_scope" do
setup do
@user = create(:user)
@adopter_foster_account = create(:adopter_foster_account, user: @user, organization: @organization)
@pet = create(:pet)
@adopted_application = create(:adopter_application, adopter_foster_account: @adopter_foster_account, pet: @pet, status: :adoption_made)
@match = create(:match, adopter_foster_account: @adopter_foster_account, pet: @pet, match_type: :adoption)

@other_user = create(:user)
@other_account = create(:adopter_foster_account, user: @other_user, organization: @organization)
@other_pet = create(:pet)
create(:adopter_application, adopter_foster_account: @other_account, pet: @other_pet, status: :adoption_made)
@other_match = create(:match, adopter_foster_account: @other_account, pet: @other_pet, match_type: :adoption)

ActsAsTenant.with_tenant(create(:organization)) do
create(:match, adopter_foster_account: @adopter_foster_account, match_type: :adoption)
end
end

should "return only the user's adopted pets" do
scoped = @policy.call.apply_scope(Match.all, type: :active_record_relation)

assert_equal 1, scoped.count
assert_includes scoped, @match
end
end

context "rules" do
context "#index?" do
setup do
@action = -> { @policy.call.apply(:index?) }
end

context "when user is nil" do
setup { @user = nil }
should "return false" do
assert_equal false, @action.call
end
end

context "when user is adopter" do
setup do
@user = create(:adopter)
@adopter_foster_account = create(:adopter_foster_account, user: @user, organization: @organization)
end

should "return true" do
assert_equal true, @action.call
end
end

context "when user is staff" do
setup { @user = create(:admin) }
should "return false" do
assert_equal false, @action.call
end
end
end
end

context "pre_check" do
setup do
@user = create(:user)
@action = -> { @policy.call.apply(:index?) }
end

context "when user has an adopter_foster_account" do
setup do
create(:adopter_foster_account, user: @user, organization: @organization)
end

should "not raise an error" do
assert_nothing_raised { @action.call }
end
end
end
end

0 comments on commit 3470658

Please sign in to comment.