# Project export: Tutori.ai

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: Find and provide tutoring anytime, anywhere. Tutori.ai is the perfect place to find last minute tutoring in any subject and find students to tutor.
- Devpost: https://devpost.com/software/tutori-ai
- GitHub: https://github.com/dr4Nx/cal-hacks2024/
- Team: 4 GitHub contributor(s) — Evan Luo (43 commits), frawgmanman (26 commits), Felix (19 commits), praneel-c (7 commits)

## Devpost submission (written by the team)

### Inspiration

Tutori.ai stems from the lack of worldwide 24/7 academic support. We believe everyone is deserving of academic support anytime, anywhere. With Tutori.ai, students and tutors can directly connect in a streamlined platform, eliminating all intermediaries, such as being referred by a friend of a friend. We envision that with Tutori.ai, no matter your background, location, or education level, you can find the help you need.

### What it does

Tutori.AI is a service that streamlines connecting tutors and students for on-demand tutoring. Students can make tutoring requests, and tutors can filter through requests based on their expertise. Once you sign up, you have to option to request tutoring for your academic needs and give back as a tutor; there are no limits to what you can be! While Tutor.ai's primary goal is to connect tutors and students, it also fosters worldwide connections as your tutor/student can be from anywhere across the globe.

### How we built it

React.js for core frontend React-router for client navigation Vite for quick frontend building TailwindCSS for styling and simple transitions Animxyz and React Fast Marquee for fancy transitions and animations Firebase for rapid backend development

### Challenges we ran into

We initially tried using Flask with Firebase, but it took us a while to realize we could call Firebase directly without Flask We did not build with Next.js, so a lot of online UI components were not available as it was not worth rebuilding our entire project using Next.js by the time we were adding animations. However, we were able to find libraries that worked (shout out to React Fast Marquee and Animxyz!)

### Accomplishments we're proud of

We have a finished product even though we are all first-time hackers (our front-end developer learned React the night before) We learned a lot about Firebase, React, TailwindCSS, and Git versioning control amidst collaboration - working on a time forced us to learn a lot in a short amount of time

### What we learned

When using React, build with Next.js from the get go Sleep helps! Losing a few hours to sleep actually improves productivity

### What's next

Creating a points based system where tutors earn points for every tutoring session. These points can be traded in for prizes, offering an incentive for tutors to tutor and keeping our platform free for all. Add AI to suggest relevant requests to tutors Rating system for tutors and students so users can see how reputable they are

## README (from the GitHub repository)

# Tutori.AL

Frontend: ReactJS w/ Vite, TailwindCSS
Backend: Firebase (Auth, Firestore)


## Detected evidence (automated analysis)

