# Project export: MoleculeIQ

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Molecule-IQ Waste Management: Harnessing AI to streamline waste reporting and reward eco-friendly actions, creating a smarter and more sustainable community solution.
- Devpost: https://devpost.com/software/moleculeiq-v5w9p2
- GitHub: https://github.com/vishomallaoli/mol-iq-waste-mgmt
- Demo: https://mol-iq-waste-mgmt.vercel.app/
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

: The idea for Molecule-IQ Waste Management was inspired by the increasing need for efficient and sustainable waste management solutions. With growing concerns around waste disposal and the environmental impact, I wanted to create a platform that not only streamlines the waste reporting process but also motivates users to take eco-friendly actions. By incorporating AI for real-time waste verification and using gamification to engage users, we aimed to create a community-driven approach to addressing the waste crisis.

### What it does

: Molecule-IQ Waste Management is an AI-powered platform that simplifies waste reporting and rewards users for making sustainable choices. The platform leverages real-time AI verification to ensure accurate waste categorization, and gamification features—such as points and leaderboards—encourage users to engage more actively. It promotes a community-focused solution to waste management by rewarding users for their eco-friendly actions and providing a seamless, user-friendly experience.

### How we built it

: I built the platform using a modern, full-stack tech stack, including: • Next.js 14 for fast, server-rendered pages and seamless API integration. • Tailwind CSS for responsive, elegant design. • Google Gemini AI for real-time AI-assisted waste categorization and verification. • Drizzle ORM to handle database interactions with Neon Database. • Web3Auth for secure and decentralized authentication. The entire platform was designed with scalability and performance in mind, ensuring that as more users engage, the system remains efficient and responsive.

### Challenges we ran into

: I faced several challenges throughout development: • Database Connection Issues: Integrating Drizzle ORM with Neon Database led to numerous hiccups. Ensuring stable database connections was a recurring issue, especially when working with real-time data. • Deployment Errors: While the platform worked perfectly on our local network, deploying it on Vercel introduced unexpected authentication errors, particularly around Web3Auth. Debugging and solving these deployment-specific issues took more time than anticipated.

### Accomplishments we're proud of

: Despite the challenges, I successfully built a beautiful, fully functional full-stack web application. The platform features a working database, responsive design, and operational APIs, all seamlessly integrated into a cohesive user experience. We’re particularly proud of the fact that we created a visually appealing product that functions as intended and addresses a real-world problem

### What we learned

: Originally, I was working on a different project related to drug research, but after realizing that it lacked a clear problem statement, I decided to pivot towards Molecule-IQ Waste Management. This experience taught us the importance of recognizing when to stop and change direction while staying focused on the broader goal. We had to adapt quickly and work efficiently under time constraints, learning several new technologies in the process. We also gained deeper experience working with Git, especially in managing version control and collaborating effectively on a team.

### What's next

: Moving forward, I and others aim to take Molecule-IQ Waste Management to the next level by getting real users on board. Gathering feedback is crucial for improving the platform, so we plan to establish a feedback loop where user suggestions can directly influence future updates. Our goal is to refine the user experience, enhance the AI-powered features, and expand the platform’s reach to become a staple solution in sustainable waste management.

## README (from the GitHub repository)

# MoleculeIQ - Waste Buster
<p align="center">
  <a href="https://nextjs.org/">
    <img src="https://img.shields.io/badge/Next.js-000000?style=flat-square&logo=nextdotjs&logoColor=white" alt="Next.js">
  </a>
  <a href="https://orm.drizzle.team/">
    <img src="https://img.shields.io/badge/Drizzle_ORM-009688?style=flat-square&logo=drizzle-orm&logoColor=white" alt="Drizzle ORM">
  </a>
  <a href="https://tailwindcss.com/">
    <img src="https://img.shields.io/badge/TailwindCSS-38B2AC?style=flat-square&logo=tailwind-css&logoColor=white" alt="TailwindCSS">
  </a>
  <a href="https://ai.google.dev/">
    <img src="https://img.shields.io/badge/Google_Gemini_AI-4285F4?style=flat-square&logo=google&logoColor=white" alt="Google Gemini AI">
  </a>
  <a href="https://web3auth.io/">
    <img src="https://img.shields.io/badge/Web3Auth-39C1E2?style=flat-square&logo=web3auth&logoColor=white" alt="Web3Auth">
  </a>
  <a href="https://neon.tech/">
    <img src="https://img.shields.io/badge/Neon_Database-00A6F9?style=flat-square&logo=neon&logoColor=white" alt="Neon Database">
  </a>
</p>
Welcome to the *Mol IQ - Waste Management* project! This repository contains the source code for **Zero2Hero**, an AI-powered waste management platform designed to incentivize and streamline waste reporting and collection. Our goal is to create a community-driven approach to waste management, rewarding users for their eco-friendly actions.

