Case study · 2026

Ed’s Cabinets

Ed quoted his jobs from pencil sketches. Now customers set the measurements, watch the piece drawn to size, and pay the deposit when he sends the price — in English or Spanish.

Role
Designer & sole developer
Timeline
Sep 2026 – present
Status
Live, payments on Ed's own Square
Type
Bilingual site + quote system
900
strings, written twice

Every word on the site and in Ed's emails, in English and in Mexican Spanish.

13
pieces the builder draws

Store fixtures and home pieces, each drawn to the customer's own measurements.

234
places, by distance

How far each town is from the workshop decides its minimum order.

14.7k
lines of TypeScript

Site, quote builder, Ed's work board and payments, in 49 commits.

The problem

The quote was a sketch on paper.

Ed’s Cabinets is my uncle’s shop. Ed builds store fixtures — produce displays, bulk bins, gondolas, checkout counters — for markets, carnicerías and dulcerías across the Central Valley, and kitchens and closets for homes. Every job started with a sketch on paper, the measurements written on it.

A sketch like the ones Ed quoted from: the run drawn from above, the measurements in inches.

That works when a customer already knows him. Anyone else had nothing to go on: no website, no Google listing, no way to see his work or picture a piece before a phone call.

So the site starts where his sketch did.

edscabinets.com/en/quote
Ed's Cabinets quote form: a gondola drawn in 3D at 6 feet tall, 4 feet long and 38 inches deep, with Corner, Front, Side and Top views, beside photo cards of the pieces Ed builds

On the site, the customer gives the measurements and the drawing follows — turnable before anyone picks up a phone.

What shipped

From a sketch to a deposit, in either language.

Five steps, from the first question to a paid deposit.

  1. Pick what you need

    The home page asks one question — what do you need built? — and answers with Ed's own photos of that piece. No stock photography anywhere.

  2. Draw it to size

    Length, height and depth move the drawing as they change, flat or in 3D. Material, finish, extras, photos of the space, and several different pieces in one request.

  3. The shop gets it in Spanish

    The request reaches the shop by email, in Spanish, with the drawing attached. The customer gets a copy and a page of their own to come back to.

  4. Ed prices each piece

    From his phone, on a Spanish-first work board. One tap creates the Square customer, order and invoice: the deposit due now, the balance when the job is ready.

  5. Paid by link or by QR

    Emails carry a pay link that always opens the current price. In the store, the customer scans a code on Ed's phone — and Ed can take the whole order there, in the same builder.

Ed’s side

Every job on one board, in Spanish.

Every request becomes a job on a board built for Ed’s phone.

  1. Nuevas

    new · needs a price

    Then: Ed taps “Mandar precio y anticipo”

    Square · customer → order → invoice

  2. Esperando anticipo

    waiting for the deposit

    Then: Square says the deposit is in — or Ed marks it paid by hand

    signed webhook · invoice.payment_made

  3. Para trabajar

    to build

    Then: Ed taps “Cobrar el resto”

    balance requested · email with the pay link

  4. Por cobrar

    to collect

    Then: The balance comes in, by link or in person

    signed webhook · or marked by hand

  5. Terminadas

    finished · paid in full

From Nuevas or Esperando anticipo, “No tomo este trabajo” moves a job to No tomadas.

any open Square invoice is cancelled first

When the money comes through Square, Ed doesn’t move a thing: Square’s signed notice moves the job.

Captured from a local copy running on test jobs and placeholder prices — the real board holds customers’ details.

Ed's board in Spanish: stage tiles with counts — Nuevas, Esperando anticipo, Para trabajar, Por cobrar — above a list of test jobs, each with a call button
sample data
SolicitudesEvery job by where it stands, with a call button on each.
One test job in Ed's admin: the suggested price marked provisional, a price for a bulk-bin display, the total and the 50% deposit, and the Mandar precio y anticipo button
sample data
Un pedidoA price for each piece, the deposit worked out, and the button that sends it.
Ed's price list in Spanish: the distance and minimum order section, zone 1 up to 30 miles and zone 2 up to 90 with a $1,000 minimum, each marked provisional
sample data
PreciosThe price list, with the distance zones and the towns that fall in each.
How it’s built

The drawing, the price and the payment can’t disagree.

A quote system fails quietly: a drawing that doesn’t match the words, a price nobody actually set, a pay link that stopped working. I designed each of those out.

One model, three outputs

buildPiece(spec) turns a piece into parts in inches. The server-rendered drawing, the three.js view (loaded only when someone asks for it) and the written request all read those same parts, so what a customer sees, turns and sends can’t disagree. Parts may touch; they never cross.

No price anyone invented

Ed’s board suggests prices from a rate card that stays provisional until the shop saves it as its own. Customers are shown a range, never a single figure — and only once every number behind it is Ed’s.

A pay link that can’t go dead

Sending a new price replaces the Square invoice, which would kill the link in the first email. So emails carry the site’s own signed /pay/ link, which opens the invoice as it stands. Payments arrive by an HMAC-verified webhook, with a catch-up check whenever Ed opens his board.

Distance is part of the price

234 California places carry their distance from the workshop in Ballico — the straight line, stretched for the road. Four distance zones on Ed’s price list decide each town’s minimum order, and delivery is free.

Spanish from day one, not translated after

900 strings in each language, in the formal usted store owners expect. Ed’s board and his emails default to Spanish; each customer hears back in the language they chose.

Stack

  • Next.js 16
  • React 19
  • TypeScript
  • Tailwind v4
  • next-intl
  • three.js
  • Supabase
  • Square Invoices API
  • Resend
  • zod
  • Vercel
Outcome

Ed’s shop is online, in both languages.

edscabinets.com is live, and payments run through Ed’s own Square account. A quote that used to start with a phone call and a pencil now arrives drawn and measured, with photos of the space — in Spanish, if that’s what the customer speaks.

What’s next is Ed’s to switch on: once his own prices are saved, customers see an estimate before they send, and a Google listing puts the shop on the map for the first time.