Indexed codebase: 28 recognized source files, 64 KB.
- CSS (language) — detected in the code
- Firebase (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code

## Codebase structure (from repository index)

### Files (39 of 39)

```
.DS_Store
.gitignore
.vscode/launch.json
back-end/main.py
front-end/.firebaserc
front-end/.gitignore
front-end/eslint.config.js
front-end/firebase.json
front-end/firestore.indexes.json
front-end/firestore.rules
front-end/functions/.eslintrc.js
front-end/functions/.gitignore
front-end/functions/index.js
front-end/functions/package.json
front-end/index.html
front-end/package.json
front-end/postcss.config.js
front-end/README.md
front-end/src/App.css
front-end/src/App.jsx
front-end/src/components/Navbar/NavBar.jsx
front-end/src/components/Navbar/NavbarElements.jsx
front-end/src/components/recyclerlistview/requestCard.jsx
front-end/src/components/recyclerlistview/requestRecyclerview.jsx
front-end/src/components/Spinner.jsx
front-end/src/index.css
front-end/src/main.jsx
front-end/src/pages/ask.jsx
front-end/src/pages/index.jsx
front-end/src/pages/login.jsx
front-end/src/pages/LoginForm.jsx
front-end/src/pages/logout.jsx
front-end/src/pages/profile.jsx
front-end/src/pages/provide.jsx
front-end/src/pages/RegisterForm.jsx
front-end/src/pages/request.jsx
front-end/tailwind.config.js
front-end/vite.config.js
README.md
```

### Dependencies

- front-end/functions/package.json: eslint@^8.15.0, eslint-config-google@^0.14.0, firebase-admin@^12.6.0, firebase-functions@^6.0.1, firebase-functions-test@^3.1.0
- front-end/package.json: @animxyz/core@^0.6.6, @animxyz/react@^0.6.7, @eslint/js@^9.11.1, @mui/base@^5.0.0-beta.59, @types/react@^18.3.10, @types/react-dom@^18.3.0, @vitejs/plugin-react@^4.3.2, autoprefixer@^10.4.20, eslint@^9.11.1, eslint-plugin-react@^7.37.0, eslint-plugin-react-hooks@^5.1.0-rc.0, eslint-plugin-react-refresh@^0.4.12, firebase@^10.14.1, firebase-admin@^12.6.0, framer-motion@^11.11.9, globals@^15.9.0, mailtrap@^3.4.0, postcss@^8.4.47, react@^18.3.1, react-dom@^18.3.1, react-fast-marquee@^1.6.5, react-icons@^5.3.0, react-router-dom@^6.27.0, react-spinners@^0.14.1, react-toastify@^10.0.6, tailwindcss@^3.4.14, toastify@^2.0.1, vite@^5.4.8

### Recent commits (newest first)

- Update README.md
- Create README.md
- correct extra space
- more styling changes
- fix ask page
- styling changes
- name correction
- search stuff
- search stuff
- whale size
- Merge branch 'main' of https://github.com/dr4Nx/cal-hacks2024
- yap
- Update NavBar.jsx
- all transisions
- hehejkahdksjf;lksaj home page
- Merge branch 'main' of https://github.com/dr4Nx/cal-hacks2024
- heehadfs
- more functionality
- add completing a request/seeing email
- Merge pull request #11 from dr4Nx/front-end

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

### front-end/package.json

```
{
  "name": "front-end",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "@animxyz/core": "^0.6.6",
    "@animxyz/react": "^0.6.7",
    "@mui/base": "^5.0.0-beta.59",
    "firebase": "^10.14.1",
    "firebase-admin": "^12.6.0",
    "framer-motion": "^11.11.9",
    "mailtrap": "^3.4.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-fast-marquee": "^1.6.5",
    "react-icons": "^5.3.0",
    "react-router-dom": "^6.27.0",
    "react-spinners": "^0.14.1",
    "react-toastify": "^10.0.6",
    "toastify": "^2.0.1"
  },
  "devDependencies": {
    "@eslint/js": "^9.11.1",
    "@types/react": "^18.3.10",
    "@types/react-dom": "^18.3.0",
    "@vitejs/plugin-react": "^4.3.2",
    "autoprefixer": "^10.4.20",
    "eslint": "^9.11.1",
    "eslint-plugin-react": "^7.37.0",
    "eslint-plugin-react-hooks": "^5.1.0-rc.0",
    "eslint-plugin-react-refresh": "^0.4.12",
    "globals": "^15.9.0",
    "postcss": "^8.4.47",
    "tailwindcss": "^3.4.14",
    "vite": "^5.4.8"
  }
}

```

### front-end/functions/package.json

```
{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "lint": "eslint .",
    "serve": "firebase emulators:start --only functions",
    "shell": "firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "18"
  },
  "main": "index.js",
  "dependencies": {
    "firebase-admin": "^12.6.0",
    "firebase-functions": "^6.0.1"
  },
  "devDependencies": {
    "eslint": "^8.15.0",
    "eslint-config-google": "^0.14.0",
    "firebase-functions-test": "^3.1.0"
  },
  "private": true
}

```

### back-end/main.py

```python
import firebase_admin
from firebase_admin import credentials, auth
from flask import Flask, request, jsonify

cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred)

app = Flask(__name__)

@app.route("/")
def standard():
    return "This is a test response"

@app.route('/verify-token', methods=['POST'])
def verify_token():
    id_token = request.json.get('idToken')
    try:
        decoded_token = auth.verify_id_token(id_token)
        uid = decoded_token['uid']
        return jsonify({'message': 'Token is valid', 'uid': uid}), 200
    except Exception as e:
        return jsonify({'error': str(e)}), 401

```

### front-end/src/main.jsx

```javascript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './index.css'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

```

### front-end/functions/index.js

```javascript
/**
 * Import function triggers from their respective submodules:
 *
 * const {onCall} = require("firebase-functions/v2/https");
 * const {onDocumentWritten} = require("firebase-functions/v2/firestore");
 *
 * See a full list of supported triggers at https://firebase.google.com/docs/functions
 */

const {onRequest} = require("firebase-functions/v2/https");
const {logger} = require("firebase-functions/logger");

// Create and deploy your first functions
// https://firebase.google.com/docs/functions/get-started

// exports.helloWorld = onRequest((request, response) => {
//   logger.info("Hello logs!", {structuredData: true});
//   response.send("Hello from Firebase!");
// });

```

### front-end/src/App.jsx

```javascript
import Navbar from "./components/Navbar/NavBar.jsx";
import{
  BrowserRouter as Router,
  Routes,
  Route
} from "react-router-dom";
import Home from "./pages/index.jsx"
import Login from "./pages/login.jsx"
import LogOut from "./pages/logout.jsx"
import Ask from "./pages/ask.jsx"
import Provide from "./pages/provide.jsx"
import Profile from "./pages/profile.jsx"
import Request from "./pages/request.jsx"
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
// TODO: Don't hardcode this, use environment variables
import "react-toastify/dist/ReactToastify.css";
import { ToastContainer, toast } from "react-toastify";
import { useState } from "react";
import { getAuth, onAuthStateChanged } from "firebase/auth";
import { useEffect } from "react";
import "@animxyz/core";
import { XyzTransition } from "@animxyz/react";



export default function App() {
  const firebaseConfig = {
    apiKey: `${import.meta.env.VITE_API_KEY}`,
    authDomain: `${import.meta.env.VITE_AUTH_DOMAIN}`,
    projectId: `${import.meta.env.VITE_PROJECT_ID}`,
    storageBucket: `${import.meta.env.VITE_STORAGE_BUCKET}`,
    messagingSenderId: `${import.meta.env.VITE_MESSAGING_SENDER_ID}`,
    appId: `${import.meta.env.VITE_APP_ID}`,
    measurementId: `${import.meta.env.VITE_MEASUREMENT_ID}`
  };

  initializeApp(firebaseConfig);


  const [loggedIn, setLoggedIn] = useState(false);
  const [userCredential, setUserCredential] = useState(null);
  

  useEffect(() => {
    const auth = getAuth();
    const storedCredential = localStorage.getItem('firebaseCredential');
    
    if (storedCredential) {
      setUserCredential(JSON.parse(storedCredential));
      setLoggedIn(true);
    }

    onAuthStateChanged(auth, (user) => {
      if (user) {
        const credential = {
          uid: user.uid,
          email: user.email,
        };
        localStorage.setItem('firebaseCredential', JSON.stringify(credential));
        setUserCredential(credential);
        setLoggedIn(true);
      } else {
        localStorage.removeItem('firebaseCredential');
        setUserCredential(null);
        setLoggedIn(false);
      }
    });
  }, []);


  
  return (
    <>
    <Router>
      <Navbar loggedIn={loggedIn} />
      <Routes>
        <Route path="/" element={<Home loggedIn={loggedIn} />}/>
        <Route path='/profile' element={<Profile />}/>
        <Route path="/ask" element={<Ask />}/>
        <Route path="/provide" element={<Provide />}/>
        <Route path="/login" element={<Login />}/>
        <Route path="/request/:id" element={<Request />}/>
        <Route path="/logout" element={<LogOut setLoggedIn={setLoggedIn} />}/>
      </Routes>
      <ToastContainer />

    </Router>
      
    </>
  )
}

```

### front-end/src/pages/index.jsx

```javascript
import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'
import Marquee from "react-fast-marquee";
import "@animxyz/core";
import { XyzTransition } from "@animxyz/react";
// React Server Components


const Home = ({ loggedIn }) => {
    const [scrollPosition, setScrollPosition] = useState(0);
    const handleScroll = () => {
        setScrollPosition(window.scrollY);
        console.log(window.scrollY);
    }

    useEffect(() => {
        // Add scroll event listener when the component mounts
        window.addEventListener('scroll', handleScroll);
    
        // Clean up the event listener when the component unmounts
        return () => {
          window.removeEventListener('scroll', handleScroll);
        };
      }, []);
    return (
        <div onScroll={handleScroll} className="scrollable-element">
            <XyzTransition appear xyz="fade down duration-10">
                <div className='w-full h-[70dvh] triangle'> {/*for the titlest page*/}
                    <div className='text-center mt-[350px]'>

                        <h1 className="text-[5rem] font-bold font-poppins">
                            Tutoring.
                            <br />
                            Anytime, Anywhere
                        </h1>

                        <br />
                        <p className='font-figtree text-5xl mb-5'>Your path to learning starts here.</p>
                        <br />
                        <Link to={loggedIn ? "/profile" : "/login"} className="transition ease-in-out delay-150 bg-sage hover:bg-darksage rounded font-figtree my-5 p-4 text-white">Get Started</Link>
                    </div>
                </div>
            </XyzTransition>
            <div className="bg-lightsage w-full h-[10dvh]"></div>
            <div className="text-[80px] font-bold italic font-figtree">
            <Marquee>
                    BE AT THE FRONTLINES OF EDUCATION FOR ALL.
            </Marquee>
            </div>
            <div className='bg-lightsage flex overflow-hidden min-h-[80dvh]'>{/*for the about section*/}
                <XyzTransition appear xyz="fade down duration-10">
                {window.scrollY > 600 && <div className='drop-shadow-sm bg-white font-figtree float-left text-[32px] overflow-hidden rounded-md tracking-wide min-h-fit ml-10 my-[10%] min-w-[600px] w-5/12 p-10'>
                        <p>We believe that education should be accessible, flexible, and empowering for everyone. Our platform connects learners with experienced tutors from around the world, anytime and anywhere. Whether you're looking for help in mastering a challenging subject, preparing for exams, or exploring new skills, we have the perfect tutor for you.
 </p>
                    </div> }
                </XyzTransition>
                <XyzTransition appear xyz="fade down duration-10">
                    {window.scrollY > 600 && <div className="float-right  text-center w-[50%]">
                        {/*Fix 24/7 academic support heading not wrapping and going below the textbox :(*/}
                        <h2 className='font-bold font-poppins text-wrap text-[60px] mt-[40%]'>24/7 Academic Support</h2>
                        <br />
                        <Link to={loggedIn ? "/profile" : "/login"} className="transition ease-in-out delay-150 bg-sage hover:bg-darksage rounded font-figtree my-3 p-4 text-white">Get Started</Link>
                    </div>}
                </XyzTransition>
            </div>
            <div className="reverse-triangle">
            <div className="w-full pl-10 py-[20%] min-h-[100dvh]" >
                <XyzTransition appear xyz="fade left stagger duration-8">
                    {window.scrollY > 1400 && <div>
                        <h2 className="font-bold text-[42px] font-poppins ">Help others, and win</h2>
                    </div>}
                </XyzTransition>
                <XyzTransition appear xyz="fade left stagger duration-9">
                    {window.scrollY > 1550 && <div>
                        <p className="font-figtree text-[28px] mr-80">Did you know that personally leaning a subject only makes up 60% of truly comprehending a subject? Well, it's true, and the last 40% of learning comes from successfully teaching someone else the subject. Here at Tutori.ai, you can unlock your last 40% of understanding by becoming a tutor anytime, anywhere.</p>
                    </div>}
                </XyzTransition>
                <br></br>
                <Link to={loggedIn ? "/profile" : "/login"} className="transition ease-in-out delay-150 bg-sage hover:bg-darksage rounded font-figtree my-3 p-4 text-white">Become a tutor</Link>
                {/*change to /prizes once you make it*/}
            </div>
            
            </div>
            <div className="bg-white w-full h-[10dvh]"></div>
            </div>
    );
};

export default Home;
```

### front-end/postcss.config.js

```javascript
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

```

### front-end/vite.config.js

```javascript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
})

```

### front-end/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image" href="/public/whale1.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&display=swap"
      rel="stylesheet"
    />
    <link
      href="https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@animxyz/core">
    <title>Tutori.ai</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

```

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