Next.js 14  ·  TypeScript  ·  PostgreSQL  ·  Google Cloud Run  ·  Prisma ORM  ·  shadcn/ui + Tailwind
Volunteer and Auth
  • NextAuth.js v5 — email/password and Google OAuth, database sessions, role-based middleware on /dashboard, /org/*, /admin/*
  • Recurring event signup with four scopes: single, all, until date, pick specific dates — one VolunteerSignup row per occurrence, generated at signup time via rrule expansion
  • Availability grid (7 days × 3 time buckets) in VolunteerAvailability table — queried before sending new-event notifications to filter by day and time bucket
  • Add to Calendar on signup confirmation and each dashboard row — Google, Apple, Microsoft 365, Outlook via add-to-calendar-button-react, client-side only, no API key
  • QR self-check-in via HMAC-SHA256 signed URL, server-validated against CHECKIN_SECRET, registered users only — unregistered volunteers checked in manually
Organization and Infrastructure
  • Recurring event RRULE builder — occurrences generated up to 12 months at save time, extended by monthly Cloud Scheduler cron at /api/cron/generate-occurrences
  • Unified roster — VolunteerSignup and UnregisteredSignup in one table, one-click check-in on both record types
  • /org/volunteers — two-tab directory, signed claim token on invite, migrates UnregisteredSignup to VolunteerSignup on account creation, sets claimedAt and claimedByUserId
  • CSV bulk import for events and volunteers — row-level server validation with preview before confirm, separate API routes for template, validate, and confirm
  • Resend notifications — sendEmailToUser (respects emailNotifications flag) and sendEmail (direct address for unregistered) — availability filtering on Trigger 1 only, Triggers 4 and 5 reach both record types
  • Cloud Run (scales to zero) + Cloud SQL db-f1-micro + Cloud Storage — Cloud Build CI/CD from GitHub, secrets in Secret Manager, Cloud Scheduler cron for reminders (every 5 min) and occurrence generation (1st of month)
$11 – $22/month  ·  qrcode.react  ·  rrule  ·  add-to-calendar-button-react  ·  bcryptjs  ·  cloudscheduler.googleapis.com
Phase 2 — Native Apps & Expanded Features 50 – 70 additional hours
Expo / React Native  ·  Twilio  ·  same Phase 1 API endpoints, no rebuild
Volunteer-facing
  • Expo/React Native apps for iOS and Android — calls Phase 1 Next.js API routes directly, distributed via App Store and Google Play
  • Push notifications via Expo Notifications — FCM for Android, APNs for iOS, covers new events, reminders, and emergency
  • EventFeedback model — post-event survey triggered 24h after check-in, results visible on org roster
  • Birthday field: birthdayMonth + birthdayDay on User and UnregisteredVolunteer — no year stored, visible to orgs for recognition only
  • Richer profiles: hours history, skill endorsements, in-app notification bell with unread count
Organization-facing
  • Twilio SMS for emergency and time-sensitive broadcasts
  • In-app QR scanner for org staff — replaces printed code, built into native app
  • VolunteerGroup and VolunteerGroupMember models — supports registered and unregistered members, group signup creates individual records, group messaging sends to all members with email on file
  • Filtered export with AND logic: skills, day-of-week availability, specific-date with time bucket, prior check-in on a specific recurring event
  • Resource Library — 10 skill categories, public (no login), markdown body, admin CMS at /admin/resources
  • Phase 2 migration: reverse relations added to User, UnregisteredVolunteer, Organization, and Event — no destructive changes to Phase 1 schema
Adds ~$15 – $30/month for Twilio SMS  ·  all other hosting costs unchanged
Ongoing After Launch low recurring commitment
Maintenance time

2 – 4 hrs/month routine — dependency patches, small fixes, content updates

4 – 8 hrs per major framework update (typically 1 – 2 times per year)

Hosting & platform costs

$11 – $22/month — Cloud Run, Cloud SQL, Cloud Storage, Resend (Phase 1)

Adds ~$15 – $30/month for Twilio SMS (Phase 2 only)

Inside the Build Manual 13 stages  ·  schema  ·  prompts  ·  checklists  ·  audit
A look at what is in the spec — enough to show the thinking, available in full on request

The manual includes a complete Prisma schema, 13 Claude Code prompting stages each with a copy-paste prompt and a verification checklist, and page-by-page specs for every route. Below is a sample: two schema models from Phase 1, a partial Stage 7 checklist, and a selection of issues caught in review before any code was written.

schema.prisma — Phase 1 excerpt
model UnregisteredVolunteer {
  id              String    @id @default(cuid())
  organizationId  String
  firstName       String
  lastName        String
  phone           String
  email           String?
  invitedAt       DateTime?
  claimedAt       DateTime?
  claimedByUserId String?
  claimedByUser   User? @relation(
    "ClaimedAccounts",
    fields: [claimedByUserId],
    references: [id])
  organization  Organization @relation(...)
  signups       UnregisteredSignup[]
}

model VolunteerAvailability {
  id        String  @id @default(cuid())
  userId    String
  dayOfWeek Int
  // 0 = Sunday through 6 = Saturday
  morning   Boolean @default(false)
  afternoon Boolean @default(false)
  evening   Boolean @default(false)
  user      User    @relation(...)
  @@unique([userId, dayOfWeek])
}
Stage 7 — Org Admin Features (checklist excerpt)

Each stage has a matching prompt and checklist. This is a partial view of Stage 7, which covers the unified roster, volunteer directory, and recurring occurrence generation.

  • Roster shows both VolunteerSignup and UnregisteredSignup rows in one table
  • "No account" badge on unregistered rows, "Platform" badge on registered
  • One-click Check In updates row in place without a page reload
  • Invite button sends email and sets invitedAt on record
  • Claim link creates account, migrates signups, sets claimedAt
  • Recurring event saves RRULE and generates occurrences up to 12 months out
  • Edit recurring event shows "this / future / all" prompt
  • Cron occurrence-generation endpoint protected by CRON_SECRET header
Caught in review — before a line of code was written
  • claimedByUserId was a bare FK with no @relation — would have broken prisma generate
  • Stage 13 enabled Cloud Run but never set up Cloud Scheduler — two cron jobs would have had no trigger after deployment
  • Emergency notification only queried VolunteerSignup — unregistered volunteers with email addresses would have been silently skipped
  • sendEmail only accepted a userId — no code path to reach unregistered volunteers who have no account
  • Organization.groups reverse relation missing from Phase 2 migration notes — Prisma would have rejected the schema at build time