Compassion Rescue
A bilingual public site, adoption pipeline, and shelter operations platform for an animal rescue organization.
COMPASSION RESCUE
Where Every Life Is Curated With Compassion
- Languages, EN / ZH
- 2
- Programs Unified
- 4
- Epics Shipped
- 7/7

- Client
- Compassion Rescue
- Industry
- Animal Rescue · Nonprofit
- Engagement
- Full-stack platform build
- Status
- In Progress — staging delivered
- Duration
- May – June 2026 · ~6 weeks to staging
- Scope
- Bilingual public site, admin platform, volunteer portal
01 Overview
Who is the client?
Compassion Rescue is an animal shelter serving a bilingual community — the platform runs fully in English and Simplified Chinese, and the shelter's own default settings for language, timezone, and currency point to a Chinese-speaking operation. Their work spans four programs: adopting animals out to permanent homes, placing animals with foster carers, running a volunteer program with scheduled shifts, and publishing news and success stories to keep the community engaged.
02 Problem Statement
What problem were they facing?
The shelter was running all of this without a system — no public catalogue, no application pipeline, no volunteer portal, and no way to touch content without a developer, in two languages.
No public catalogue
Someone wanting to adopt had no way to browse who was available, filter by type or age or size, or read an animal's story and medical background before reaching out.
No application pipeline
Adoption, foster, and volunteer applications had no intake process and no pipeline behind them. Tracking pending vs. approved was manual — and remembering to update the animal's availability once an adoption went through was exactly the kind of thing that slips.
No volunteer portal
Shifts, announcements, and logged hours lived wherever the coordinator kept them, with no self-serve system for volunteers to check either.
Developer required for every change, in two languages
News posts, the animals themselves, site announcements — everything content-related needed technical help to change, doubled by the bilingual requirement.
03 Solution
How did we solve it?
We built a full platform around the four programs, with the bilingual requirement treated as a first-class constraint rather than an afterthought. The public site covers the whole visitor journey — homepage, a filterable residents directory with URL-synced filters, animal detail pages, and news with individual articles. Every piece of text, including animal descriptions, exists in both English and Chinese, with a language toggle that remembers the visitor's choice and its own CJK typography rules — no italics, no letter-spacing.
Applying is a proper multi-step flow, separate for adoption, foster, and volunteer, with drafts saved as the applicant types. The admin side is where the operational value sits: a dashboard, full animal management, a bilingual content editor, foster and volunteer management with shift scheduling, medical records per animal, site settings, and an adoption pipeline where applications move pending → reviewing → approved/rejected with a confirmation dialog and a five-second undo on every action. The piece that ties it together is a backend hook — when an adoption is approved, the animal's status flips to "adopted" automatically, and a foster approval flips it to "pending".
- FE Next.js 16 Bilingual frontend React 19Cookie auth
- PB PocketBase Go + embedded SQLite Custom seed cmdApproval hook
- S3 MinIO Object storage Auto-provisionedS3-compatible
- Next.js 16
- React 19
- TypeScript
- PocketBase
- Go
- SQLite
- MinIO (S3)
- Tailwind CSS v4
- Docker
- Dokploy
04 Project Timeline
Key milestones and duration
Phase 1 May 4–15, 2026
Foundation & Public Site
Project structure, Docker environments, the design system, and the public pages built against mock data with "coming soon" markers on anything not yet wired.
Phase 2 May 11–18, 2026
Backend & Schema
PocketBase collections for animals, applications, posts, volunteers, shifts, fosters, medical records, and settings, defined in code with per-collection access rules. Seed data in both languages.
Phase 3 May 19–25, 2026
Staging Infrastructure
The Dokploy deployment, MinIO storage wiring, and a written operator runbook. Two permanent gotchas documented here: build-time env vars, and Dokploy's single-compose-file requirement.
Phase 4 June 8–12, 2026
Live Backend Integration
A written plan — including an upfront adversarial review that caught an SDK version mismatch, missing update rules, and a settings anti-pattern — followed by wiring every screen to the live backend and removing every mock banner.
Phase 5 June 12–16, 2026
Systematic QA
An accessibility + interaction audit and a five-state resilience check (loading, error, empty, sparse, full) applied to every data-fetching point. Every item checked off, smoke tests pass, build is clean.
Production Rollout From June 17, 2026 — In Progress
Domain & Storage Migration
Staging verified and signed off. The rollout plan covers domain and DNS, moving object storage from MinIO to a cloud provider, backup retention, and a final security review.
05 Challenges & Resolutions
What obstacles did we face, and how did we overcome them?
Server renders English, browser remembers Chinese
problemThe saved language lives in the browser, but the server renders first — so a returning Chinese-language visitor could see the page flash English before switching. Distracting on every single navigation.
resolutionThe language provider now waits until the client has confirmed the saved language before rendering, behind a neutral loading state — no flash, no flicker, on any page.
Approved adoption, stale listing
problemApproving an adoption used to require remembering a second step: going back and marking the animal unavailable. That's exactly the kind of manual step that gets forgotten under real workload.
resolutionA backend hook closed the gap — approval and the animal's status change are now one action, running server-side, so it works no matter which screen or client triggered the approval.
A production build that broke in a way dev never would
problemNEXT_PUBLIC_ environment variables get compiled into the JavaScript bundle at build time — changing them on the server and restarting does nothing. Cost real debugging time on staging: login worked, every admin page said "failed to fetch".
resolutionGot a permanent note in the repo, and the runbook now includes a one-line command to verify the URL actually made it into the built bundle before anyone chases a phantom bug again.
Image uploads succeeded, then 404'd
problemPocketBase won't use S3 storage until it's explicitly told to. On a fresh deploy, uploads would appear to succeed and then 404 — silent until someone actually clicked the image.
resolutionA scripted one-time step (npm run configure-s3) went into the first-deploy bootstrap checklist and is documented as the canonical first-deploy gotcha.
Backend written against an API newer than the pinned version
problemThe backend was initially written against a newer PocketBase API than the version actually pinned in the module file — it didn't compile.
resolutionThe adversarial review built into the integration plan caught it before it could block anything downstream; the backend was refactored to the correct SDK surface before wiring began.
06 Before & After
Comparison of the situation before and after the project
| Before | After |
|---|---|
| No public catalogue of animals in care | Filterable bilingual directory with detail pages and shareable, URL-synced filtered views |
| No formal intake for adoption, foster, or volunteer interest | Multi-step adoption/foster/volunteer flows with draft saving as the applicant types |
| Application tracking done manually | Pipeline with statuses, search, confirmation dialogs, rejection reasons, and a five-second undo |
| Adoption → listing update was a separate manual step, easy to miss | Automatic via a backend hook — approval and availability change in one action |
| No volunteer system — shifts and hours lived wherever the coordinator kept them | Authenticated portal with shifts, announcements, logged hours; admin-side scheduling |
| Medical history not digitized | Per-animal medical records in the admin panel |
| Developer needed for every content change, single language | Bilingual editor, news publishing, announcement banner — all self-serve |
| No deployment infrastructure | Dockerized stack on Dokploy with S3 storage, health checks, backups, and an operator runbook |

