Skip to main content
Back to projects
ClientIn Progress

Andar Volunteer Connect

Volunteer management for the Andrésar andar leikar ski tournament

From PIN to assignment in seconds — a purpose-built volunteer system for the Andrésar andar leikar children’s ski tournament. The 2026 report records 144 volunteers, 458 assignments across 21 events and tasks, and 97% of 472 slots filled.

“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
A pair of children's skis stands upright in the snow, ringed by small glowing lanterns.

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

144 (2026 report)VolunteersRegistered and auto-assigned across Thursday, Friday, and Saturday shifts
21 (events and tasks)EventsEvents and tasks across three competition days, as counted in the 2026 organiser report
96 testsTest declarations83 Playwright e2e declarations + 13 Vitest integration declarations, counted in source on 4 November 2026; this is not a pass rate
2024–2026EditionsSystem has evolved and been used across three consecutive tournament years
<600msBuild time590 ms reported in the 8 April 2026 development log for a Vite 8 production build; outputs to /docs
0npm audit findings0 findings reported by npm audit in the 8 April 2026 development log; not a current security certification

The Tournament

Andrésar andar leikar — Andrés Duck Games — is a children’s alpine ski tournament held since 1976. Held annually at Hlíðarfjall in Akureyri, the 2026 edition marks the 50th anniversary. The 2026 organiser report records three competition days, 21 events and tasks, and 144 registered volunteers.

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:

  • tournaments → events → event_roles → assignments
  • tournaments → volunteer_registrations → profiles
  • tournaments → canteen_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 →