# Project export: Scammer Jammer

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: Scammer Jammer is our solution to deter scam callers. Using Hume.ai's empathic voice interface, our app mimics the speech of a real human to annoy and waste scammers' time during real-time calls!
- Devpost: https://devpost.com/software/scammer-jammer
- GitHub: https://github.com/Ohm-Rajpal/scammerjammer
- Demo: https://github.com/ishirgarg/hume_ai_proxy
- Video: https://www.youtube.com/embed/fJOen0E8oxk?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — agamjots (10 commits), Ohm-Rajpal (7 commits)

## Devpost submission (written by the team)

### Inspiration

Our team loves creating interesting projects that defy the norm, and just like Hume.ai's innovative empathetic LLM, we wanted to create something that can leverage its unique power in a context that is both silly but also helpful to society!

### What it does

The idea of the website is to create a solution to the vast number of scam calls that occur within the US. In our case, we have created a website where you initiate a call using a custom phone number provided through Twilio which will initiate the call between you (the scammer) and Hume.ai. Acting as the scammer you can try to convince the EVI to buy you things, but will encounter them as incredibly annoying. This is the point of the product, we wanted to create an incredibly frustrating person to talk to the scammer. We made it so that it would always talk about topics unrelated to the question and overall just waste the other person's time, so that the scammer would have less time to scam more vulnerable victims.

### How we built it

We first began by understanding how Hume.ai works by going to their workshops. Afterwards, we began splitting up the work between front-end and back-end. We took care of the front-end first in Vite and Vercel, using it to guide our product vision and paying attention to the details and features we would want to include. The second half of the hackathon was more focused towards developing the backend, where we leveraged Node.js and Typescript's WebSocket interface to communicate audio data across secure channels.

### Challenges we ran into

The biggest hurdle was to create a custom middleware server that would receive the input from Twilio's audio, transcribe the audio in a format that EVI could respond to, stream EVI's response to our middleware, and then stream the response as back to Twilio. This challenge was very hard, but luckily we had an amazing experience working with the Hume AI team where we received consistent and helpful advice, and we additionally solved some of the technical challenges on our own! At the end, we designed a proxy between Twilio and Hume.ai that could forward audio between the two clients while also displaying the conversation on our custom UI.

### Accomplishments we're proud of

Our biggest accomplishments are our well designed and clean front end, and our backend that has the ability to monitor the data and enable bidirectional communication / monitoring between Hume.ai and Twilio.

### What we learned

Our group deepened our core understanding of API calls, especially in the context of Hume.ai's eLLM model and Twilio API, advanced our knowledge in topics such as proxies and WebSockets, and also expanded our technical framework knowledge such as learning Vite! Another major takeaway our team learned was to value the importance of any technical challenge as seemingly simple ideas are potentially difficult. Initially, our team thought the project idea was relatively straightforward, but eventually, we discovered the true engineering and significant thought process required to tackle our problem. Our group eventually created and designed a scalable architecture that could efficiently transport audio data in multiple forms and still provide low latency so that the conversation would feel natural.

### What's next

We are excited to continue Scammer Jammer by creating an app that can be both offered on iOS or Android devices! Our team strongly believes in the spirit of helping people, and wants to improve our application's effectiveness as a mobile app could intercept real spam and scammers. Our team plans to implement the app using similar techniques as our current website.

## README (from the GitHub repository)

<div align="center">
  <img src="https://storage.googleapis.com/hume-public-logos/hume/hume-banner.png">
</div>

## Scammer Jammer

This is our teams submission for CalHacks 11.0


## Detected evidence (automated analysis)