## 🎓 What You'll Learn:
- **Next.js 14** fundamentals and best practices
- Full stack development with **Next.js**
- Integrating **AI** (Google's Gemini AI) into your Next.js app
- **TypeScript** implementation in Next.js
- State management with **React hooks**
- Responsive design using **Tailwind CSS**
- Authentication with **Web3Auth**
- Database integration using **Drizzle ORM**
- Deployment of Next.js projects

## 📚 Project Highlights:
- **AI-assisted waste verification**
- **User reward system** for eco-friendly actions
- Real-time **waste collection task management**
- Interactive **leaderboard** for community engagement

## 👨‍💻 Step-by-Step Guide:
1. Setting up a **Next.js 14** project
2. Creating a responsive layout with **Tailwind CSS**
3. Implementing **Web3Auth** authentication
4. Database design and integration with **Drizzle ORM**
5. **AI model integration** for waste verification
6. Building interactive UI components (e.g., leaderboard, reward system)
7. Deploying your **Next.js** application

## 📚 Materials/References:
- GitHub Repo (give it a star ⭐): [https://github.com/mendsalbert/zero-t](https://github.com/vishomallaoli/mol-iq-waste-mgmt.git)
- README (assets & code): [https://github.com/mendsalbert/zero-t](https://github.com/vishomallaoli/mol-iq-waste-mgmt.git)
## Installation

1. Clone the repository:

   ```bash
   git clone https://github.com/vishomallaoli/mol-iq-waste-mgmt
   cd mol-iq-waste-mgmt

	2.	Install dependencies:

npm install


	3.	Set up environment variables:
Create a .env.local file in the root of the project and add the following variables:

NEXT_PUBLIC_WEB3AUTH_CLIENT_ID=your_web3auth_client_id
FIREBASE_API_KEY=your_firebase_api_key
FIREBASE_AUTH_DOMAIN=your_firebase_auth_domain
FIREBASE_PROJECT_ID=your_firebase_project_id


	4.	Start the development server:

npm run dev


	5.	Visit http://localhost:3000 in your browser to view the app.

Deployment

To deploy the project, follow these steps:

	1.	Build the project:

npm run build


	2.	Deploy on Vercel:
	•	If you haven’t already, sign up for Vercel and link your GitHub repository.
	•	Configure environment variables on Vercel to match your .env.local.
	•	Deploy your app with just one click or using the Vercel CLI:

vercel --prod



Your app should now be live at https://your-app-name.vercel.app.

Contributing

We welcome contributions! Here’s how you can help:

	1.	Fork the repository.
	2.	Create a feature branch (git checkout -b feature-branch).
	3.	Commit your changes (git commit -m "Add a feature").
	4.	Push to the branch (git push origin feature-branch).
	5.	Open a pull request.

Please make sure to update tests as appropriate.

License

This project is licensed under the MIT License. See the LICENSE file for details.


## Detected evidence (automated analysis)

Indexed codebase: 33 recognized source files, 132 KB.
- CSS (language) — detected in the code
- Google Gemini (technology) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Vercel (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (41 of 41)

```
.DS_Store
.gitignore
components.json
drizzle.config.js
netlify.toml
next.config.mjs
package.json
postcss.config.mjs
README.md
src/app/collect/page.tsx
src/app/globals.css
src/app/layout.tsx
src/app/leaderboard/page.tsx
src/app/messages/page.tsx
src/app/metadata.ts
src/app/page.tsx
src/app/report/page.tsx
src/app/rewards/page.tsx
src/app/settings/page.tsx
src/app/verify/page.tsx
src/components/ContractInteraction.tsx
src/components/createSchemas.tsx
src/components/DimoEcoScore.tsx
src/components/DimoVehicleData.tsx
src/components/Header.tsx
src/components/Map.tsx
src/components/Sidebar.tsx
src/components/ui/badge.tsx
src/components/ui/button.tsx
src/components/ui/dropdown-menu.tsx
src/components/ui/input.tsx
src/components/ui/select.tsx
src/components/ui/switch.tsx
src/components/UserDashboard.tsx
src/hooks/useMediaQuery.ts
src/lib/utils.ts
src/utils/db/actions.ts
src/utils/db/dbConfig.jsx
src/utils/db/schema.ts
tailwind.config.ts
tsconfig.json
```

### Dependencies

- package.json: @chainlink/contracts@^1.2.0, @dimo-network/dimo-node-sdk@^1.2.0, @ethsign/sp-sdk@^0.7.2, @google/generative-ai@^0.17.1, @layerzerolabs/lz-evm-oapp-v2@^2.3.39, @lit-protocol/lit-node-client@^6.4.10, @neondatabase/serverless@^0.9.4, @nomicfoundation/hardhat-chai-matchers@^2.0.0, @nomicfoundation/hardhat-ethers@^3.0.0, @nomicfoundation/hardhat-ignition@^0.15.5, @nomicfoundation/hardhat-ignition-ethers@^0.15.0, @nomicfoundation/hardhat-network-helpers@^1.0.0, @nomicfoundation/hardhat-toolbox@^5.0.0, @nomicfoundation/hardhat-verify@^2.0.0, @nomicfoundation/ignition-core@^0.15.5, @openzeppelin/contracts@^5.0.2, @radix-ui/react-dropdown-menu@^2.1.1, @radix-ui/react-select@^2.1.1, @radix-ui/react-slot@^1.1.0, @radix-ui/react-switch@^1.1.0, @react-google-maps/api@^2.19.3, @typechain/ethers-v6@^0.5.0, @typechain/hardhat@^9.0.0, @types/chai@^4.2.0, @types/leaflet@^1.9.12, @types/mocha@>=9.1.0, @types/node@^20, @types/react@^18, @types/react-dom@^18, @web3auth/base@^8.12.4, @web3auth/ethereum-provider@^8.12.4, @web3auth/modal@^8.12.6, class-variance-authority@^0.7.0, clsx@^2.1.1, drizzle-kit@^0.24.2, drizzle-orm@^0.33.0, ethers@^5.7.2, hardhat@^2.22.9, hardhat-gas-reporter@^1.0.8, leaflet@^1.9.4, lucide-react@^0.436.0, next@14.2.7, postcss@^8, postgres@^3.4.4, react@^18, react-dom@^18, react-hot-toast@^2.4.1, react-leaflet@^4.2.1, solidity-coverage@^0.8.1, tailwind-merge@^2.5.2, tailwindcss@^3.4.1, tailwindcss-animate@^1.0.7, typechain@^8.3.0, typescript@^5, viem@^2.21.1

### Recent commits (newest first)

- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Replaced old files with new local files
- Merge pull request #3 from vishomallaoli/visho
- Dashboard has been implement --first iteration
- Merge pull request #2 from vishomallaoli/savar
- added header
- Add .gitignore file
- Merge pull request #1 from vishomallaoli/visho
- Remove .next folder from tracking
- Removed node_modules and unnecessary files from tracking
- front end has been developed
- the layout and else implemented
- Create README.md
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "engines": {
  "node": ">=20.0.0"
},
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "db:push": "drizzle-kit push",
    "db:studio": "npx drizzle-kit studio"
  },
  "dependencies": {
    "@chainlink/contracts": "^1.2.0",
    "@dimo-network/dimo-node-sdk": "^1.2.0",
    "@ethsign/sp-sdk": "^0.7.2",
    "@google/generative-ai": "^0.17.1",
    "@layerzerolabs/lz-evm-oapp-v2": "^2.3.39",
    "@lit-protocol/lit-node-client": "^6.4.10",
    "@neondatabase/serverless": "^0.9.4",
    "@nomicfoundation/hardhat-chai-matchers": "^2.0.0",
    "@nomicfoundation/hardhat-ethers": "^3.0.0",
    "@nomicfoundation/hardhat-ignition": "^0.15.5",
    "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0",
    "@nomicfoundation/hardhat-network-helpers": "^1.0.0",
    "@nomicfoundation/hardhat-verify": "^2.0.0",
    "@nomicfoundation/ignition-core": "^0.15.5",
    "@openzeppelin/contracts": "^5.0.2",
    "@radix-ui/react-dropdown-menu": "^2.1.1",
    "@radix-ui/react-select": "^2.1.1",
    "@radix-ui/react-slot": "^1.1.0",
    "@radix-ui/react-switch": "^1.1.0",
    "@react-google-maps/api": "^2.19.3",
    "@typechain/ethers-v6": "^0.5.0",
    "@typechain/hardhat": "^9.0.0",
    "@types/chai": "^4.2.0",
    "@types/mocha": ">=9.1.0",
    "@web3auth/base": "^8.12.4",
    "@web3auth/ethereum-provider": "^8.12.4",
    "@web3auth/modal": "^8.12.6",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "drizzle-kit": "^0.24.2",
    "drizzle-orm": "^0.33.0",
    "ethers": "^5.7.2",
    "hardhat-gas-reporter": "^1.0.8",
    "leaflet": "^1.9.4",
    "lucide-react": "^0.436.0",
    "next": "14.2.7",
    "postgres": "^3.4.4",
    "react": "^18",
    "react-dom": "^18",
    "react-hot-toast": "^2.4.1",
    "react-leaflet": "^4.2.1",
    "solidity-coverage": "^0.8.1",
    "tailwind-merge": "^2.5.2",
    "tailwindcss-animate": "^1.0.7",
    "typechain": "^8.3.0",
    "viem": "^2.21.1"
  },
  "devDependencies": {
    "@nomicfoundation/hardhat-toolbox": "^5.0.0",
    "@types/leaflet": "^1.9.12",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "hardhat": "^2.22.9",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  },
  "resolutions": {
    "@openzeppelin/contracts": "^5.0.1"
  },
  "type": "module"
}

```

### src/app/layout.tsx

```typescript
"use client"

import { useState, useEffect } from "react"
import { Inter } from 'next/font/google'
import "./globals.css"
import Header from "@/components/Header"
import Sidebar from "@/components/Sidebar"
import 'leaflet/dist/leaflet.css'
import { Toaster } from 'react-hot-toast'
import { getAvailableRewards, getUserByEmail } from '@/utils/db/actions'

const inter = Inter({ subsets: ['latin'] })

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  const [sidebarOpen, setSidebarOpen] = useState(false)
  const [totalEarnings, setTotalEarnings] = useState(0)

  useEffect(() => {
    const fetchTotalEarnings = async () => {
      try {
        const userEmail = localStorage.getItem('userEmail')
        if (userEmail) {
          const user = await getUserByEmail(userEmail)
          console.log('user from layout', user);
          
          if (user) {
            const availableRewards = await getAvailableRewards(user.id) as any
            console.log('availableRewards from layout', availableRewards);
                        setTotalEarnings(availableRewards)
          }
        }
      } catch (error) {
        console.error('Error fetching total earnings:', error)
      }
    }

    fetchTotalEarnings()
  }, [])

  return (
    <html lang="en">
      <body className={inter.className}>
        <div className="min-h-screen bg-gray-50 flex flex-col">
          <Header onMenuClick={() => setSidebarOpen(!sidebarOpen)} totalEarnings={totalEarnings} />
          <div className="flex flex-1">
            <Sidebar open={sidebarOpen} />
            <main className="flex-1 p-4 lg:p-8 ml-0 lg:ml-64 transition-all duration-300">
              {children}
            </main>
          </div>
        </div>
        <Toaster />
      </body>
    </html>
  )
}

```

### src/app/page.tsx

```typescript
// @ts-nocheck
'use client'
import { useState, useEffect } from 'react'
import { ArrowRight, Leaf, Recycle, Users, Coins, MapPin, ChevronRight } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Poppins } from 'next/font/google'
import Link from 'next/link'
import ContractInteraction from '@/components/ContractInteraction'
import { getRecentReports, getAllRewards, getWasteCollectionTasks } from '@/utils/db/actions'
const poppins = Poppins({ 
  weight: ['300', '400', '600'],
  subsets: ['latin'],
  display: 'swap',
})

function AnimatedGlobe() {
  return (
    <div className="relative w-32 h-32 mx-auto mb-8">
      <div className="absolute inset-0 rounded-full bg-purple-500 opacity-20 animate-pulse"></div>
      <div className="absolute inset-2 rounded-full bg-purple-400 opacity-40 animate-ping"></div>
      <div className="absolute inset-4 rounded-full bg-purple-300 opacity-60 animate-spin"></div>
      <div className="absolute inset-6 rounded-full bg-purple-200 opacity-80 animate-bounce"></div>
      <Leaf className="absolute inset-0 m-auto h-16 w-16 text-purple-600 animate-pulse" />
    </div>
  )
}

export default function Home() {
  const [loggedIn, setLoggedIn] = useState(false);
  const [impactData, setImpactData] = useState({
    wasteCollected: 0,
    reportsSubmitted: 0,
    tokensEarned: 0,
    co2Offset: 0
  });

  useEffect(() => {
    async function fetchImpactData() {
      try {
        const reports = await getRecentReports(100);  // Fetch last 100 reports
        const rewards = await getAllRewards();
        const tasks = await getWasteCollectionTasks(100);  // Fetch last 100 tasks

        const wasteCollected = tasks.reduce((total, task) => {
          const match = task.amount.match(/(\d+(\.\d+)?)/);
          const amount = match ? parseFloat(match[0]) : 0;
          return total + amount;
        }, 0);

        const reportsSubmitted = reports.length;
        const tokensEarned = rewards.reduce((total, reward) => total + (reward.points || 0), 0);
        const co2Offset = wasteCollected * 0.5;  // Assuming 0.5 kg CO2 offset per kg of waste

        setImpactData({
          wasteCollected: Math.round(wasteCollected * 10) / 10, // Round to 1 decimal place
          reportsSubmitted,
          tokensEarned,
          co2Offset: Math.round(co2Offset * 10) / 10 // Round to 1 decimal place
        });
      } catch (error) {
        console.error("Error fetching impact data:", error);
        // Set default values in case of error
        setImpactData({
          wasteCollected: 0,
          reportsSubmitted: 0,
          tokensEarned: 0,
          co2Offset: 0
        });
      }
    }

    fetchImpactData();
  }, []);

  const login = () => {
    setLoggedIn(true);
  };

  return (
    <div className={`container mx-auto px-4 py-16 ${poppins.className}`}>
      <section className="text-center mb-20">
        <AnimatedGlobe />
        <h1 className="text-6xl font-bold mb-6 text-gray-800 tracking-tight">
          EcoGuard <span className="text-purple-600">Waste Management</span>
        </h1>
        <p className="text-xl text-gray-600 max-w-2xl mx-auto leading-relaxed mb-8">
          Join our community in making waste management more efficient and rewarding!
        </p>
        {!loggedIn ? (
          <Button onClick={login} className="bg-purple-600 hover:bg-purple-700 text-white text-lg py-6 px-10 rounded-full font-medium transition-all duration-300 ease-in-out transform hover:scale-105">
            Get Started
            <ArrowRight className="ml-2 h-5 w-5" />
          </Button>
        ) : (
          <Link href="/report">
            <Button className="bg-purple-600 hover:bg-purple-700 text-white text-lg py-6 px-10 rounded-full font-medium transition-all duration-300 ease-in-out transform hover:scale-105">
              Report Waste
              <ArrowRight className="ml-2 h-5 w-5" />
            </Button>
          </Link>
        )}
      </section>
      
      <section className="grid md:grid-cols-3 gap-10 mb-20">
        <FeatureCard
          icon={Leaf}
          title="Eco-Friendly"
          description="Contribute to a cleaner environment by reporting and collecting waste."
        />
        <FeatureCard
          icon={Coins}
          title="Earn Rewards"
          description="Get tokens for your contributions to waste management efforts."
        />
        <FeatureCard
          icon={Users}
          title="Community-Driven"
          description="Be part of a growing community committed to sustainable practices."
        />
      </section>
      
      <section className="bg-white p-10 rounded-3xl shadow-lg mb-20">
        <h2 className="text-4xl font-bold mb-12 text-center text-gray-800">Our Impact</h2>
        <div className="grid md:grid-cols-4 gap-6">
          <ImpactCard title="Waste Collected" value={`${impactData.wasteCollected} kg`} icon={Recycle} />
          <ImpactCard title="Reports Submitted" value={impactData.reportsSubmitted.toString()} icon={MapPin} />
          <ImpactCard title="Tokens Earned" value={impactData.tokensEarned.toString()} icon={Coins} />
          <ImpactCard title="CO2 Offset" value={`${impactData.co2Offset} kg`} icon={Leaf} />
        </div>
      </section>
    </div>
  )
}

function ImpactCard({ title, value, icon: Icon }: { title: string; value: string | number; icon: React.ElementType }) {
  const formattedValue = typeof value === 'number' ? value.toLocaleString('en-US', { maximumFractionDigits: 1 }) : value;
  
  return (
    <div className="p-6 rounded-xl bg-gray-50 border border-gray-100 transition-all duration-300 ease-in-out hover:shadow-md">
      <Icon className="h-10 w-10 text-purple-500 mb-4" /> {/* Icon color updated to purple */}
      <p className="text-3xl font-bold mb-2 text-gray-800">{formattedValue}</p>
      <p className="text-sm text-gray-600">{title}</p>
    </div>
  )
}

function FeatureCard({ icon: Icon, title, description }: { icon: React.ElementType;
[truncated — 569 more characters]
```

### src/app/messages/page.tsx

```typescript
'use client'
import { useState, useEffect, useRef } from 'react'
import { GoogleGenerativeAI } from '@google/generative-ai'
import { Send, Loader2 } from 'lucide-react'

type Message = {
  role: 'user' | 'assistant'
  content: string
}

export default function MessagesPage() {
  const [messages, setMessages] = useState<Message[]>([])
  const [input, setInput] = useState('')
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState('')
  const messagesEndRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    const API_KEY = process.env.NEXT_PUBLIC_GOOGLE_AI_API_KEY
    console.log('API Key:', API_KEY ? 'Present' : 'Missing')
  }, [])

  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
  }, [messages])

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    if (!input.trim() || isLoading) return

    setIsLoading(true)
    setError('')

    const newMessage: Message = { role: 'user', content: input.trim() }
    setMessages(prev => [...prev, newMessage])
    setInput('')

    try {
      const API_KEY = process.env.NEXT_PUBLIC_GOOGLE_AI_API_KEY
      if (!API_KEY) throw new Error('API key is missing')

      const genAI = new GoogleGenerativeAI(API_KEY)
      const model = genAI.getGenerativeModel({ model: "gemini-pro" })

      console.log('Sending message:', newMessage.content)
      const result = await model.generateContent(newMessage.content)
      const responseText = result.response.text()
      console.log('Received response:', responseText)

      const assistantMessage: Message = { role: 'assistant', content: responseText }
      setMessages(prev => [...prev, assistantMessage])
    } catch (err) {
      console.error('Error:', err)
      setError(err instanceof Error ? err.message : 'An unknown error occurred')
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <div className="flex flex-col h-screen bg-gray-100">
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((msg, index) => (
          <div key={index} className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>
            <div className={`max-w-xs sm:max-w-sm md:max-w-md lg:max-w-lg xl:max-w-xl rounded-lg p-3 ${
              msg.role === 'user' ? 'bg-blue-500 text-white' : 'bg-white text-gray-800'
            }`}>
              <p>{msg.content}</p>
            </div>
          </div>
        ))}
        {error && <p className="text-red-500 text-center">{error}</p>}
        <div ref={messagesEndRef} />
      </div>
      <form onSubmit={handleSubmit} className="p-4 bg-white border-t">
        <div className="flex space-x-2">
          <input
            type="text"
            value={input}
            onChange={(e) => setInput(e.target.value)}
            placeholder="Type your message here..."
            className="flex-grow px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
            disabled={isLoading}
          />
          <button 
            type="submit" 
            className="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50"
            disabled={isLoading}
          >
            {isLoading ? <Loader2 className="w-5 h-5 animate-spin" /> : <Send className="w-5 h-5" />}
          </button>
        </div>
      </form>
    </div>
  )
}
```

### src/app/settings/page.tsx

```typescript
'use client'
import { useState } from 'react'
import { User, Mail, Phone, MapPin, Save } from 'lucide-react'
import { Button } from '@/components/ui/button'

type UserSettings = {
  name: string
  email: string
  phone: string
  address: string
  notifications: boolean
}

export default function SettingsPage() {
  const [settings, setSettings] = useState<UserSettings>({
    name: 'John Doe',
    email: 'john.doe@example.com',
    phone: '+1 234 567 8900',
    address: '123 Eco Street, Green City, 12345',
    notifications: true,
  })

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value, type, checked } = e.target
    setSettings(prev => ({
      ...prev,
      [name]: type === 'checkbox' ? checked : value,
    }))
  }

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    // Here you would typically send the updated settings to your backend
    console.log('Updated settings:', settings)
    alert('Settings updated successfully!')
  }

  return (
    <div className="p-8 max-w-2xl mx-auto">
      <h1 className="text-3xl font-semibold mb-6 text-gray-800">Account Settings</h1>
      
      <form onSubmit={handleSubmit} className="space-y-6">
        <div>
          <label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-1">Full Name</label>
          <div className="relative">
            <input
              type="text"
              id="name"
              name="name"
              value={settings.name}
              onChange={handleInputChange}
              className="pl-10 w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-green-500 focus:border-green-500"
            />
            <User className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={18} />
          </div>
        </div>

        <div>
          <label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">Email Address</label>
          <div className="relative">
            <input
              type="email"
              id="email"
              name="email"
              value={settings.email}
              onChange={handleInputChange}
              className="pl-10 w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-green-500 focus:border-green-500"
            />
            <Mail className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={18} />
          </div>
        </div>

        <div>
          <label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-1">Phone Number</label>
          <div className="relative">
            <input
              type="tel"
              id="phone"
              name="phone"
              value={settings.phone}
              onChange={handleInputChange}
              className="pl-10 w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-green-500 focus:border-green-500"
            />
            <Phone className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={18} />
          </div>
        </div>

        <div>
          <label htmlFor="address" className="block text-sm font-medium text-gray-700 mb-1">Address</label>
          <div className="relative">
            <input
              type="text"
              id="address"
              name="address"
              value={settings.address}
              onChange={handleInputChange}
              className="pl-10 w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-green-500 focus:border-green-500"
            />
            <MapPin className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={18} />
          </div>
        </div>

        <div className="flex items-center">
          <input
            type="checkbox"
            id="notifications"
            name="notifications"
            checked={settings.notifications}
            onChange={handleInputChange}
            className="h-4 w-4 text-green-600 focus:ring-green-500 border-gray-300 rounded"
          />
          <label htmlFor="notifications" className="ml-2 block text-sm text-gray-700">
            Receive email notifications
          </label>
        </div>

        <Button type="submit" className="w-full bg-green-500 hover:bg-green-600 text-white">
          <Save className="w-4 h-4 mr-2" />
          Save Changes
        </Button>
      </form>
    </div>
  )
}
```

### src/app/verify/page.tsx

```typescript
'use client'
import { useState } from 'react'
import { Upload, CheckCircle, XCircle, Loader } from 'lucide-react'
import { Button } from '@/components/ui/button'

export default function VerifyWastePage() {
  const [file, setFile] = useState<File | null>(null)
  const [preview, setPreview] = useState<string | null>(null)
  const [verificationStatus, setVerificationStatus] = useState<'idle' | 'verifying' | 'success' | 'failure'>('idle')
  const [verificationResult, setVerificationResult] = useState<{
    wasteType: string;
    quantity: string;
    confidence: number;
  } | null>(null)

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files && e.target.files[0]) {
      const selectedFile = e.target.files[0]
      setFile(selectedFile)
      const reader = new FileReader()
      reader.onload = (e) => {
        setPreview(e.target?.result as string)
      }
      reader.readAsDataURL(selectedFile)
    }
  }

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    if (!file) return

    setVerificationStatus('verifying')
    
    // Simulating AI verification process
    setTimeout(() => {
      // This is where you would integrate with Galadriel or your AI service
      const mockResult = {
        wasteType: 'Plastic',
        quantity: '2.5 kg',
        confidence: 0.92
      }
      setVerificationResult(mockResult)
      setVerificationStatus('success')
    }, 3000)
  }

  return (
    <div className="p-8 max-w-4xl mx-auto">
      <h1 className="text-3xl font-bold mb-8 text-gray-800">Verify Waste Collection</h1>
      
      <form onSubmit={handleSubmit} className="bg-white p-6 rounded-xl shadow-md mb-8">
        <div className="mb-6">
          <label htmlFor="waste-image" className="block text-sm font-medium text-gray-700 mb-2">
            Upload Waste Image
          </label>
          <div className="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
            <div className="space-y-1 text-center">
              <Upload className="mx-auto h-12 w-12 text-gray-400" />
              <div className="flex text-sm text-gray-600">
                <label
                  htmlFor="waste-image"
                  className="relative cursor-pointer bg-white rounded-md font-medium text-green-600 hover:text-green-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-green-500"
                >
                  <span>Upload a file</span>
                  <input id="waste-image" name="waste-image" type="file" className="sr-only" onChange={handleFileChange} accept="image/*" />
                </label>
                <p className="pl-1">or drag and drop</p>
              </div>
              <p className="text-xs text-gray-500">PNG, JPG, GIF up to 10MB</p>
            </div>
          </div>
        </div>
        
        {preview && (
          <div className="mt-4 mb-6">
            <img src={preview} alt="Waste preview" className="max-w-full h-auto rounded-lg" />
          </div>
        )}
        
        <Button type="submit" className="w-full bg-green-600 hover:bg-green-700 text-white" disabled={!file || verificationStatus === 'verifying'}>
          {verificationStatus === 'verifying' ? (
            <>
              <Loader className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" />
              Verifying...
            </>
          ) : 'Verify Waste'}
        </Button>
      </form>

      {verificationStatus === 'success' && verificationResult && (
        <div className="bg-green-50 border-l-4 border-green-400 p-4 mb-8">
          <div className="flex">
            <div className="flex-shrink-0">
              <CheckCircle className="h-5 w-5 text-green-400" />
            </div>
            <div className="ml-3">
              <h3 className="text-sm font-medium text-green-800">Verification Successful</h3>
              <div className="mt-2 text-sm text-green-700">
                <p>Waste Type: {verificationResult.wasteType}</p>
                <p>Quantity: {verificationResult.quantity}</p>
                <p>Confidence: {(verificationResult.confidence * 100).toFixed(2)}%</p>
              </div>
            </div>
          </div>
        </div>
      )}

      {verificationStatus === 'failure' && (
        <div className="bg-red-50 border-l-4 border-red-400 p-4 mb-8">
          <div className="flex">
            <div className="flex-shrink-0">
              <XCircle className="h-5 w-5 text-red-400" />
            </div>
            <div className="ml-3">
              <h3 className="text-sm font-medium text-red-800">Verification Failed</h3>
              <div className="mt-2 text-sm text-red-700">
                <p>Unable to verify the waste. Please try again with a clearer image.</p>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  )
}
```

### src/app/leaderboard/page.tsx

```typescript
'use client'
import { useState, useEffect } from 'react'
import { getAllRewards, getUserByEmail } from '@/utils/db/actions'
import { Loader, Award, User, Trophy, Crown } from 'lucide-react'
import { toast } from 'react-hot-toast'

type Reward = {
  id: number
  userId: number
  points: number
  level: number
  createdAt: Date
  userName: string | null
}

export default function LeaderboardPage() {
  const [rewards, setRewards] = useState<Reward[]>([])
  const [loading, setLoading] = useState(true)
  const [user, setUser] = useState<{ id: number; email: string; name: string } | null>(null)

  useEffect(() => {
    const fetchRewardsAndUser = async () => {
      setLoading(true)
      try {
        const fetchedRewards = await getAllRewards()
        setRewards(fetchedRewards)

        const userEmail = localStorage.getItem('userEmail')
        if (userEmail) {
          const fetchedUser = await getUserByEmail(userEmail)
          if (fetchedUser) {
            setUser(fetchedUser)
          } else {
            toast.error('User not found. Please log in again.')
          }
        } else {
          toast.error('User not logged in. Please log in.')
        }
      } catch (error) {
        console.error('Error fetching rewards and user:', error)
        toast.error('Failed to load leaderboard. Please try again.')
      } finally {
        setLoading(false)
      }
    }

    fetchRewardsAndUser()
  }, [])

  return (
    <div className="">
      <div className="max-w-3xl mx-auto">
      <h1 className="text-3xl font-semibold mb-6 text-gray-800">Leaderboard </h1>

        {loading ? (
          <div className="flex justify-center items-center h-64">
            <Loader className="animate-spin h-8 w-8 text-gray-600" />
          </div>
        ) : (
          <div className="bg-white shadow-xl rounded-2xl overflow-hidden">
            <div className="bg-gradient-to-r from-purple-500 to-purple-600 p-6">
              <div className="flex justify-between items-center text-white">
                <Trophy className="h-10 w-10" />
                <span className="text-2xl font-bold">Top Performers</span>
                <Award className="h-10 w-10" />
              </div>
            </div>
            <div className="overflow-x-auto">
              <table className="w-full">
                <thead className="bg-gray-50">
                  <tr>
                    <th className="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">Rank</th>
                    <th className="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">User</th>
                    <th className="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">Points</th>
                    <th className="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">Level</th>
                  </tr>
                </thead>
                <tbody>
                  {rewards.map((reward, index) => (
                    <tr key={reward.id} className={`${user && user.id === reward.userId ? 'bg-indigo-50' : ''} hover:bg-gray-50 transition-colors duration-150 ease-in-out`}>
                      <td className="px-6 py-4 whitespace-nowrap">
                        <div className="flex items-center">
                          {index < 3 ? (
                            <Crown className={`h-6 w-6 ${index === 0 ? 'text-yellow-400' : index === 1 ? 'text-gray-400' : 'text-yellow-600'}`} />
                          ) : (
                            <span className="text-sm font-medium text-gray-900">{index + 1}</span>
                          )}
                        </div>
                      </td>
                      <td className="px-6 py-4 whitespace-nowrap">
                        <div className="flex items-center">
                          <div className="flex-shrink-0 h-10 w-10">
                            <User className="h-full w-full rounded-full bg-gray-200 text-gray-500 p-2" />
                          </div>
                          <div className="ml-4">
                            <div className="text-sm font-medium text-gray-900">{reward.userName}</div>
                          </div>
                        </div>
                      </td>
                      <td className="px-6 py-4 whitespace-nowrap">
                        <div className="flex items-center">
                          <Award className="h-5 w-5 text-indigo-500 mr-2" />
                          <div className="text-sm font-semibold text-gray-900">{reward.points.toLocaleString()}</div>
                        </div>
                      </td>
                      <td className="px-6 py-4 whitespace-nowrap">
                        <span className="px-3 py-1 inline-flex text-sm leading-5 font-semibold rounded-full bg-indigo-100 text-indigo-800">
                          Level {reward.level}
                        </span>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        )}
      </div>
    </div>
  )
}
```

### src/app/rewards/page.tsx

```typescript
'use client'
import { useState, useEffect } from 'react'
import { Coins, ArrowUpRight, ArrowDownRight, Gift, AlertCircle, Loader } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { getUserByEmail, getRewardTransactions, getAvailableRewards, redeemReward, createTransaction } from '@/utils/db/actions'
import { toast } from 'react-hot-toast'

type Transaction = {
  id: number
  type: 'earned_report' | 'earned_collect' | 'redeemed'
  amount: number
  description: string
  date: string
}

type Reward = {
  id: number
  name: string
  cost: number
  description: string | null
  collectionInfo: string
}

export default function RewardsPage() {
  const [user, setUser] = useState<{ id: number; email: string; name: string } | null>(null)
  const [balance, setBalance] = useState(0)
  const [transactions, setTransactions] = useState<Transaction[]>([])
  const [rewards, setRewards] = useState<Reward[]>([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    const fetchUserDataAndRewards = async () => {
      setLoading(true)
      try {
        const userEmail = localStorage.getItem('userEmail')
        if (userEmail) {
          const fetchedUser = await getUserByEmail(userEmail)
          if (fetchedUser) {
            setUser(fetchedUser)
            const fetchedTransactions = await getRewardTransactions(fetchedUser.id)
            setTransactions(fetchedTransactions as Transaction[])
            const fetchedRewards = await getAvailableRewards(fetchedUser.id)
            setRewards(fetchedRewards.filter(r => r.cost > 0)) // Filter out rewards with 0 points
            const calculatedBalance = fetchedTransactions.reduce((acc, transaction) => {
              return transaction.type.startsWith('earned') ? acc + transaction.amount : acc - transaction.amount
            }, 0)
            setBalance(Math.max(calculatedBalance, 0)) // Ensure balance is never negative
          } else {
            toast.error('User not found. Please log in again.')
          }
        } else {
          toast.error('User not logged in. Please log in.')
        }
      } catch (error) {
        console.error('Error fetching user data and rewards:', error)
        toast.error('Failed to load rewards data. Please try again.')
      } finally {
        setLoading(false)
      }
    }

    fetchUserDataAndRewards()
  }, [])

  const handleRedeemReward = async (rewardId: number) => {
    if (!user) {
      toast.error('Please log in to redeem rewards.')
      return
    }

    const reward = rewards.find(r => r.id === rewardId)
    if (reward && balance >= reward.cost && reward.cost > 0) {
      try {
        if (balance < reward.cost) {
          toast.error('Insufficient balance to redeem this reward')
          return
        }

        // Update database
        await redeemReward(user.id, rewardId);
        
        // Create a new transaction record
        await createTransaction(user.id, 'redeemed', reward.cost, `Redeemed ${reward.name}`);

        // Refresh user data and rewards after redemption
        await refreshUserData();

        toast.success(`You have successfully redeemed: ${reward.name}`)
      } catch (error) {
        console.error('Error redeeming reward:', error)
        toast.error('Failed to redeem reward. Please try again.')
      }
    } else {
      toast.error('Insufficient balance or invalid reward cost')
    }
  }

  const handleRedeemAllPoints = async () => {
    if (!user) {
      toast.error('Please log in to redeem points.');
      return;
    }

    if (balance > 0) {
      try {
        // Update database
        await redeemReward(user.id, 0);
        
        // Create a new transaction record
        await createTransaction(user.id, 'redeemed', balance, 'Redeemed all points');

        // Refresh user data and rewards after redemption
        await refreshUserData();

        toast.success(`You have successfully redeemed all your points!`);
      } catch (error) {
        console.error('Error redeeming all points:', error);
        toast.error('Failed to redeem all points. Please try again.');
      }
    } else {
      toast.error('No points available to redeem')
    }
  }

  const refreshUserData = async () => {
    if (user) {
      const fetchedUser = await getUserByEmail(user.email);
      if (fetchedUser) {
        const fetchedTransactions = await getRewardTransactions(fetchedUser.id);
        setTransactions(fetchedTransactions as Transaction[]);
        const fetchedRewards = await getAvailableRewards(fetchedUser.id);
        setRewards(fetchedRewards.filter(r => r.cost > 0)); // Filter out rewards with 0 points
        
        // Recalculate balance
        const calculatedBalance = fetchedTransactions.reduce((acc, transaction) => {
          return transaction.type.startsWith('earned') ? acc + transaction.amount : acc - transaction.amount
        }, 0)
        setBalance(Math.max(calculatedBalance, 0)) // Ensure balance is never negative
      }
    }
  }

  if (loading) {
    return <div className="flex justify-center items-center h-64">
      <Loader className="animate-spin h-8 w-8 text-gray-600" />
    </div>
  }

  return (
    <div className="p-8 max-w-4xl mx-auto">
      <h1 className="text-3xl font-semibold mb-6 text-gray-800">Rewards</h1>
      
      <div className="bg-white p-6 rounded-xl shadow-lg flex flex-col justify-between h-full border-l-4 border-purple-500 mb-8">
        <h2 className="text-xl font-semibold mb-4 text-gray-800">Reward Balance</h2>
        <div className="flex items-center justify-between mt-auto">
          <div className="flex items-center">
            <Coins className="w-10 h-10 mr-3 text-purple-500" />
            <div>
              <span className="text-4xl font-bold text-purple-500">{balance}</span>
              <p className="text-sm text-gray-500">Available Points</p>
            </div>
          </div>
        </div>
      </div>

      <div className="grid md:grid-cols-2 gap-8">
  
[truncated — 3722 more characters]
```

### drizzle.config.js

```javascript
export default {
    dialect: "postgresql",
    schema: "./src/utils/db/schema.ts",
    out: "./drizzle",
    dbCredentials: {
      url: "postgresql://neondb_owner:Nn7IsAgV9tyL@ep-cool-moon-a5r8lx6i.us-east-2.aws.neon.tech/zerotohero?sslmode=require",
      connectionString:
        "postgresql://neondb_owner:Nn7IsAgV9tyL@ep-cool-moon-a5r8lx6i.us-east-2.aws.neon.tech/zerotohero?sslmode=require",
    },
  };
  
```

### src/app/metadata.ts

```typescript
import type { Metadata } from "next"

export const metadata: Metadata = {
  title: "Zero-to-Hero",
  description: "AI-powered waste management platform",
}
```

[23 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]