Whiskerville

Whiskerville was designed around a simple observation: The hardest part of rescue work is often coordination, not compassion.

Whiskerville's dashboard showing a summary of the organization's capacity and pending action items.

Overview

Many foster-based rescues run on a patchwork of Slack threads, spreadsheets, shared drives, text chains, and institutional memory — patterns I became deeply familiar with while volunteering in animal rescue. Foster placement, transport, clinic days, sitter coverage, supply requests, adoption applications, and medical tracking each live in a different tool, and nobody has a single view of what is happening.

Whiskerville is my answer: a multi-tenant operations platform for rescues and foster networks. It started as a front-end exploration of calm, workflow-driven UX and has grown into a full product — a React and TypeScript front end on Postgres with row-level security, roles and permissions, notifications, reporting, mapping, and grounded AI writing for adoption listings.

I designed and built all of it: product scope, data model, interaction design, visual system, front end, and backend schema. The through-line never changed:

“Make operational work feel calm, flexible, and humane, even as the organization gets more complex.”


What I Focused On

Real rescue operations are dynamic: animals move between foster homes, clinic schedules shift, transport changes daily, volunteers cover for each other, and a supply run serves a whole foster household. The system had to support that without turning volunteers into ticket-filers.

The Animal Profile: photo, traits, status, current foster, litter, and next medical at a glance; below, adoption history, the tabbed record, an Adoption Readiness checklist, and external listings.

Product & UX Decisions

Foster placements as first-class operational objects

Animals move between foster homes for medical needs, behavior, availability, emergency overflow, and temporary coverage. Instead of overwriting a single “current foster” field, each placement is its own record with a start, an end, a reason, and a temporary flag. Reassignment closes one and opens the next, so foster history reads as a coherent timeline rather than a series of anonymous edits.

Guardrails grew out of that model: placing an animal moves it into care automatically, the form warns when a foster is at capacity, and a foster parent can request a reassignment themselves rather than waiting on a coordinator.

Normalize where it helps the humans

A recurring challenge was resisting structure that looked clean technically but created friction operationally. Supply requests serve a foster household, not one animal. A sitter request covers everything in someone's care. A transport may carry animals, supplies, or both. A bonded pair moves through a single adoption application. A litter is entered once, with only the per-kitten differences filled in. Each of those cost something in relational purity and paid it back in speed for the person filling out the form.

Timeline-driven operational visibility

The timeline is the backbone of the animal record. Intake, placements, medical records, clinic visits, notes, adoption progress, and transport are organized chronologically and filterable, so anyone opening the page can answer the two questions that matter:

What happened to this animal, and what happens next?

“What happens next” is its own object: an animal has at most one open action item, and any priority above normal requires one. The banner is a forcing function, not a reminder.

Grounded AI, not generative filler

Rescues write the same things over and over: a summary for a foster handoff, a Petfinder blurb, a longer profile for their own site. Whiskerville generates these with one hard constraint: the model may only use what the rescue has actually recorded. It is fed the animal's traits, medical history, notes, and placement context, and told to omit anything not stated rather than invent a friendly detail.

Two details make that trustworthy in practice. A Summary Inputs card shows which sources the summary drew on, so a thin summary reads as a data-quality prompt rather than a model failure. And every generated piece is fingerprinted against its inputs, so the UI flags it as stale when the record changes.

For adoption listings, the organization owns a template of fixed disclaimers, fees, and merge fields; AI fills only the animal-specific sections. Tone and length are org settings — a short Petfinder blurb or a detailed website profile — so the output sounds like the rescue, not like a model.

The Summary tab: a short, factual summary generated only from the animal's recorded traits, notes, and medical history, with Edit and Regenerate controls and a footer noting when and how it was generated.
The Adoption Profile tab: the organization's fixed intro, medical statement, fees, and closing instructions come from its template, while the hook, personality narrative, and home requirements are generated per animal.

From record to adoption

Adoption is a pipeline, not a checkbox. Applications move from inquiry through meet-and-greet, paperwork, and placement. Unsuccessful closes are recorded distinctly (declined, withdrawn, duplicate) so reports do not compress every lost application into an org-sounding “cancelled,” and returns bring the animal back into care with a required reason. Because rescues post the same animal in several places, each animal also tracks its external listings per platform — Petfinder, Adopt-a-Pet, the org's site, social — with a draft, published, or removed status. That is groundwork for automated syncing without pretending it exists yet.

