Skip to main content
Back to projects
ClientIn Progress

Andar Volunteer Connect

Volunteer management for Iceland's longest-running ski tournament

From PIN to assignment in seconds — a purpose-built volunteer system for Iceland's oldest children's ski tournament. ~100 volunteers, 17 events, 96 tests, zero coordination emails.

The best volunteer systems are invisible to volunteers and obvious to organizers.
Design principle, Andar Volunteer Connect
RoleLead Developer
Duration2024–2026
ClientAndrésar andar leikar
StatusIn Progress

The Challenge

Coordinating ~100 volunteers across a three-day alpine ski tournament with 17 events had relied on phone calls, spreadsheets, and institutional memory. Role assignments were manual, conflicts were invisible until they surfaced on the mountain, and there was no single view of who was where. Volunteers signed up without knowing whether their preferred shift and role was still available. Organizers had no dashboard — just a growing inbox.

The Approach

A PIN-gated registration form collects availability by day and shift, then calls a server-side PostgreSQL RPC that atomically creates the volunteer profile, inserts the registration, runs auto-assignment against open event roles, and handles duplicates — all in one round-trip. The admin dashboard gives organizers a live view of event staffing, a canteen task board, and bulk email via Resend. Public station display pages use a narrow read-only RPC so on-mountain screens show live staffing without admin access. Code-split lazy loading keeps the volunteer-facing form fast on mobile networks.

Outcomes

~100VolunteersRegistered and auto-assigned across Thursday, Friday, and Saturday shifts
17EventsAlpine events across three competition days, each with morning and afternoon roles
96 testsTest coverage83 Playwright e2e tests against the live site + 13 Vitest integration tests
2024–2026EditionsSystem has evolved and been used across three consecutive tournament years
<600msBuild timeVite 8 production build, outputs to /docs for static hosting
0VulnerabilitiesClean npm audit across all production dependencies

The Tournament

Andrésar andar leikar — Andrés Duck Games — is Iceland's longest-running children's alpine ski tournament. Held annually at Skálafell ski resort in Borgarfjörður, the 2026 edition marks the 50th anniversary. Three days of competition, seventeen alpine events, and approximately one hundred volunteers make it one of the largest volunteer-coordinated sporting events in Iceland outside the capital.

Running it requires scheduling roles at every gate and finish area, tracking who is available each day, feeding everyone at the canteen, and keeping a dozen organizers synchronized across a mountain. For years, that coordination happened by phone, spreadsheet, and goodwill.


The Registration Flow

Volunteers access the form with a tournament PIN — low-friction verification, not security. From there, a three-tab form (Thursday / Friday / Saturday) collects morning and afternoon shift preferences. Each shift requires a role selection; the available roles are context-aware — Leikjabraut events show different options than standard gate positions. Zod schema validation with Icelandic error messages catches incomplete submissions before they reach the server.

On submit, a single submit_volunteer_registration RPC runs inside a PostgreSQL transaction: create or update the volunteer profile, insert the registration record, run auto-assignment against open event roles, and guard against duplicate entries. If the RPC isn't yet deployed to the environment, the client falls back to legacy direct writes — so migrations never break the live registration form.

The success screen shows a summary card, a WhatsApp QR code for the volunteer group, and a downloadable PDF of the volunteer's assignment.

Admin Dashboard

  • Event management — create and edit events, attach roles with capacity limits, view staffing status per shift.
  • Volunteer registry — searchable list with inline detail view, manual registration entry for walk-ins, year filter for multi-year history.
  • Canteen task board — per-day coordination notes for food preparation and volunteer catering.
  • Event reports — daily notes and staffing summaries with a print view for mountain use.
  • Bulk email — send tournament communications to all registered volunteers via Resend, authenticated through a Supabase Edge Function so the key never leaves the server.

Public Station Displays

Each competition station has a URL showing live staffing — who is assigned to that position, on which shifts. These pages call get_public_tournament_assignments, a narrow read-only RPC that returns only the fields needed for display. Admin credentials are never required. The pages are designed to load fast on phones in areas with limited mountain connectivity.

Architecture

  • Frontend — React 18, TypeScript (strict mode), Vite 8, React Router v6 (HashRouter for static hosting), Tailwind CSS, shadcn/ui, React Hook Form + Zod, TanStack Query, Lucide icons, jsPDF.
  • Backend — Supabase PostgreSQL with 9 tables and full Row Level Security. Three server-side functions handle registration, public assignment reads, and phone normalization.
  • Code splitting — all 14 admin routes are lazy-loaded via React.lazy(). The volunteer registration bundle stays small for mobile users.
  • CI/CD — GitHub Actions runs typecheck, lint, and build on every push. Vercel deploys on merge to main.
  • Testing — 83 Playwright end-to-end tests across 7 test files run against the live site. 13 Vitest integration tests cover registration logic and RPC fallback behavior.

Database Schema

Nine tables with cascading relationships and full RLS:

  • tournamentseventsevent_rolesassignments
  • tournamentsvolunteer_registrationsprofiles
  • tournamentscanteen_notes, event_reports, tournament_implementation_notes

A unique constraint on assignments prevents duplicates even under concurrent registration. The normalize_phone function strips formatting before profile lookup, so "+354 5551234" and "5551234" resolve to the same volunteer.

Technology Stack

ReactTypeScriptViteSupabaseTailwind CSSPlaywright

Resources

Lessons Learned

  • A server-side RPC for atomic registration eliminated race conditions that occurred when two volunteers submitted simultaneously with client-side writes — the database transaction either succeeds completely or rolls back.
  • Context-aware role filtering (hiding Portavörður/Brautarvinnsla on Leikjabraut shifts) reduces registration errors without requiring organizers to explain the distinction. The form knows the rules.
  • A narrow public RPC for station displays is more reliable than reusing admin-facing queries — it returns exactly what on-mountain screens need and nothing that requires authentication.
  • The RPC fallback pattern (try the new function; fall back to legacy writes if absent) made deploying new database migrations safe. The form kept working while the function was rolled out.
  • Lazy loading all admin routes kept the volunteer-facing bundle small — the registration experience stayed fast even on mountain mobile networks.

Related Projects

View all projects