Hackathon project (GDG 2026). Carbon Pals helps users estimate their carbon footprint, complete AI‑generated tailored daily tasks, and track progress with social features.
- Onboarding questionnaire to estimate baseline footprint.
- Daily AI-generated tasks (10 per day) tailored to the user.
- Task completion with optional photo proof (compressed + stored in Firebase Storage).
- Progress tracking (streaks, totals, charts).
- Social features: friends and communities.
- Public feed showing recent completed tasks.
- Next.js (App Router) + React + TypeScript
- Firebase Auth + Firestore + Firebase Storage
- OpenRouter (Gemini) for AI tasks
- Tailwind CSS + Framer Motion
- Install dependencies:
npm install- Create
.envwith the following values:
OPENROUTER_API_KEY="..."
FIREBASE_SERVICE_ACCOUNT_JSON='{"type":"service_account",...}'
NEXT_PUBLIC_FIREBASE_API_KEY="..."
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN="..."
NEXT_PUBLIC_FIREBASE_PROJECT_ID="..."
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET="..."
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID="..."
NEXT_PUBLIC_FIREBASE_APP_ID="..."
BACKFILL_SECRET="..." # optional, for admin backfill routeNotes:
FIREBASE_SERVICE_ACCOUNT_JSONmust be the raw JSON string (minified is fine) wrapped in single quotes.- If
private_keycontains\n, it will be normalized automatically. - You must restart the dev server after editing
.env.
- Run the dev server:
npm run dev/landing page/loginand/signup/questionnaire/homemain dashboard/socialcommunities + friends/community/[id]community detail/friend/[id]friend profile/feedpublic feed
username: stringemail: stringcity: stringinitialFootprintKg: number | nullcarbonOffsetKgTotal: numbertasksCompletedCount: numberstreakCurrent: numberstreakBest: numberlastCompletionDateKey: string | nulldailyTasksMeta: { dateKey: string, generatedAt: timestamp } | nulldailyTasks: Array<{ id, title, carbonOffsetKg, difficulty?, reason?, dateKey, createdAt }>completedTaskIds: string[]friends: string[]communities: string[]questionnaireAnswers: object | nullquestionnaireVersion: string | nullquestionnaireCompression: object | nullupdatedAt: timestamp
Duplicate of global completed task document for user-only access.
name: stringmembers: string[]createdBy: stringcreatedAt: timestamp
Global feed collection.
uid: stringusername: stringuserEmail: string | nulltitle: stringcarbonOffsetKg: numberimageUrl: string | nulldateKey: stringcompletedAt: timestampsourceDailyTaskId: string | null
dateKey: stringtasksCompleted: numbercarbonOffsetKg: numberupdatedAt: timestamp
dateKey: stringtasksCompleted: numbercarbonOffsetKg: numberupdatedAt: timestamp
tasksCompleted: numbercarbonOffsetKg: numberupdatedAt: timestamp
POST /api/signupPOST /api/loginPOST /api/logoutGET /api/profile
GET /api/questionnaire/v1POST /api/questionnaire/submitPOST /api/questionnaire/reset
GET /api/daily-tasksPOST /api/complete-task
POST /api/social/add-friendPOST /api/social/create-communityPOST /api/social/join-communityGET /api/social/meGET /api/social/userGET /api/social/communityGET /api/social/all-communities
GET /api/feedGET /api/home-statsGET /api/completed-tasks
POST /api/admin/backfill-completed(requiresBACKFILL_SECRET)
Tasks are generated via OpenRouter using the Gemini model:
google/gemini-2.0-flash-exp- Output is strict JSON
- Exactly 10 tasks per day
- Carbon reduction is clamped to 1–10 kg
On /home, task proof images are:
- Resized to 300x300
- Compressed to JPEG (quality 0.4)
- Uploaded to
taskProof/{uid}/{dateKey}/{taskId}.jpg
- Ensure
FIREBASE_SERVICE_ACCOUNT_JSONis valid JSON in.env - Use single quotes around the JSON
- Restart the dev server
Some queries may require composite indexes. The Firestore console link in the error will create the index automatically.
- Alexander Jando
- Chris Li
- Ben Rosa
- Sophia Bianchi