Skip to content

Pk/mspca 46 animal availability frontend endpoint - #30

Open
pujitakalinadhabhotla wants to merge 9 commits into
mainfrom
pk/mspca-46-animal-availability-frontend-endpoint
Open

pujitakalinadhabhotla wants to merge 9 commits into
mainfrom
pk/mspca-46-animal-availability-frontend-endpoint

Conversation

@pujitakalinadhabhotla

Copy link
Copy Markdown

ℹ️ Issue

Closes https://mspca.atlassian.net/jira/software/projects/MSPCA/boards/1?filter=&groupBy=none&selectedIssue=MSPCA-46

📝 Description

Adds the Animal Availability page, where volunteers browse animals that need fostering, filter them, view details, and request to foster. Also adds the backend endpoint that supplies the animals.

Changes:

  1. GET /api/animals (animals.controller.ts, animals.service.ts, animals.module.ts): returns all animals, or [] when there are none. Covered by new controller and service tests.
  2. Match comments (1793300000000-AddMatchAdditionalComments.ts, matches.entity.ts, create-match.dto.ts, matches.service.ts): new optional additional_comments column on matches, accepted by POST /api/matches.
  3. Routing (routes.ts, app.tsx): ROUTES.ANIMAL_AVAILABILITY = '/animal-availability', rendering the new page.
  4. Page (pages/AnimalAvailability.tsx): fetches all animals once and shows a card grid with loading, error (with retry), and empty states. The map area is left blank.
  5. components/AnimalCard.tsx: reusable card showing the photo (placeholder), name, breed, restriction tags, age, and location, with a "See More" button.
  6. components/AnimalModal.tsx: opens from "See More" and shows full details plus a comments box. Apply sends the foster request through createMatch.
  7. components/AnimalFilters.tsx, utils/animals.ts: filter panel for Animal Type, Restrictions, and Location with Apply/Cancel, chips, and Clear Filters. Filtering runs in the browser on the already-fetched list, so changing a filter makes no new API request.
  8. components/RestrictionTag.tsx: colored tag pill used on the card and in the modal.
  9. api/apiClient.ts: added getAnimals and createMatch.
  10. theme.ts: added a card shadow token and the avatar purple from the Figma.
  11. utils/mockRestrictions.ts: temporary mock tags (see notes). The logo, placeholder photo, and two icons are in assets/.

✔️ Verification

  1. Run migrations: yarn typeorm:migrate
  2. Add a few animals in psql (no seed migration), for example:
    INSERT INTO animals (chameleon_animal_id, name, age, sex, breed, weight, location, foster_bio_memo) VALUES
     (1001,'Biscuit',2,'Male','{Golden Retriever}',55,'8 Address Street, Boston MA','Friendly and loves walks.'),
     (1002,'Mochi',1,'Female','{Domestic Shorthair}',9,'22 Elm Street, Quincy MA','Playful tabby.');
  3. Start the apps: npx nx serve backend and npx nx serve frontend
  4. Open http://localhost:4200/animal-availability and check:
    • Cards render. Stopping the backend shows the error state, and an empty animals table shows the empty state.
    • The filter button opens the panel. Ticked options appear as chips, Apply updates the grid, and Cancel and Clear Filters reset it.
    • "See More" opens the modal. Entering a comment and pressing Apply shows "Request submitted!" and adds a row to matches.
  5. Run tests: npx jest apps/backend/src/animals apps/backend/src/matches
Screenshot 2026-11-03 at 6 05 22 PM Screenshot 2026-11-03 at 6 05 38 PM Screenshot 2026-11-03 at 6 06 00 PM Screenshot 2026-11-03 at 6 06 22 PM

@dburkhart07 dburkhart07 left a comment

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.

some initial comments. can you please merge main in so this diff gets cleaned up a bit?

