https://github.com/rockacola/cdawgva-cyclethon-app
Real-time charity donation tracker for CDawgVA's cross-Japan cycling event
https://github.com/rockacola/cdawgva-cyclethon-app
chakra-ui charity donation-tracker nextjs reactjs server-sent-events typescript vercel
Last synced: 29 days ago
JSON representation
Real-time charity donation tracker for CDawgVA's cross-Japan cycling event
- Host: GitHub
- URL: https://github.com/rockacola/cdawgva-cyclethon-app
- Owner: rockacola
- Created: 2026-04-04T00:08:54.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-05-22T07:07:33.000Z (about 1 month ago)
- Last Synced: 2026-05-22T15:25:32.736Z (about 1 month ago)
- Topics: chakra-ui, charity, donation-tracker, nextjs, reactjs, server-sent-events, typescript, vercel
- Language: TypeScript
- Homepage: https://cdawgva-cyclethon.vercel.app
- Size: 4.1 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# CDawgVA Cyclethon Tracker
A real-time donation tracker built for the [CDawgVA Cyclethon 5](https://tiltify.com/@cdawgva/cyclethon-5) charity event, a 15-day cross-Japan cycling challenge. The app ingests live donation data via Tiltify webhooks and a Cloudflare R2 data feed, then presents it through multiple interactive views: live feed, donor leaderboards, searchable history, journey progress, and themed "donation wars."
Built with **Next.js 15**, **React 19**, **Chakra UI v3**, and **TypeScript**. Supports English and Japanese with timezone-aware formatting.
## Demo
**Live site:** [cdawgva-cyclethon.vercel.app](https://cdawgva-cyclethon.vercel.app/)

Key screens include:
- **Live Feed** (`/donations/live`): streaming donation table with relative timestamps
- **Top Donors** (`/donations/top`): ranked leaderboard by total donated and largest single donation
- **Search** (`/donations/search`): full history with text search, amount filtering, sorting, and pagination
- **Journey** (`/journey`): day-by-day route tracker with maps, daily stats, and embedded clips
- **About Cyclethon** (`/about-cyclethon`): historical context across all five editions — cumulative donation charts, edition comparisons by route, days, and total raised
- **Finish Line** (`/finish-line`): details for the IRL finish line event (venue, reservations, special pizzas)
- **About** (`/about`): tracker data sources and ways to support the cause
- **Donation Wars** (`/stats/donation-war`): category tallies where donations are classified by theme (anime, games, countries, etc.) using regex pattern matching on donor comments
## Tech Stack
| Layer | Technology |
| ------------ | ----------------------------------------------------- |
| Framework | Next.js 15 (App Router, Server Components, React 19) |
| UI | Chakra UI v3, Lucide Icons, Recharts |
| Language | TypeScript 5.8 (strict mode) |
| Real-time | Tiltify Webhooks (HMAC-SHA256) + Server-Sent Events |
| Data | Cloudflare R2 (public JSON bucket) |
| i18n | Custom hook-based system (EN/JA), no external library |
| Hosting | Vercel (auto-deploy on push to `main`) |
| Code Quality | ESLint 9, Prettier, TypeScript strict |
## Architecture
```text
Browser <-- SSE stream ── Next.js API route <-- Tiltify webhook (HMAC verified)
│ │
│ ▼
└── polling (30s) --> Cloudflare R2 (JSON) <-- external data pipeline
```
**Data flow:**
1. **Tiltify** sends donation events to `/api/webhooks/tiltify`, verified with HMAC-SHA256
2. Events are broadcast to connected clients via an in-memory **EventEmitter** singleton and **Server-Sent Events** (`/api/donations/stream`)
3. As a fallback, pages also **poll Cloudflare R2** every 30 seconds for the latest JSON snapshot
4. Server Components fetch data at request time with **60-second revalidation** for fast initial loads
**State and rendering:**
- Server Components handle initial data fetch (SSR with cache revalidation)
- A **DonationsContext** provider shares donation state across client components
- Three context providers manage user preferences: appearance (dark/light/system), locale (EN/JA), and timezone (JST/UTC/local), all persisted in localStorage
## Key Features
- **Real-time updates** via webhook-driven SSE with polling fallback
- **Donation war classifier**: regex pattern engine that categorizes donations into themed wars (anime, games, countries, consoles, pizza, Pokemon, Gacha, Sanrio, Uma Musume, etc.) by parsing donor comments
- **Custom i18n**: lightweight hook-based translation system with namespace scoping, no external dependency
- **Multi-timezone support**: timestamps render in JST, UTC, or local time; currency symbols adapt accordingly
- **Animated counters**: donation totals animate smoothly using `requestAnimationFrame` with cubic ease-out
- **Responsive design**: mobile drawer navigation, adaptive chart sizing (30 bars mobile / 60 desktop)
- **Dark/light mode**: system-aware theming via next-themes + Chakra UI color mode
## What This Demonstrates
| Skill | Where it shows up |
| ------------------------- | -------------------------------------------------------------------------------------- |
| **Frontend architecture** | Next.js App Router with server/client component split, context providers, custom hooks |
| **Real-time systems** | Webhook ingestion, HMAC signature verification, SSE broadcasting, polling fallback |
| **TypeScript** | Strict mode, typed interfaces for all data, type-safe context/hooks |
| **API design** | REST webhook endpoint, SSE streaming endpoint, structured error handling |
| **State management** | React Context for global state, localStorage persistence, animated transitions |
| **i18n engineering** | Custom translation system with namespace lookup, locale detection |
| **Data processing** | Regex-based text classification, donation aggregation, multi-currency handling |
| **UI/UX** | Responsive layouts, dark mode, timezone handling, loading states, search with filters |
| **DevOps** | Vercel CI/CD, Cloudflare R2 integration, environment-based configuration |
## Getting Started
### Prerequisites
- Node.js 18+
- npm
### Installation
```bash
# Clone the repository
git clone https://github.com/rockacola/cdawgva-cyclethon-app.git
cd cdawgva-cyclethon-app
# Install dependencies
npm install
# Set up environment variables
cp .env.example .env.local
```
### Environment Variables
Edit `.env.local` with:
| Variable | Required | Description |
| ---------------------------- | -------- | ------------------------------------------------------------------------------- |
| `NEXT_PUBLIC_R2_BASE_URL` | Yes | Base URL of the Cloudflare R2 public bucket serving donation JSON files |
| `TILTIFY_WEBHOOK_SIGNING_ID` | No | Secret for verifying Tiltify webhook signatures (not needed for local browsing) |
### Run
```bash
npm run dev # Start dev server at http://localhost:3000
```
### Other Commands
```bash
npm run build # Production build
npm start # Start production server
npm run lint # ESLint
npm run typecheck # TypeScript type checking
npm run format # Prettier (write)
npm run check # Run format + lint + typecheck
```
## Project Structure
```text
src/
├── app/ # Next.js App Router pages
│ ├── api/ # API routes (webhook handler, SSE stream)
│ ├── about/ # About the tracker (data sources, how to support)
│ ├── about-cyclethon/ # Cyclethon history and edition comparisons
│ ├── donations/ # Donations section: live, search, top donors
│ ├── finish-line/ # Finish line IRL event details
│ ├── journey/ # Day-by-day journey pages ([day] dynamic route)
│ ├── stats/ # Donation Wars tracker
│ └── en/, ja/ # Locale redirect routes
├── components/ # React components (UI, charts, leaderboards)
│ └── journey/ # Journey-specific sub-components
├── contexts/ # DonationsContext (global donation state)
├── hooks/ # Custom hooks (polling, translations, animation)
├── lib/ # Data fetching, types, utilities, pattern matchers
├── messages/ # Translation files (en.json, ja.json)
└── providers/ # Chakra, appearance, locale, timezone providers
```
## Future Improvements
- Add unit and integration tests for data processing and pattern matching logic
- Extract donation war patterns into a configurable format (JSON/YAML) for non-developer editing
- Add WebSocket support as an alternative to SSE for bidirectional communication
- Implement donation milestone notifications and achievement badges
- Add historical event comparison (Cyclethon 4 vs 5 stats)