← Back to selected work

Case study

NavisSedes

Live system serving paid and free events with interactive seating, 10-minute holds, Stripe checkout, QR tickets, and role-based admin check-in.

NavisSedes overview

Proof points

  • 10-minute atomic seat holds
  • Paid + free checkout paths
  • Versioned seating plans (Draft → Published)
  • Vitest + Playwright coverage

Problem

A church concert series needed reliable seat reservations, payments, invoicing, and door check-in — not a generic ticketing SaaS.

Approach

Next.js 16 + Prisma/Neon domain model (Venue → Plan → Seat), atomic seat holds, Stripe webhooks with mock fallback, QR tokens, admin scanner, Vitest + Playwright.

Challenge

Concurrent seat selection and hold expiry had to stay consistent under checkout races without overselling.

Trade-off

Chose optimistic locking + short holds over websockets — simpler ops, clearer failure modes, easier to test under load.

Architecture decisions

  • Venue → SeatingPlan → Section → Bench → Seat
  • AVAILABLE → HELD → sold/released lifecycle
  • Stripe Checkout + webhook (mock fallback for local/dev)
  • QR tokens without PII + staff scanner roles

How I tested this

Vitest covers domain/services; Playwright covers booking and admin flows — quality is part of the delivery pipeline, not a afterthought.

What I owned

End-to-end product ownership: seating plans, checkout, payments, tickets/invoices, role-based admin, and automated regression coverage.

What I'd do differently

Next I would add stronger observability around hold expiry/webhook retries and a staging seed that mirrors production seating density.

Product walkthrough

NavisSedes screen
NavisSedes screen
NavisSedes screen

Stack

Next.jsPrismaNeonStripePlaywrightVitest