Aaron Perkel

projects / red-king-registration

Red King Registration

Feis registration and payments for a nonprofit Irish dance tabulation org

Red King Registration screenshot
Status
In development; first season 2027
Role
Sole developer (Aaron Perkel LLC)
Timeline
Sep 2026 – present
Stack
Next.js 16, TypeScript, PostgreSQL (Neon), Drizzle ORM, Auth.js, Stripe, Resend, Vercel, Vitest, Playwright

Highlights

  • Built for a nonprofit that runs three feiseanna a year, about 350 dancers each; first season 2027.
  • The signed Stripe webhook is the only source of payment state; deduplicated events and idempotency keys make retries and off-session waitlist charges safe.
  • 79 Vitest files against in-memory Postgres plus 8 Playwright end-to-end specs, including a concurrent capacity race.

Overview

Red King Tabulation is a nonprofit that runs results for Irish dance competitions (feiseanna) — about 350 dancers per feis, three feiseanna a year. This app replaces a third-party registration service. Families register dancers into competitions, pay through Stripe, and get competitor numbers and schedules by email; feis chairs and admins handle setup, waitlists, check-in, and exports from an admin console.

How it works

Auth

Passwordless sign-in with Auth.js magic links, sent through Resend and rate-limited per address. The emailed link opens a confirm page and only signs you in on a button press, so mail scanners that prefetch links can’t burn the token. Database sessions and three roles — parent, admin, and feis chair — with chairs scoped to their own feis.

Data model

Families own dancers; a registration ties a dancer to a feis, and entries tie a registration to individual competitions. Composite foreign keys keep every entry inside its own feis, CHECK constraints guard valid states, and unique indexes enforce competitor numbers, dancing order, duplicate-dancer detection, and one active waitlist spot per family. Money is integer cents with non-negative checks.

Payments

Stripe Checkout for registration. The signed webhook is the only thing that marks a payment paid, and every event is recorded insert-on-conflict so Stripe’s retries are no-ops. Waitlisted families save a card with a SetupIntent and are charged off-session, with an idempotency key, when a spot opens. Fee rules — per-competition fees, a capped family fee, date-based late fees — live in one pure, tested module.

Capacity

A per-feis dancer cap with 15-minute holds, evaluated when it’s read rather than by a cleanup job — there’s no cron to fail.

Admin and exports

Feis setup (competitions, stages, running order, schedule, waiver), payments including cash and check, waitlist release, competitor numbers, and duplicate review. Exports to the .xlsx format the tabulation workbooks already use, plus PDF check-in sheets. Every admin change writes an audit-log row in the same transaction.

Migrations and tests

16 Drizzle SQL migrations. 79 Vitest files run against in-memory Postgres (PGlite) — fee rules, database constraints, migrations, a concurrent cap race, the webhook — plus 8 Playwright end-to-end specs.

What I’d do next

  • An admin dashboard with an audit-log view, reports, and a full data export.
  • Assisted import of each feis’s syllabus, so setup isn’t typed in by hand.

Links

The code is private (client project). I’m happy to walk through the architecture in an interview.

← All projects