The Adoptions page, listing applications with their stage, fee, and dates. A bonded pair moves through one application, and lost applications are recorded as Rejected or Cancelled by Applicant rather than a generic Cancelled.

Rescue sites on a map

Trap-neuter-return work is geographic: a colony behind a restaurant, a feeder who checks it daily, a trapper who covers that neighborhood. Whiskerville models these as Rescue Sites — a named location with an address-autocompleted map pin, a site lead, a point of contact, volunteers with their roles, and the animals that came from it. Closing a site keeps its history. The same address and mapping layer powers saved locations for clinics and transport pickups, so “where” is entered once and reused everywhere.

A Rescue Site: reporter and address, distance from the viewer, a map pin, a summary of animals taken from the site by outcome, site volunteers with their roles, and running notes.

Coordination without tickets

Transport, sitter coverage, and supplies share one request-and-claim pattern: anyone can raise a request, a volunteer claims it or a coordinator assigns it, and everyone involved is told. Transports carry animals and supplies with separate pickup and drop-off locations; a sitter request temporarily hands off placement responsibility; a supply request can be submitted on behalf of a foster and warns about duplicates.

Notifications are what let this replace Slack rather than duplicate it. Events are created by database triggers, never the client, and fan out per recipient. An hourly job sends proactive reminders in the organization's own timezone: a clinic appointment tomorrow, a transport still needing a volunteer, a foster placement ending in three days.

The notifications bell: a volunteer claimed the viewer's transport request, and their supply request moved through in progress and fulfilled.

Clinic days as an operational system

A clinic event is a coordination hub: a date, a location, a vet, a coordinator, limited slots, and the transport needed to fill them. Each slot is one animal, and each animal usually gets several procedures in a visit, so procedures are tracked individually. A three-step completion flow (attendance, records, review) records who showed up and what was done, captures what was learned on the table (a previously unknown sex, for example), and writes medical records back to each animal, guarding against duplicates of records that already exist.

The final step of the Complete Clinic flow (Attendance, Records, Review): who attended, which procedures were done for each animal, and the medical records that will be created when the clinic is locked as completed.

One system, many kinds of users

Owners and admins configure the organization; members get granular permissions (animals, medical, transport, adoptions, sites, listings); foster parents see and edit only what concerns the animals in their care, and contact details can be hidden from members who do not need them. Access is invite-based with Google, email, and passkey sign-in, and support staff can be granted temporary, audited access when something needs a second pair of eyes.

Configuration instead of assumptions

Every rescue is opinionated in a different way. Rather than encode one org's rules, each organization enables its own species and breeds, defines its own traits (split into characteristics of the animal versus requirements of the home), sets a name-reuse policy from “duplicates are fine” to “wait two years,” edits its adoption template, and hides entire workflows it does not use — a shelter without a foster program never sees foster or sitting features at all.


Visual & Interaction Design

The visual system is meant to feel calm, trustworthy, and operationally clear. Many operational tools overwhelm because every workflow competes equally for attention. Whiskerville prioritizes strong hierarchy, restrained color, soft surfaces, generous spacing, and lightweight status signaling: lifecycle status, priority, and care flags are three separate axes and are styled as three separate things, and an opportunity flag like “recommended for transfer” is deliberately green, not amber.

Principles that held up across dozens of screens:

The full application shell: a quiet sidebar, one primary action per page, soft cards, and status colors reserved for meaning rather than decoration.

Engineering Notes

Whiskerville is React, TypeScript, Vite, and Tailwind on Supabase (Postgres, row-level security, storage, auth), with edge functions for AI generation and email. A few decisions I would defend in a design review:

Reports for a chosen range: adoptions, applications, conversion rate, and days from application to adoption, plus animals by status and intake versus adoption over the last twelve months.

Trade-offs

The public demo linked above runs entirely in the browser against seeded data, so anyone can explore it without an account; the production build is the same interface on the live backend. A few things are deliberately not built:

Why this approach
Building fewer workflows with strong operational realism, then hardening them into a real multi-tenant product, was a more meaningful exercise than a generic animal database with every feature half-built.


Result

Whiskerville grew from a UX exploration into a working platform, and the original thesis held: operational complexity becomes understandable instead of overwhelming when the workflows mirror how the work actually happens.

The project reflects the kind of product engineering I like best, where

…all reinforce each other instead of existing separately.

Rather than treating rescue work as a collection of forms and records, Whiskerville approaches it as a living operational system centered around coordination, trust, and care.

— Dan Thoreson