Comment thread apps/frontend/src/assets/mspca-logo.png Resolved
Comment thread apps/frontend/src/theme.ts Outdated
brown: '#5e3638',
},
purple: {
avatar: '#b57bf8',

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.

can you check the figma again? i think this is supposed to be a different hexcode: https://www.figma.com/design/NNzZhw8n4UWmG3UeJTLEwD/MSPCA?node-id=633-8125&m=dev

@@ -0,0 +1,16 @@
// TEMPORARY: the animals API doesn't return restriction tags yet, so these are

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.

we should have restrictions returned, so we shouldnt need any mock data anymore. lets just add the data into the db ourselves and taht should have all the schema we need

Comment thread apps/frontend/src/assets/x.svg Outdated

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.

Fairly certain we are going to be using lucide-react icons. can we use those corresponding ones from the figma instead of this and the list filter?

Comment thread apps/frontend/src/api/apiClient.ts Outdated
const defaultBaseUrl =
import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:3000';

export interface Animal {

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.

lets create a separate types.ts file within frontend/src and put all the interfaces in there.

Comment thread apps/frontend/src/api/apiClient.ts Outdated

export interface Animal {
animalId: number;
chameleonAnimalId: number;

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.

this is a string now

Comment thread apps/frontend/src/api/apiClient.ts Outdated
location: string;
picture: string | null;
fosterBioMemo: string | null;
// Restriction tag labels (e.g. 'Dog-Free Home'). Not returned by the API yet;

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.

this will also need updating since we do return it

Comment thread apps/frontend/src/api/apiClient.ts Outdated
const defaultBaseUrl =
import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:3000';

export interface Animal {

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.

lets also just make sure all of these schemas are exactly right with what the backend entities are

@@ -1,108 +1,24 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class CreateAnimals1792897315518 implements MigrationInterface {

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.

we should already have this migration. can you make sure this goes away when we update it? the entity is different now too

@IsPositive()
chameleonAnimalId!: number;

@IsOptional()

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.

this should have the new docstring of @IsNonEmptyString, as well as specific ValidateIf. we should be able to still accept null values for this though

@dburkhart07 dburkhart07 left a comment

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.

for right now, can we comment out the stuff in main.tsx so that we only use the nonauthenticated frontend? we will adjust this later when we implement sign-in.

sorry theres so many comments 😭 lmk if you have any questions on any of them. tysm pujita, really good work on this so far, ik it was lowk a huge pr!

// Remaining months of age, e.g. 5 for "4Y5M"
ageMonth: number;
sex: string;
// 'Dog' | 'Cat' | 'Small Animals' | 'Baby Bottle Animals' | 'Farm Animals'

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.

nit: we can delete these comments tbh

matchId: number;
volunteerId: number;
chameleonAnimalId: string;
// 'Pending' | 'Active' | 'Complete' | 'Denied' | 'Withdrawn' | 'Canceled'

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.

same here


it('does not update preferences when an added animal does not exist', async () => {
const error = new NotFoundException(
'Animals with Chameleon IDs not found: 998, 999',

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.

nit: this error should be A998 and A999

'At least one of added or removed must be provided',
);
}
await this.animalsService.checkAllExistOrFail(dto.added ?? []);

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.

this is already being checked in the service, can we just keep it there?

description: 'No volunteer or animal exists with the given ID.',
})
async createMatch(@Body() body: CreateMatchDto): Promise<Match> {
await this.animalsService.findByChameleonIdOrFail(body.chameleonAnimalId);

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.

we should move this check into the service as well, and therefore we shouldnt have a diff for this file or the controller tests

locations: string[];
}

const ChevronIcon: React.FC<{ open: boolean }> = ({ open }) => (

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.

why do we need this? can we just use a lucide icon instead?

align="stretch"
gap="4"
>
{options.length === 0 ? (

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.

options will always have something available.

{ label: 'Requires Medication', color: 'trello.requiresMedication' },
];

export const getRestriction = (label: string): Restriction | undefined =>

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.

this is a Restriction enum

label,
color:
key === 'restrictions'
? getRestriction(label)?.color ?? 'neutrals.black'

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.

when would this ever fail?

Clear Filters
</Button>
</Flex>
{chips.map((chip) => (

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.

these are supposed to have a buttons to view them horizontally, rather than overflowing into the next line. id check the figma design again on this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants