Buy / sell/ tradeCRM
A used-bike resale business was running on spreadsheets, a shared inbox and somebody's memory. I replaced all of it with one platform that carries a bike from the seller's first photo to the day it sells.
- Role
- Sole developer
- Surface
- Internal web app + public intake
- Stack
- Next.js · Supabase · Shopify
- Status
- In daily production use






Interface rebuilt with sample data — no real seller or pricing information shown.
One bike, seven stages, one system.
Every stage used to live somewhere else — a form, a spreadsheet, an inbox, a notebook on the workbench. Each handoff was a chance to lose a bike, misprice it, or forget a seller was waiting on an answer.
A public intake form takes photos and specs with no account required. The bike is valued against a model library and cached MSRP, then offered as cash, store credit or both, which the seller accepts by link. Once it arrives, tune-up steps and condition grading get logged per item, so listing copy composes from what the mechanic actually recorded. The deal closes out in the P&L against what it cost to buy and fix.
The parts that were hard.
A catalog nobody agrees on
Bikes don't have clean SKUs. The same frame ships under different names by year, region and build kit. The model and variant tables exist so a mechanic can find the thing in front of them without knowing what marketing called it.
Money that splits two ways
Sellers get paid in cash, store credit, or both, and the split changes what the deal is worth to the business. Every stage total on the board carries both numbers, because a credit dollar and a cash dollar are not the same dollar.
Listings that stay in sync
The storefront needs structured metafields, not prose. Condition copy, tune-up notes and spec fields compose into a listing payload so the same bike reads consistently everywhere it appears.