07 Client Benefits & Outcomes
What value did the client gain?
- Epics Shipped
- 7/7
- all tasks complete, mock banners removed
- Languages
- 2
- English · Simplified Chinese, hydration-safe
- Programs Unified
- 4
- adopt · foster · volunteer · news
- Resilience States Audited
- 5
- loading · error · empty · sparse · full
One system instead of scattered manual processes
Staff manage animals, applications, fosters, volunteers, medical records, and content themselves, in both languages, without technical help.
The two most damaging failure modes of manual tracking are gone
Every application has a status someone owns, and an approved adoption updates the public site by itself — lost applications and stale listings can't happen anymore.
A genuinely accessible front door
44px touch targets, focus traps in modals, skip-to-content, zero layout shift, ARIA roles — the audit standards weren't aspirational, they were verified item by item.
A documented, reproducible system sized to a nonprofit's budget
One backend binary, one modest server, open-source object storage locally with a clear upgrade path to cloud storage for production. The runbook means a fresh deploy doesn't depend on whoever set it up being available.
08 Key Learnings & Takeaways
What did we learn from this project?
Writing failure modes down before building catches real bugs
The adversarial review in the integration plan found a broken SDK version, missing access rules, and a schema anti-pattern before any of them cost debugging time downstream. A cheap habit with a high hit rate.
Checklists beat memory for QA at this scale
"Does every fetch point handle loading, error, empty, sparse, and full?" isn't a question anyone answers reliably from memory across forty-plus screens. Two audit documents with per-item checkboxes made the verification real instead of assumed.
Deployment gotchas belong in the repo, not in someone's head
The build-time env var issue and the S3 configuration step each burned time once; both are now permanent notes with verification commands, and neither can burn time twice.
Internationalization is a design constraint, not a translation task
Duplicated content fields, CJK typography rules, hydration-safe language detection — all of it had to be decided early, because retrofitting any of it would have touched every screen.
Automate the step humans forget
The adoption-approval hook is ten lines of backend code that eliminates the single most likely operational error in the whole workflow.
Live on staging · Real seeded data
The Modules
Every screen maps to a real workflow the shelter operates daily, captured live off the running staging build. Click any to expand.
















