Production Web App

knockabase

A door-to-door canvassing app for an HVAC electrification crew, where a rep speaks a knock between houses and it becomes a validated database record instead of a note typed at 9pm.

Next.js
TypeScript
Supabase
PostGIS
Mapbox
Claude API
Live Code: Private repo
Territory map with coloured door pins along a Vancouver block, a drawn territory boundary, and a microphone dock

Employer signal

What This Project Shows

This is the project where I owned a whole system rather than a feature: schema and row-level security, a real-time map, an LLM extraction pipeline with server-side validation, offline sync, and the design decisions that make it usable one-handed on a doorstep. It is in the hands of a real crew, which is what makes the constraints real.

Problem

What Needed To Be Solved

Canvassing software assumes a person who can type. A rep works outdoors, one-handed, holding a storm door open, for a five-hour shift — so notes get reconstructed from memory in the truck, and the highest-value sentence anyone hears all day ("come back when my wife's home") survives about ninety minutes. Meanwhile residential streets and basements have no signal, so an app that needs a connection to accept a knock is an app that loses knocks.

Approach

How I Built The Solution

I made speech the primary input and treated the model's output as untrusted. The phone's own recogniser runs first because it is free and instant, with a cloud transcription fallback primed with HVAC vocabulary for wind and traffic. The transcript is mapped onto a fixed schema and validated on the server before it can reach the database, and the rep confirms the filled-in card before anything saves. Everything below that assumes intermittent connectivity rather than treating it as an error state.

Outcome

What It Demonstrates

It runs a real crew's day: territories on a map, knocks captured by voice, numbers a manager can act on, and a tool that degrades to manual entry rather than failing when a key expires or the signal drops. The judgement it demonstrates is knowing which parts had to be right the first time — the tenancy boundary, the ordering guarantees, the schema — and which could be learned by shipping and being corrected.

Screens

Territory map with coloured door pins along a Vancouver block inside a drawn boundary
Doors cluster by zoom and carry both a colour and a glyph. Colour is the only chromatic thing in the entire app, so a pin is always the most visible object on screen — and the map still reads under colour blindness and in direct sun.
Knock sheet showing known household data, tags, and a row of outcome buttons
What the rep sees at the door: what is already known about the household, and a one-tap outcome row. Dispositions are abbreviated to the shorthand this industry already uses. Voice is the upgrade, not the requirement.
Design reference page showing four field themes and the eight-value disposition scale
Four themes chosen by the light the screen is read in — direct sun through to a true-black night shift — and the disposition scale ordered so neighbouring outcomes differ in brightness as well as hue.

Evidence From Source

Security probed, not assumed

Two bugs in one session had comments asserting properties no code implemented — an unfiltered UPDATE that PostgREST rejects outright, and a column documented as guarded by a function nobody had written. Both passed type-check, lint and build. A doc comment asserting a security property no code implements is worse than no comment, because the next reader stops looking.

Design decided by evidence

The busy indicator on a button took five designs: nothing, a 2px rule invisible at arm's length, a spinner that overlapped labels on narrow controls, a glow too stark to tune across four themes, and finally a 3px bar travelling the bottom edge. The typeface is Atkinson Hyperlegible for the same reason — reps read unit numbers in the rain, and 1B versus lB is a knock at the wrong door.