← Back to projects

Kapwa Help

Kapwa Help
Offline-first
PWA
3
Languages (EN · FIL · ILO)
$0
Infra cost
Open source
MIT
React + TypeScriptVitePWA / WorkboxSupabase / PostgresLeaflet / OpenStreetMap

The Problem

When Typhoon Emong hit La Union in 2025, volunteers self-organized across municipalities to move meals, water, and medical supplies to the people who needed them. Coordination lived in group chats. Tracking lived in spreadsheets, when it happened at all. There was no shared, real-time picture of what was needed, what had already been sent, or where the gaps were, so aid clustered in some barangays while others waited.

The Solution

Kapwa Help turns that scramble into one coordinated system: a real-time relief map of field-reported needs, a transparency dashboard of donations and distribution, and a multilingual report form anyone on the ground can use. At its core it is a matching engine. Reported needs (demand) are verified, matched against the goods sitting in deployment hubs (supply), then dispatched and confirmed, with every step visible to donors and coordinators.

How It Works

The app is a client-side PWA designed for disaster relief areas with unreliable connectivity.

Offline submissiondevice → postgres
On the device · no signal
Fill report
GPS, photo, urgency
Held in outbox
IndexedDB · client UUID
↻ connection returns
Postgres · on reconnect
Atomic RPC insert
parent + junction, one txn
Dedup on conflict
duplicate replay dropped
A dashed gap stands in for the network. Everything on the device works with no signal; the queued report crosses only when the connection returns, straight into Postgres with no server between.
  • Offline-first by default. Reports are composed and queued in an IndexedDB outbox with a client-minted UUID, so a volunteer can submit with no signal. When the device reconnects, the queue replays into atomic database calls. A duplicate replay hits a unique constraint and is dropped silently, so a double submission offline never creates double records.
  • Built for bad networks. A service worker precaches the app shell, serves data network-first and map tiles cache-first, so the map still opens when the connection does not.
  • Auth at the database, no server. With no backend to gate writes, Row Level Security becomes the auth boundary. The browser uses an anonymous key, sensitive writes are gated by an is_admin() Postgres check, and the public reads everyone sees come through PII-stripped views rather than the base tables.
  • Atomic multi-table writes. A need and its aid categories (a parent row plus its junction rows) are written inside a single transaction, so a partial write is impossible. That matters most when the client is replaying a queued submission.

From Report to Relief

A need is not just a row. It moves through a lifecycle, and a different person acts at each stage.

Needs lifecycleneed_status enum
Pending
Field volunteer
logged with GPS, photo, urgency
Verified
Coordinator
confirmed real; verifier recorded
In transit
Logistics
matched to a hub, dispatched
Confirmed
On the ground
closed with a delivery photo
A verified need is matched against hub inventory before it can move to in transit; one atomic RPC writes the deployment and advances the need to its next status in a single transaction.

The status lives in a Postgres need_status enum, and transitions are enforced in the database layer rather than the client. Marking a need as in transit and creating its deployment is one atomic operation, so a hub commitment and the need's status can never drift apart.

Built to Run Anywhere

  • Trilingual. English, Filipino, and Ilocano. New strings are bootstrapped by a machine-translation script that preserves interpolation variables, then sent for human review.
  • Zero-budget by design. Supabase, Vercel, and OpenStreetMap free tiers, with no paid services, so a volunteer-run project can survive without funding.
  • Measured where it matters. Performance is checked against throttled 2G/3G and a low-end Android profile, not desktop scores, ensuring consistent performance across areas.

It is a working demo today, not yet launched. But it is built so that when the next storm comes, the response can start from a shared, transparent system instead of a group chat and a spreadsheet.

View demo
View on GitHub