Zephz

One platform for the brand and the dealers who sell it.

The catalog site and its CMS, and now a wholesale portal where dealers order by size run, built to take over from a Classic ASP dealer system that is still in daily use.

Role
Product design · full-stack build · data migration
Stack
Next.js 15 · NestJS 11 · PostgreSQL · Prisma
Status
Site live · dealer portal built, launch next
Markets
.com en-US · .eu en-EU
Zephz home page with the featured shoe
The brand's home page, on the .com and the .eu. www.zephz.com

What it needed to solve

Zephz makes performance footwear for three very different worlds: cheerleading, team sports and court sports. It sells through retailers, and wholesale through its dealers.

01There was no single place that presented the brand and the full range.

02Two markets at once: a US-facing .com and an EU-facing .eu.

03Content the team had to edit themselves, without a developer in the loop.

04And wholesale ran on a separate Classic ASP system, used by dealers and staff every day.

What was needed was one platform for the brand and its dealers, run by the team itself.

What was needed was one platform for the brand and its dealers, run by the team itself.

How we built it

One codebase for shoppers and for dealers.

The public site, the admin and the dealer portal share one NestJS API and one PostgreSQL database.

For shoppers

01

A catalog by sport.

Products organised by the three sport segments, with galleries that take photos and video, size runs for youth, adult and ladies, and related products.

Zephz cheerleading catalog
02

Catalog first, retail one click away.

Each product hands off to retailers through where-to-buy buttons, per market, and the site stays fast and ready for search.

Zephz product page with where-to-buy buttons

For dealers

03

Dealers order by size run.

Each dealer sees the catalog at their own account price, with stock per size and the date when a missing size is due back in.

Zephz dealer portal: ordering a model by size, with account prices and stock
04

Ship complete, or split.

Checkout quotes every line live. When part of an order cannot ship on the chosen date, the dealer chooses: everything together later, or what is in stock now and the rest as a backorder.

Zephz dealer checkout asking whether to ship complete or split into a backorder
05

Every order, and where it stands.

Orders sit under new, future, ship date to be set, shipped and cancelled. Each one opens with its ship date, carrier and lines, prints cleanly, and has a return request one click away.

Zephz dealer order with its status, ship date and lines

For the team

06

One admin for the site and the portal.

Products, news, pages and media sit next to orders, returns, dealers, the wholesale catalog and inventory. Editors and operations staff each see what their role allows.

Zephz admin dashboard with dealer portal and website figures
07

Orders the team can work.

Ship, reopen or cancel with a recorded history, print packing slips, enter phone and fax orders, and keep each backorder linked to its original order.

Zephz admin order detail linked to its backorder

Two apps, one API.

A Next.js app serves the public site, the admin and the dealer portal; a NestJS API with Prisma and PostgreSQL holds the content, the catalog and every order. Each market gets its own assortment, prices, retailer links and copy from the same database, on its own domain.

Dealers

A separate login with its own token audience, single-use, hashed invitation and reset links, a lockout after repeated failed logins, and rate limits per IP. The portal reaches the API only through the web app, with the token in an HttpOnly cookie.

Orders

Placing an order is idempotent and runs in one transaction with the stock rows locked: a retried request never creates a second order, and two orders never take the same pair. The old system's rules for ship dates, backorders, drop ship and returns carry over.

Stock

Inventory moves through a CSV in the warehouse's own layout: export, edit, then upload with a dry run before anything changes.

E-mail

Every e-mail, from invitations and order receipts to warehouse notices, shipping notices and low-stock alerts, goes through a transactional outbox with retries, and staff see what failed.

Audit

Every staff change is written to an audit log in the same transaction as the change itself.

Bringing the dealer history across.

The portal takes over from the Classic ASP system together with its data: accounts, logins, locations and every order back to 2003. The import is rehearsed against a snapshot of the production database before it goes live.

extractReads only

Copies the legacy tables with row counts and checksums, using fixed SELECTs.

dry runRolls back

The whole import runs in one transaction, then rolls back and writes a report.

--applyCommits on a match

Only when row counts, stock, order lines and totals per year and per account all reconcile.

In the rehearsal every reconciliation check passed, and a second run changed nothing. The import sends no e-mail and deletes nothing; dealers move over through invitations.

Built on.

Web
Next.js 15 · React 19 · MUI 7
API
NestJS 11 · Prisma 6
Database
PostgreSQL
Media
Google Cloud Storage
Hosting
Vercel (web) · Hetzner with Docker (API)
Tooling
pnpm workspaces · Turborepo

Checked.

  • 29 unit tests and 109 end-to-end tests on the API, against a dedicated test database.
  • The old system's rules recorded one by one, each with its source in the legacy code.
  • Prisma migrations as the only way the schema changes.
  • In development, e-mail is only logged, never sent.

Where it stands today.

The site is live: one home for the brand and the whole range, consistent across sports, languages and regions, that the team updates themselves. Shoppers browse the catalog and click straight through to a retailer.

The dealer portal is built on the same platform and rehearsed against the production legacy data. What remains is the launch: the production import and inviting every dealer over.

  • Catalog by three sports
  • Galleries with photo and video
  • Own CMS with roles
  • .com in en-US, .eu in en-EU
  • Where-to-buy hand-off
  • Dealer catalog at account prices
  • Size-run ordering with live availability
  • Orders, returns and packing slips
  • Inventory CSV with a dry run
  • Import rehearsed on production data
  • Production import and dealer invitations
  • Public store locator
“TherraCode gave our brand the home it never had - every shoe, every story, in every market, all in one place we control.”
Zephz

Selling through dealers?

Tell us how your dealers order today. We'll come back within one business day with a clear plan.

Next project · Platform

UK Cheerleading Association

A full-stack platform that runs the whole sport: clubs, competitions, courses, certifications and payments in one place.