
Full-stack animal shelter coordination platform
Animal shelters face critical coordination challenges: managing foster placements across families, tracking inventory across multiple locations, maintaining medical records, and balancing staff access with foster family permissions.
The system needed to:

We built ShelterSync using a modular, feature-based architecture that separates concerns across the frontend and backend. The design prioritizes scalability, auditability, and role-based security from the ground up.
Frontend: React with TanStack Router for navigation, Zustand for state management, and Tailwind CSS for styling. Each feature has its own set of pages and components.
Backend: Node.js/Express with PostgreSQL via Prisma ORM. We use Zod for schema validation and implement role-based permissions at the API level.
Each batch (identified by expiration date) is a separate row with dedicated transactions for all movements, ensuring compliance and audit trails.
No direct quantity edits. All changes flow through transaction endpoints, creating an immutable record of every action.
Single PATCH endpoints with an action parameter reduce API surface area while improving consistency and reducing error states.
Zod schemas ensure type safety across frontend and backend, catching issues at request time before they reach business logic.
Traditional inventory systems store quantity as a single number. ShelterSync creates a separate inventory row for each batch (identified by expiration date). This design achieves two critical goals:
First, it provides granular control over expiration dates - essential for food and medicine. Second, it creates a natural audit trail: each batch row's history tells the complete story of that inventory's lifecycle.
When a staff member checks out supplies, they select from specific batches and quantities are decremented on that batch row. For crate loans, an entire batch (the crate) moves between states: pending → active → returned.
Every change to inventory is recorded as a transaction. There are two transaction types:
This ensures compliance auditors can trace any discrepancy to the exact transaction. No data is overwritten; every state change creates a new record. This immutable pattern is foundational for correctness and accountability.
The system distinguishes between two user types: Staff (full control) and Foster Families (limited, read-mostly access). Rather than limiting at the page level, we enforce permissions at the API level.
Each endpoint's response filters fields based on user role. A foster family can view their assigned animals but not medical notes reserved for staff. A staff member can see everything. This centralized approach ensures security cannot be bypassed by client-side changes.
The backend uses consistent plural naming (e.g., `/api/animals`, `/api/inventory-transactions`) and consolidates related actions into single endpoints with an `action` parameter instead of proliferating separate endpoints. This reduces the API surface area and makes the contract more discoverable.
For example, instead of `/api/animals/assign` and `/api/animals/unassign`, we use `PATCH /api/foster-assignments` with `action: 'assign' | 'unassign'`. This pattern appears throughout the API and improves consistency.
Planning: Jira for ticket tracking and sprint planning
Communication: Discord for real-time screen shares and decisions
Git Workflow: Branch naming `ANIMALS-[number]-[description]`, always rebase against main before PR
Code Review: Peer reviews before merge, iterative feedback culture
Challenge: Managing 6 simultaneous developers, merge conflicts, keeping UI in sync with API changes
Jira Project Board

Successfully coordinated a relay-style demo video with all six team members, demonstrating the full system lifecycle from intake to foster assignment to medical tracking.

Database Schema