Indexed codebase: 34 recognized source files, 225 KB.
- CSS (language) — detected in the code
- Firebase (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
- Express (technology) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- Vercel (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (39 of 39)

```
.env.example
.gitignore
app/about/page.tsx
app/auth/login/index.jsx
app/auth/register/index.jsx
app/error.tsx
app/globals.css
app/layout.tsx
app/page.tsx
app/team/experience.tsx
app/team/page.tsx
app/tryusnow/page.tsx
components.json
components/action.ts
components/AngerScoreDisplay.tsx
components/Chat.tsx
components/Controls.tsx
components/Expressions.tsx
components/logos/GitHub.tsx
components/logos/Hume.tsx
components/Messages.tsx
components/MicFFT.tsx
components/Nav.tsx
components/StartCall.tsx
components/ui/button.tsx
components/ui/toggle.tsx
contexts/authContext/index.jsx
firebase/auth.js
firebase/firebase.js
next-env.d.ts
package.json
postcss.config.mjs
README.md
tailwind.config.ts
tsconfig.json
utils/expressionColors.ts
utils/expressionLabels.ts
utils/getHumeAccessToken.ts
utils/index.ts
```

### Dependencies

- package.json: @heroicons/react@^2.1.5, @humeai/voice-react@^0.1.15, @radix-ui/react-slot@^1.0.2, @radix-ui/react-switch@^1.0.3, @radix-ui/react-toggle@^1.0.3, @types/node@^20, @types/react@^18, @types/react-dom@^18, @types/react-virtualized@^9.21.30, class-variance-authority@^0.7.0, clsx@^2.1.1, eslint@^8, eslint-config-next@14.2.3, firebase@^10.14.1, framer-motion@^11.2.10, geist@^1.3.0, heroicons@^2.1.5, hume@^0.8.10, lucide-react@^0.394.0, next@^14.2.15, pnpm@^9.12.2, postcss@^8, react@^18, react-dom@^18, react-virtualized@^9.22.5, remeda@^2.0.9, server-only@^0.0.1, tailwind-merge@^2.3.0, tailwindcss@^3.4.1, tailwindcss-animate@^1.0.7, tailwindcss-animated@^1.1.2, tailwindcss-motion@^0.3.0-beta, ts-pattern@^5.1.2, typescript@^5

### Recent commits (newest first)

- Update README.md
- Update layout.tsx
- Update layout.tsx
- Fixing production
- finished app
- finalized landing and about page
- fixed nav
- fixing merge
- firebase more like firecringe
- fixed up landing
- landing
- auth in progress, we finish it when i wake up im tired
- Merge branch 'main' of https://github.com/Ohm-Rajpal/scammerjammer
- majority finished landing
- Starting login
- fixing production issue
- Intro members, will make it look nicer later
- fixed navbar
- fixed navbar
- fixed structure

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

### package.json

```
{
  "name": "hume-evi-next-js-starter",
  "version": "0.1.0",
  "private": true,
  "homepage": "https://github.com/humeai/hume-evi-next-js-starter",
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@heroicons/react": "^2.1.5",
    "@humeai/voice-react": "^0.1.15",
    "@radix-ui/react-slot": "^1.0.2",
    "@radix-ui/react-switch": "^1.0.3",
    "@radix-ui/react-toggle": "^1.0.3",
    "@types/react-virtualized": "^9.21.30",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "firebase": "^10.14.1",
    "framer-motion": "^11.2.10",
    "geist": "^1.3.0",
    "heroicons": "^2.1.5",
    "hume": "^0.8.10",
    "lucide-react": "^0.394.0",
    "next": "^14.2.15",
    "pnpm": "^9.12.2",
    "react": "^18",
    "react-dom": "^18",
    "react-virtualized": "^9.22.5",
    "remeda": "^2.0.9",
    "server-only": "^0.0.1",
    "tailwind-merge": "^2.3.0",
    "ts-pattern": "^5.1.2"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "eslint": "^8",
    "eslint-config-next": "14.2.3",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "tailwindcss-animate": "^1.0.7",
    "tailwindcss-animated": "^1.1.2",
    "tailwindcss-motion": "^0.3.0-beta",
    "typescript": "^5"
  }
}

```

### utils/index.ts

```typescript
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { GeistSans } from "geist/font/sans";
import { GeistMono } from "geist/font/mono";
import "./globals.css";
import { Nav } from "@/components/Nav";
import { cn } from "@/utils";

export const metadata: Metadata = {
  title: "ScammerJammer",
  description: "Scammer Jammer",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={cn(
          GeistSans.variable,
          GeistMono.variable,
          "flex flex-col min-h-screen"
        )}
      >
        <Nav />
        {children}
      </body>
    </html>
  );
}

```

### app/page.tsx

```typescript
import Link from "next/link";
import Image from "next/image";
import scamimg from "@/assets/scammerimg.png";
import {HelpCircle } from 'lucide-react';

export default function Landing() {
  return (
    <div className="min-h-screen max-w-content-width flex flex-col bg-white [background:radial-gradient(125%_125%_at_50%_50%,#fff_40%,#63e_100%)]">
      <div className="mx-auto max-w-content-width px-4 flex flex-col">
        
        {/* Main Section */}
        <section className="min-h-fit flex flex-col lg:flex-row items-center text-center lg:text-left px-4 pt-40 pb-10 gap-10">
          <div className="flex-1 lg:pl-16 xl:pl-24 pb-10">
            <h1 className="animate-in fade-in-5 duration-1000 ease-in text-6xl font-bold text-gray-800 mb-6">
              Beating Scammers At Their Own Game.
            </h1>
            <p className="animate-in fade-in-5 duration-1000 ease-in text-xl text-gray-600 mb-8">
              Take control of your phone! Waste the scammers' time, not yours. We engage them so you don't have to.
            </p>
            <div className="animate-in fade-in-5 duration-1000 ease-in flex justify-center lg:justify-start gap-4">
              <Link href="/about" className="text-blue-600 border border-blue-600 px-6 py-3 rounded-md hover:bg-blue-600 hover:text-white transition">
                Learn More
              </Link>
              <Link href="/tryusnow" className="text-gray-800 border border-gray-800 px-6 py-3 rounded-md hover:bg-gray-800 hover:text-white transition">
                Try Us Now
              </Link>
            </div>  
          </div>
          <div className="flex-1">
            <Image 
              src={scamimg} 
              alt="Scammer Image" 
              className="rounded-3xl w-full max-w-xs lg:max-w-xl mx-auto"
              priority={true}
            />
          </div>
        </section>

        {/* How It Works Section */}
        <section className="w-full py-10 pb-40">
          <h2 className="text-3xl font-semibold pt-40 pb-10 text-gray-800 text-center mb-8">
            How Does Scammer Jammer Work
          </h2>
          <div className="flex flex-wrap justify-center gap-8">
            <div className="max-w-sm shadow-md rounded-lg p-6 text-center">
              <h3 className="text-xl font-bold text-gray-800 ">Identify Scammers</h3>
              <h3 className="text-xl  text-gray-800 mb-4">(Coming Soon)</h3> 
              <p className="text-gray-600">
                Using Hume AI, we analyze conversations to detect emotional patterns associated with scams. Once verified, we engage with the scammer.
              </p>
            </div>
            <div className="max-w-sm shadow-md rounded-lg p-6 text-center">
              <h3 className="text-xl font-bold text-gray-800 mb-4">Engage and Waste Their Time</h3>
              <p className="text-gray-600">
                After identifying scammers, our AI-driven system keeps them engaged in long conversations, preventing them from targeting others.
              </p>
            </div>
            <div className="max-w-sm shadow-md rounded-lg p-6 text-center">
              <h3 className="text-xl font-bold text-gray-800 mb-4">Comprehensive Protection</h3>
              <p className="text-gray-600">
                We use advanced AI-powered analysis to handle scam calls for you, ensuring minimal interaction while maximizing disruption to scammers.
              </p>
            </div>
          </div>
        </section>

        {/* Idk maybe we can add this later */}
        {/* <section className="w-full py-12 bg-gray-50 rounded-lg">
          <h2 className="text-3xl font-semibold text-gray-800 text-center mb-8">
            What Our Users Say
          </h2>
          <div className="flex flex-wrap justify-center gap-8">
            {[
              { name: "John D.", quote: "Scammer Jammer has saved me countless hours and potential financial losses. It's a game-changer!" },
              { name: "Sarah M.", quote: "I feel so much more secure knowing Scammer Jammer is protecting my phone lines. Highly recommended!" },
              { name: "Robert L.", quote: "The peace of mind this service provides is priceless. No more stress from scam calls!" },
            ].map((testimonial, index) => (
              <div key={index} className="max-w-sm bg-white shadow-md rounded-lg p-6">
                <div className="flex items-center mb-4">
                  <Star className="text-yellow-400 w-5 h-5" />
                  <Star className="text-yellow-400 w-5 h-5" />
                  <Star className="text-yellow-400 w-5 h-5" />
                  <Star className="text-yellow-400 w-5 h-5" />
                  <Star className="text-yellow-400 w-5 h-5" />
                </div>
                <p className="text-gray-600 mb-4">"{testimonial.quote}"</p>
                <p className="text-gray-800 font-semibold">{testimonial.name}</p>
              </div>
            ))}
          </div>
        </section> */}



        <section className="w-full py-12">
          <h2 className="text-3xl font-semibold text-gray-800 text-center mb-8">
            Frequently Asked Questions
          </h2>
          <div className="max-w-3xl mx-auto">
            {[
              { q: "How does Scammer Jammer work?", a: "Scammer Jammer uses Hume AI to engage with potential scammers, wasting their time and preventing them from targeting others." },
              { q: "What is Hume AI?", a: "Hume AI is a powerful artificial intelligence platform designed to understand and analyze human emotions. By interpreting vocal, facial, and textual expressions, Hume AI enables systems to engage in more empathetic and natural conversations." },
              { q: "(Coming Soon) Can I still receive important calls?", a: "Scammer Jammer is designed to only intercept suspected scam calls, allowing legitimate calls to reach you normally." },
              { q: "(Coming Soon) How do I set up Scammer Jammer? ", a: "Se
[truncated — 1036 more characters]
```

### contexts/authContext/index.jsx

```javascript
import { useEffect, useState, useContext } from "react"; 
import { auth } from "../../firebase/firebase";
import { onAuthStateChanged } from "firebase/auth";
import { useContext } from "react";

const AuthContext = React.createContext();

export function useAuth() {
    return useContext(AuthContext);
}

export function AuthProvider({ children }) {

    const [currentUser, setCurrentUser] = useState(null);
    const [userLoggedIn, setUserLoggedIn] = useState(false);
    const [loading, setLoading] = useState(true);
    
    // use effect
    useEffect (() => {
        const unsubscribe = onAuthStateChanged(auth, initializeUser);
        return unsubscribe;
    }, []);
    
    // initialize user
    async function initializeUser(user) {
        if (user) {
            setCurrentUser({ ...user }); // spread arg
            setUserLoggedIn(true);
        } else { // user has logged off
            setCurrentUser(null);
            setUserLoggedIn(false);
        }   
        setLoading(false);
    }

    // expose value obj
    const value = {
        currentUser,
        userLoggedIn,
        loading
    }

    return (
        <AuthContext.Provider value={value}>
            {!loading && children}
        </AuthContext.Provider>
    )
}
```

### app/team/page.tsx

```typescript
import React from 'react';
import Experience from './experience';

const teamMembers = [
    {
      name: 'Agamjot Singh',
      image: "/assets/agamjot.png", // fixed 'string' to 'image'
      profile: 'Full Stack Developer with a passion for backend engineering and system design.',
      github: 'https://github.com/agamjots05',
      skills: ['Full Stack', 'Backend', 'System Design']
    },
    {
      name: 'Jane Smith',
      image: "/assets/ishir.png",
      profile: 'Frontend Developer who loves creating intuitive user interfaces with React and Tailwind.',
      github: 'https://github.com/ishirgarg',
      skills: ['Frontend', 'React', 'Tailwind CSS']
    },
    {
      name: 'Yashi Surapaneni',
      image: "/assets/ishir.png",
      profile: 'ML Engineer with expertise in machine learning and data science.',
      github: 'https://github.com/yashiberk',
      skills: ['ML', 'Data Science', 'Python']
    },
    {
      name: 'Ohm Rajpal',
      image: "/assets/ohm.jpg",
      profile: 'ML Engineer with expertise in machine learning and data science.',
      github: 'https://github.com/yashiberk',
      skills: ['ML', 'Data Science', 'Python']
    },
  ];
  

const OurTeam: React.FC = () => {
  return (
    <div className="text-center text-wrap p-5">
      <h2 className="text-5xl mb-3 text-cyan-300">Meet the team!</h2>

      {/* Flexbox container for horizontal alignment */}
      <div className="flex flex-wrap justify-around items-center">
        {teamMembers.map((member, index) => (
          <Experience
            key={index}
            justifyDir={"justify-center"} 
            imgName={member.image}
            title={member.name}
            description={member.profile}
            skills={member.skills}
            githubLink={member.github}
          />
        ))}
      </div>
    </div>
  );
};

export default OurTeam;

```

### app/tryusnow/page.tsx

```typescript
import { getHumeAccessToken } from "@/utils/getHumeAccessToken";
import dynamic from "next/dynamic";
import { Button } from "@/components/ui/button";
import { ArrowRight, Zap, Shield, Clock } from "lucide-react";

const Chat = dynamic(() => import("@/components/Chat"), {
  ssr: false,
});

export default async function Page() {
  const accessToken = await getHumeAccessToken();

  if (!accessToken) {
    throw new Error();
  }
  // <Button 
  //               className={"z-50 bg-purple-700 flex items-center gap-1.5"}
  //               onClick={handleStartCall}
  //             ></Button>

  return (
    <>

    

    <div className={"bg-white [background:radial-gradient(125%_125%_at_50%_50%,#fff_40%,#63e_100%)] grow flex flex-col"}>
          
    <div className="max-w-6xl mx-auto px-4 pt-5">
        <header className="text-center mb-12">
          <h1 className="text-5xl font-extrabold mb-4 animate-pulse">
            Try Us Now!
          </h1>
          <p className="text-xl text-purple-400">
            Experience our cutting-edge AI chat assistant in real-time
          </p>
        </header>

       

        <div className="bg-white bg-opacity-20 backdrop-blur-lg rounded-lg p-6 shadow-lg mb-12">
          <h2 className="text-2xl font-bold mb-4 flex items-center">
            <Clock className="mr-2" /> How It Works
          </h2>
          <ol className="list-decimal list-inside space-y-2">
            <li>Click the "Start Call" button below to begin your conversation.</li>
            <li>Speak naturally with our AI assistant.</li>
            <li>Our system analyzes your conversation in real-time.</li>
            <li>End the call to see a summary of your interaction.</li>
          </ol>
        </div>

        <div className="bg-purple-800 bg-opacity-50 rounded-lg p-4 mb-8 text-center">
          <p className="text-sm">
            Note: This demo uses voice recognition. Please ensure your microphone is enabled.
          </p>
        </div>

        
      </div>
      <Chat accessToken={accessToken} />
    
      
    </div>
    </>


  );
}

```

### app/about/page.tsx

```typescript
import { ArrowRight, DollarSign, ShieldAlert, Users } from 'lucide-react'

export default function AboutUs() {
  return (
    <div className="min-h-screen max-w-content-width flex flex-col bg-white [background:radial-gradient(125%_125%_at_50%_50%,#fff_40%,#63e_100%)]">
      <div className="container mx-auto px-4 py-16 max-w-4xl">
        <header className=" text-center mb-16">
          <h1 className="text-4xl font-bold mb-4 text-black">Who Are We Fighting Against?</h1>
          <p className="text-xl text-gray-600">Protecting you from financial fraud and scams</p>
        </header>
        <div className="animate-in fade-in-5 duration-1000 ease-in">
        <section className="mb-16">
          <h2 className="text-2xl font-semibold mb-6 text-black">The Growing Threat of Scams</h2>
          <p className="text-gray-700 mb-4">
            Scams have become an increasingly prevalent issue in our digital age, affecting millions of people worldwide. In the United States alone, the Federal Trade Commission (FTC) reported that consumers lost a staggering $10 billion to fraud in 2023 alone, an increase of more than 14% from 2022.
          </p>
          <p className="text-gray-700 mb-4">
            The most common types of scams include imposter scams, online shopping fraud, and investment scams. These fraudulent activities not only cause financial harm but also emotional distress to victims.
          </p>
        </section>

        <section className="grid md:grid-cols-3 gap-8 pb-40">
          {[
            { icon: DollarSign, title: "Financial Impact", description: "$10 billion lost to fraud in 2023" },
            { icon: Users, title: "Victims", description: "More than 56 million fraud reports in 2023" },
            { icon: ShieldAlert, title: "Our Mission", description: "Educate and protect against scams" },
          ].map((item, index) => (
            <div key={index} className="bg-white p-6 rounded-lg shadow-lg transition-all duration-300 hover:shadow-xl hover:-translate-y-1">
              <item.icon className="w-12 h-12 text-purple-600 mb-4" />
              <h3 className="text-xl font-semibold mb-2 text-purple-700">{item.title}</h3>
              <p className="text-gray-600">{item.description}</p>
            </div>
          ))}
        </section>
        </div>

        <section className="text-center">
          <h2 className="text-2xl font-semibold mb-6 text-black">Join Us in the Fight Against Scams</h2>
          <p className="text-gray-700 mb-6">
            Together, we can make a difference. Stay informed, report suspicious activities, and help spread awareness about the dangers of scams.
          </p>
          <a
            href="https://www.businesswire.com/news/home/20240311681922/en/More-than-56-million-U.S.-Adults-%E2%80%93-21-Percent-of-Americans-%E2%80%93-Fleeced-Out-of-25.4-Billion-in-Telephone-Scams-in-2023-per-Truecaller%E2%80%99s-latest-edition-of-the-U.S.-Spam-and-Scam-Report"
            target="_blank" // This opens the link in a new tab
            rel="noopener noreferrer" // Security measure when opening external links
            className="inline-flex items-center px-6 py-3 bg-purple-600 text-white font-semibold rounded-full transition-colors duration-300 hover:bg-purple-700"
            >
            Learn More
            <ArrowRight className="ml-2 w-5 h-5" />
        </a>
        </section>
      </div>
    </div>
  )
}
```

### app/auth/register/index.jsx

```javascript
import React, { useState } from 'react'
import { Navigate, Link, useNavigate } from 'react-router-dom'
import { useAuth } from '../../../contexts/authContext'
import { doCreateUserWithEmailPassword } from '../../../firebase/auth'

const Register = () => {

    const navigate = useNavigate()

    const [email, setEmail] = useState('')
    const [password, setPassword] = useState('')
    const [confirmPassword, setconfirmPassword] = useState('')
    const [isRegistering, setIsRegistering] = useState(false)
    const [errorMessage, setErrorMessage] = useState('')

    const { userLoggedIn } = useAuth()

    const onSubmit = async (e) => {
        e.preventDefault()
        if(!isRegistering) {
            setIsRegistering(true)
            await doCreateUserWithEmailPassword(email, password)
        }
    }

    return (
        <>
            {userLoggedIn && (<Navigate to={'/home'} replace={true} />)}

            <main className="w-full h-screen flex self-center place-content-center place-items-center">
                <div className="w-96 text-gray-600 space-y-5 p-4 shadow-xl border rounded-xl">
                    <div className="text-center mb-6">
                        <div className="mt-2">
                            <h3 className="text-gray-800 text-xl font-semibold sm:text-2xl">Create a New Account</h3>
                        </div>

                    </div>
                    <form
                        onSubmit={onSubmit}
                        className="space-y-4"
                    >
                        <div>
                            <label className="text-sm text-gray-600 font-bold">
                                Email
                            </label>
                            <input
                                type="email"
                                autoComplete='email'
                                required
                                value={email} onChange={(e) => { setEmail(e.target.value) }}
                                className="w-full mt-2 px-3 py-2 text-gray-500 bg-transparent outline-none border focus:indigo-600 shadow-sm rounded-lg transition duration-300"
                            />
                        </div>

                        <div>
                            <label className="text-sm text-gray-600 font-bold">
                                Password
                            </label>
                            <input
                                disabled={isRegistering}
                                type="password"
                                autoComplete='new-password'
                                required
                                value={password} onChange={(e) => { setPassword(e.target.value) }}
                                className="w-full mt-2 px-3 py-2 text-gray-500 bg-transparent outline-none border focus:border-indigo-600 shadow-sm rounded-lg transition duration-300"
                            />
                        </div>

                        <div>
                            <label className="text-sm text-gray-600 font-bold">
                                Confirm Password
                            </label>
                            <input
                                disabled={isRegistering}
                                type="password"
                                autoComplete='off'
                                required
                                value={confirmPassword} onChange={(e) => { setconfirmPassword(e.target.value) }}
                                className="w-full mt-2 px-3 py-2 text-gray-500 bg-transparent outline-none border focus:border-indigo-600 shadow-sm rounded-lg transition duration-300"
                            />
                        </div>

                        {errorMessage && (
                            <span className='text-red-600 font-bold'>{errorMessage}</span>
                        )}

                        <button
                            type="submit"
                            disabled={isRegistering}
                            className={`w-full px-4 py-2 text-white font-medium rounded-lg ${isRegistering ? 'bg-gray-300 cursor-not-allowed' : 'bg-indigo-600 hover:bg-indigo-700 hover:shadow-xl transition duration-300'}`}
                        >
                            {isRegistering ? 'Signing Up...' : 'Sign Up'}
                        </button>
                        <div className="text-sm text-center">
                            Already have an account? {'   '}
                            <Link to={'/login'} className="text-center text-sm hover:underline font-bold">Continue</Link>
                        </div>
                    </form>
                </div>
            </main>
        </>
    )
}

export default Register
```

### app/auth/login/index.jsx

```javascript
import React, { useState } from "react";
import { Navigate, Link } from "react-router-dom";
import { doSignInWithGoogle, doSignInWithEmailPassword } from "../../../firebase/auth";
import { useAuth } from "../../../contexts/authContext";

const Login = () => {
  const { userLoggedIn } = useAuth();

  const [email, setEmail] = useState(''); // Fix the typo here from setEamil
  const [password, setPassword] = useState('');
  const [isSigningIn, setIsSigningIn] = useState(false);
  const [errorMessage, setErrorMessage] = useState('');

  const onSubmit = async (e) => {
    e.preventDefault();
    if (!isSigningIn) {
      setIsSigningIn(true);
      try {
        await doSignInWithEmailPassword(email, password);
      } catch (error) {
        setErrorMessage(error.message); // Set the error message if login fails
        setIsSigningIn(false); // Allow reattempt
      }
    }
  };

  const onGoogleSignIn = async (e) => {
    e.preventDefault();
    if (!isSigningIn) {
      setIsSigningIn(true);
      try {
        await doSignInWithGoogle();
      } catch (error) {
        setErrorMessage(error.message); // Set error if Google sign-in fails
        setIsSigningIn(false); // Allow reattempt
      }
    }
  };

  // Check if user is already logged in and redirect to home if true
  return (
    <div>
      {userLoggedIn && <Navigate to="/" replace={true} />}

      <main className="w-full h-screen flex self-center place-content-center place-items-center">
        <div className="w-96 text-gray-600 space-y-5 p-4 shadow-xl border rounded-xl">
          <div className="text-center">
            <div className="mt-2">
              <h3 className="text-gray-800 text-xl font-semibold sm:text-2xl">Welcome Back</h3>
            </div>
          </div>

          <form onSubmit={onSubmit} className="space-y-5">
            <div>
              <label className="text-sm text-gray-600 font-bold">Email</label>
              <input
                type="email"
                autoComplete="email"
                required
                value={email}
                onChange={(e) => setEmail(e.target.value)} // Fix typo here
                className="w-full mt-2 px-3 py-2 text-gray-500 bg-transparent outline-none border focus:border-indigo-600 shadow-sm rounded-lg transition duration-300"
              />
            </div>

            <div>
              <label className="text-sm text-gray-600 font-bold">Password</label>
              <input
                type="password"
                autoComplete="current-password"
                required
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                className="w-full mt-2 px-3 py-2 text-gray-500 bg-transparent outline-none border focus:border-indigo-600 shadow-sm rounded-lg transition duration-300"
              />
            </div>

            {errorMessage && (
              <span className="text-red-600 font-bold">{errorMessage}</span>
            )}

            <button
              type="submit"
              disabled={isSigningIn}
              className={`w-full px-4 py-2 text-white font-medium rounded-lg ${isSigningIn ? 'bg-gray-300 cursor-not-allowed' : 'bg-indigo-600 hover:bg-indigo-700 hover:shadow-xl transition duration-300'}`}
            >
              {isSigningIn ? 'Signing In...' : 'Sign In'}
            </button>
          </form>

          <p className="text-center text-sm">
            Don't have an account? <Link to="/register" className="hover:underline font-bold">Sign up</Link>
          </p>

          <div className="flex flex-row text-center w-full">
            <div className="border-b-2 mb-2.5 mr-2 w-full"></div>
            <div className="text-sm font-bold w-fit">OR</div>
            <div className="border-b-2 mb-2.5 ml-2 w-full"></div>
          </div>

          <button
            disabled={isSigningIn}
            onClick={onGoogleSignIn}
            className={`w-full flex items-center justify-center gap-x-3 py-2.5 border rounded-lg text-sm font-medium ${isSigningIn ? 'cursor-not-allowed' : 'hover:bg-gray-100 transition duration-300 active:bg-gray-100'}`}
          >
            <svg className="w-5 h-5" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
              {/* Google SVG */}
              <g clipPath="url(#clip0_17_40)">
                <path d="M47.532 24.5528C47.532 22.9214 47.3997 21.2811 47.1175 19.6761H24.48V28.9181H37.4434C36.9055 31.8988 35.177 34.5356 32.6461 36.2111V42.2078H40.3801C44.9217 38.0278 47.532 31.8547 47.532 24.5528Z" fill="#4285F4" />
                <path d="M24.48 48.0016C30.9529 48.0016 36.4116 45.8764 40.3888 42.2078L32.6549 36.2111C30.5031 37.675 27.7252 38.5039 24.4888 38.5039C18.2275 38.5039 12.9187 34.2798 11.0139 28.6006H3.03296V34.7825C7.10718 42.8868 15.4056 48.0016 24.48 48.0016Z" fill="#34A853" />
                <path d="M11.0051 28.6006C9.99973 25.6199 9.99973 22.3922 11.0051 19.4115V13.2296H3.03298C-0.371021 20.0112 -0.371021 28.0009 3.03298 34.7825L11.0051 28.6006Z" fill="#FBBC04" />
                <path d="M24.48 9.49932C27.9016 9.44641 31.2086 10.7339 33.6866 13.0973L40.5387 6.24523C36.2 2.17101 30.4414 -0.068932 24.48 0.00161733C15.4055 0.00161733 7.10718 5.11644 3.03296 13.2296L11.005 19.4115C12.901 13.7235 18.2187 9.49932 24.48 9.49932Z" fill="#EA4335" />
              </g>
              <defs>
                <clipPath id="clip0_17_40">
                  <rect width="48" height="48" fill="white" />
                </clipPath>
              </defs>
            </svg>
            {isSigningIn ? 'Signing In...' : 'Continue with Google'}
          </button>
        </div>
      </main>
    </div>
  );
};

export default Login;

```

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