Move green, save green. A web app that rewards Richmond residents for car-free commuting. Set a weekly goal (bike, bus, carpool, or walk), confirm you met it via a Sunday email check-in, and unlock coupons from local businesses.
Built for the 2026 Richmond Hackathon.
- Next.js 16 (App Router) — framework
- HeroUI v3 — UI components
- Auth.js v5 — magic link authentication (no passwords)
- Prisma 6 + MongoDB — database
- Nodemailer — SMTP email (magic links + weekly check-ins)
- Vercel Cron — scheduled weekly check-in emails
- Node.js 20+
- A MongoDB database (free tier on MongoDB Atlas works great)
- SMTP credentials for sending email (we use AWS SES, but any SMTP provider works)
git clone <repo-url>
cd hackathon-26
npm installcp .env.example .env.localEdit .env.local and fill in your values. Ask Joe for the shared credentials if you don't have them.
You also need a .env file for Prisma CLI commands (it doesn't read .env.local):
echo 'DATABASE_URL="<your-mongodb-url>"' > .envnpx prisma generate
npx prisma db pushThis creates all the collections and indexes in MongoDB.
npx prisma db seedThis loads 5 placeholder rewards from Richmond businesses (Lamplighter, Carytown Bikes, etc.) valid for the current and next month.
npm run devOpen http://localhost:3000.
- Sign in — enter your email, click the magic link
- Set a goal — pick a commute mode (bike/bus/carpool/walk) and days per week
- Sunday check-in — a cron job emails everyone: "Did you meet your goal?" with a confirm link
- Confirm — click "Yes, I did it!" to unlock that week's reward coupons
- Claim rewards — view coupon codes from local businesses
The cron job normally fires on Sundays via Vercel Cron, but you can trigger it manually:
curl -H "Authorization: Bearer <your-CRON_SECRET>" http://localhost:3000/api/cron/weekly-checkinThis sends check-in emails to all users who have a goal set.
src/
├── app/
│ ├── page.tsx # Landing page / dashboard
│ ├── auth/signin/ # Magic link sign-in
│ ├── auth/verify/ # "Check your email" page
│ ├── goal/ # Set/edit weekly goal
│ ├── checkin/confirm/ # Token-based check-in confirmation
│ └── api/
│ ├── auth/[...nextauth]/ # Auth.js route handler
│ └── cron/weekly-checkin/ # Vercel Cron endpoint
├── components/
│ ├── GoalForm.tsx # Goal mode + days picker
│ ├── RewardCard.tsx # Reward display (locked/unlocked/claimed)
│ ├── RewardGrid.tsx # Grid of reward cards
│ └── CheckInHistory.tsx # Past check-in list
├── lib/
│ ├── auth.ts / auth.config.ts # Auth.js configuration
│ ├── prisma.ts # Database client
│ ├── email.ts # SMTP transport
│ ├── weeks.ts # ISO week helpers
│ └── actions/ # Server actions (goal, rewards)
└── types/
└── next-auth.d.ts # Session type extensions
Users with isAdmin: true see an Admin button in the site header linking to /admin. To make a user an admin, set isAdmin to true on their User document in MongoDB.
The admin dashboard provides:
- Stats — total users, users with goals, confirmed check-ins this week, and redemptions by reward
- Reward management — create, edit, activate/deactivate rewards. Each reward has a title, description, business name, logo, website URL, coupon code, validity dates, and optional max redemptions
RideShift launches with confirmed local business partners:
- West Broad Studios — recording studio offering discounted sessions
- Rushing Blooms — florist offering purchase discounts
- Moulton Hot Natives — hot sauce maker offering purchase discounts
Additional businesses can be onboarded by adding reward records via Prisma Studio or the seed file.
RideShift operates independently. It requires no city systems integration, no city budget, and no city staff time. The platform connects residents directly with local businesses — the City of Richmond benefits from reduced VMT and emissions without any operational burden.
RideShift uses a trust-based honor system rather than GPS tracking, photo verification, or transit card integration. This is a deliberate design decision:
- Lower barriers: No app permissions, no location sharing, no special hardware
- Broader access: Works for users without smartphones, with limited data plans, or with privacy concerns
- Civic trust: Richmond trusts its residents — invasive verification creates friction that disproportionately excludes the communities transit programs aim to serve
- Higher participation: The dramatically higher participation rate from frictionless check-ins outweighs any marginal gaming of the system
The weekly email check-in (confirm with one click) keeps the commitment lightweight and sustainable.
The /checkin/preview route provides a development and demo tool for previewing the check-in confirmation experience without a real check-in token:
?response=yes— preview the success confirmation page?response=no— preview the "not this week" page with feedback form
This exists for rapid UI iteration during development and for demo presentations where you want to show the check-in flow without triggering real emails or database writes.
RideShift has been audited for accessibility:
- Skip-to-content link for keyboard navigation
- Explicit form labels on all inputs (including screen-reader-only labels for visual controls)
- ARIA labels on navigation and interactive elements
aria-liveregions for dynamic content updates (feedback submission, form states)- Status indicators use text labels alongside color (not color-only)
- Semantic heading hierarchy across all pages
- Responsive design tested for mobile viewports
| Task | Command |
|---|---|
| Dev server | npm run dev |
| Production build | npm run build |
| Push schema changes | npx prisma db push |
| Seed rewards | npx prisma db seed |
| Browse database | npx prisma studio |
| Trigger check-in (local) | curl -H "Authorization: Bearer $CRON_SECRET" http://localhost:3000/api/cron/weekly-checkin |
| Test check-in email | npx tsx scripts/send-checkin.ts you@example.com |
| Preview check-in email | npx tsx scripts/send-checkin.ts you@example.com --dry-run |