# Project export: Nexus

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 10.0
- Tagline: Our project is a real-time voice chat app that pairs users intelligently based on their interests.
- Devpost: https://devpost.com/software/nexus-27zakp
- GitHub: https://github.com/Sankalpsp21/Nexus
- Result: winner (Ddoski's Favorite: 1st Overall)
- Team: 4 GitHub contributor(s) — Thomas (21 commits), Sankalp Patil (15 commits), derek-williams00 (8 commits), solderq35 (6 commits)

## Devpost submission (written by the team)

### Inspiration

The inspiration for our project, Nexus, comes from our experience as individuals with unique interests and challenges. Often, it isn't easy to meet others with these interests or who can relate to our challenges through traditional social media platforms. With Nexus, people can effortlessly meet and converse with others who share these common interests and challenges, creating a vibrant community of like-minded individuals. Our aim is to foster meaningful connections and empower our users to explore, engage, and grow together in a space that truly understands and values their uniqueness. What it Does In Nexus, we empower our users to tailor their conversational experience. You have the flexibility to choose how you want to connect with others. Whether you prefer one-on-one interactions for more intimate conversations or want to participate in group discussions, our application Nexus has got you covered. We allow users to either get matched with a single person, fostering deeper connections, or join one of the many voice chats to speak in a group setting, promoting diverse discussions and the opportunity to engage with a broader community. With Nexus, the power to connect is in your hands, and the choice is yours to make.

### How we built it

We built our application using a multitude of services/frameworks/tool: React.js for the core client frontend TypeScript for robust typing and abstraction support Tailwind for a utility-first CSS framework DaisyUI for animations and UI components 100ms live for real-time audio communication Clerk for a seamless and drop-in OAuth provider React-icons for drop-in pixel perfect icons Vite for simplified building and fast dev server Convex for vector search over our database React-router for client-side navigation Convex for real-time server and end-to-end type safety 100ms for real-time audio infrastructure and client SDK MLH for our free .tech domain Challenges We Ran Into Navigating new services and needing to read a lot of documentation -- since this was the first time any of us had used Convex and 100ms, it took a lot of research and heads-down coding to get Nexus working. Being awake to work as a team -- since this hackathon is both in-person and through the weekend, we had many sleepless nights to ensure we can successfully produce Nexus. Working with very poor internet throughout the duration of the hackathon, we estimate it cost us multiple hours of development time.

### Accomplishments we're proud of

Finishing our project and getting it working! We were honestly surprised at our progress this weekend and are super proud of our end product Nexus. Learning a ton of new technologies we would have never come across without Cal Hacks. Being able to code for at times 12-16 hours straight and still be having fun! Integrating 100ms well enough to experience bullet-proof audio communication.

### What we learned

Tools are tools for a reason! Embrace them, learn from them, and utilize them to make your applications better. Sometimes, more sleep is better -- as humans, sleep can sometimes be the basis for our mental ability! How to work together on a team project with many commits and iterate fast on our moving parts.

### What's next

Make Nexus rooms only open at a cadence, ideally twice each day, formalizing the "meeting" aspect for users. Allow users to favorite or persist their favorite matches to possibly re-connect in the future. Create more options for users within rooms to interact with not just their own audio and voice but other users as well. Establishing a more sophisticated and bullet-proof matchmaking service and algorithm. 🚀 Contributors 🚀

## README (from the GitHub repository)

# Nexus, __Empowering Voices, Creating Connections__.

## Inspiration
The inspiration for our project, Nexus, comes from our experience as individuals with unique interests and challenges. Often, it isn't easy to meet others with these interests or who can relate to our challenges through traditional social media platforms. 

With Nexus, people can effortlessly meet and converse with others who share these common interests and challenges, creating a vibrant community of like-minded individuals. 

Our aim is to foster meaningful connections and empower our users to explore, engage, and grow together in a space that truly understands and values their uniqueness. 

## What it Does
In Nexus, we empower our users to tailor their conversational experience. You have the flexibility to choose how you want to connect with others. Whether you prefer one-on-one interactions for more intimate conversations or want to participate in group discussions, our application Nexus has got you covered. 

We allow users to either get matched with a single person, fostering deeper connections, or join one of the many voice chats to speak in a group setting, promoting diverse discussions and the opportunity to engage with a broader community. With Nexus, the power to connect is in your hands, and the choice is yours to make.

## How we built it
We built our application using a multitude of services/frameworks/tool:
- React.js for the core client frontend
- TypeScript for robust typing and abstraction support
- Tailwind for a utility-first CSS framework
- DaisyUI for animations and UI components
- 100ms live for real-time audio communication 
- Clerk for a seamless and drop-in OAuth provider 
- React-icons for drop-in pixel perfect icons 
- Vite for simplified building and fast dev server
- Convex for vector search over our database
- React-router for client-side navigation
- Convex for real-time server and end-to-end type safety
- 100ms for real-time audio infrastructure and client SDK
- MLH for our free .tech domain

## Challenges We Ran Into
- Navigating new services and needing to read **a lot** of documentation -- since this was the first time any of us had used Convex and 100ms, it took a lot of research and heads-down coding to get Nexus working.
- Being __awake__ to work as a team -- since this hackathon is both **in-person** and **through the weekend**, we had many sleepless nights to ensure we can successfully produce Nexus.
- Deprecated/incompatible packages for our authentication manager Clerk in the back end.

## Accomplishments that we're proud of
- Finishing our project and getting it working! We were honestly surprised at our progress this weekend and are super proud of our end product Nexus. 
- Learning a ton of new technologies we would have never come across without Cal Hacks.
- Being able to code for at times 12-16 hours straight and still be having fun!
- Integrating 100ms well enough to experience bullet-proof audio communication.

## What we learned
- Tools are tools for a reason! Embrace them, learn from them, and utilize them to make your applications better.
- Sometimes, more sleep is better -- as humans, sleep can sometimes be the basis for our mental ability!
- How to work together on a team project with many commits and iterate fast on our moving parts.

## What's next for Nexus
- Make Nexus rooms only open at a cadence, ideally twice each day, formalizing the "meeting" aspect for users. 
- Allow users to favorite or persist their favorite matches to possibly re-connect in the future.
- Create more options for users within rooms to interact with not just their own audio and voice but other users as well.
- Establishing a more sophisticated and bullet-proof matchmaking service and algorithm.

<div align="center">
  <h2>🚀 Contributors 🚀</h2>
  <table>
    <tbody>
      <tr>
        <td align="center">
          <a href="https://github.com/solderq35">
            <img src="https://avatars.githubusercontent.com/u/82061589?v=4" width="80"><br>
            Jeff Huang
          </a>
        </td>
        <td align="center">
          <a href="https://github.com/derek-williams00">
            <img src="https://avatars.githubusercontent.com/u/16065188?v=4" width="80"><br>
            Derek Williams
          </a>
        </td>
        <td align="center">
          <a href="https://github.com/Nyumat">
            <img src="https://images.weserv.nl/?url=github.com/Nyumat.png&fit=cover&mask=circle" width="80"><br>
            Tom Nyuma
          </a>
        </td>
        <td align="center">
          <a href="https://github.com/Sankalpsp21">
            <img src="https://images.weserv.nl/?url=github.com/Sankalpsp21.png&fit=cover&mask=circle" width="80"><br>
            Sankalp Patil
          </a>
        </td>
      </tr>
    </tbody>
  </table>
</div>


## Detected evidence (automated analysis)

Indexed codebase: 36 recognized source files, 51 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (45 of 45)

```
core/.eslintrc.cjs
core/.gitignore
core/.prettierrc.json
core/convex/_generated/api.d.ts
core/convex/_generated/api.js
core/convex/_generated/dataModel.d.ts
core/convex/_generated/server.d.ts
core/convex/_generated/server.js
core/convex/README.md
core/convex/schema.ts
core/convex/tsconfig.json
core/convex/users.ts
core/index.html
core/package.json
core/postcss.config.js
core/README.md
core/src/App.tsx
core/src/components/CustomBackground.tsx
core/src/components/Footer.tsx
core/src/components/Header.tsx
core/src/components/HeaderText.tsx
core/src/components/IconButton.tsx
core/src/components/icons/index.tsx
core/src/components/MatchRoom.tsx
core/src/components/modals/SignIn.tsx
core/src/components/NavBar.tsx
core/src/components/Peer.tsx
core/src/components/Room.tsx
core/src/components/RoomCard.tsx
core/src/components/SideBar.tsx
core/src/data/index.json
core/src/data/rooms.json
core/src/index.css
core/src/main.tsx
core/src/pages/content/Landing.tsx
core/src/pages/content/MatchPage.tsx
core/src/pages/content/RoomPage.tsx
core/src/pages/dashboard/index.tsx
core/src/pages/onboarding/index.tsx
core/src/vite-env.d.ts
core/tailwind.config.js
core/tsconfig.json
core/tsconfig.node.json
core/vite.config.ts
README.md
```

### Dependencies

- core/package.json: @100mslive/react-sdk@^0.8.18, @clerk/clerk-react@^4.27.1, @faker-js/faker@^8.2.0, @react-icons/all-files@^4.1.0, @types/node@^20.8.9, @types/react@^18.2.15, @types/react-dom@^18.2.7, @types/react-transition-group@^4.4.8, @typescript-eslint/eslint-plugin@^6.0.0, @typescript-eslint/parser@^6.0.0, @vitejs/plugin-react@^4.0.3, autoprefixer@^10.4.16, convex@^1.5.1, daisyui@^3.9.4, eslint@^8.45.0, eslint-plugin-react-hooks@^4.6.0, eslint-plugin-react-refresh@^0.4.3, lint-staged@^15.0.2, postcss@^8.4.31, prettier@3.0.3, prop-types@^15.8.1, react@^18.2.0, react-dom@^18.2.0, react-icons@^4.11.0, react-loader-spinner@^5.4.5, react-router-dom@^6.17.0, react-transition-group@^4.4.5, tailwindcss@^3.3.5, typescript@^5.0.2, vite@^4.4.5

### Recent commits (newest first)

- Update README.md
- 🕺🚀 Cal Hacks 10.0 Final
- HeaderText
- added match functionality (not finished)
- 😳 enter the nexus
- removed clerk logo
- 🕺🏾 Lobbies and Loaders
- [PR] Rooms restyle 2 (#12)
- 🚀Moved Files To More Appropriate Directories
- ⚡️ Voice Chat Styling
- logo link
- fixes
- 10000 components
- voice room buttons
- AUDIO WORKS, but ui doesnt show...
- fixed room codes
- 👑 Multi-Select
- Merge pull request #9 from Sankalpsp21/100ms
- ⚡️ Dashboard Sidebar
- white space formatting for RoomCard

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

### core/package.json

```
{
  "name": "core",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview",
    "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\""
  },
  "dependencies": {
    "@100mslive/react-sdk": "^0.8.18",
    "@clerk/clerk-react": "^4.27.1",
    "@faker-js/faker": "^8.2.0",
    "@react-icons/all-files": "^4.1.0",
    "convex": "^1.5.1",
    "prop-types": "^15.8.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-icons": "^4.11.0",
    "react-loader-spinner": "^5.4.5",
    "react-router-dom": "^6.17.0",
    "react-transition-group": "^4.4.5"
  },
  "devDependencies": {
    "@types/node": "^20.8.9",
    "@types/react": "^18.2.15",
    "@types/react-dom": "^18.2.7",
    "@types/react-transition-group": "^4.4.8",
    "@typescript-eslint/eslint-plugin": "^6.0.0",
    "@typescript-eslint/parser": "^6.0.0",
    "@vitejs/plugin-react": "^4.0.3",
    "autoprefixer": "^10.4.16",
    "daisyui": "^3.9.4",
    "eslint": "^8.45.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.3",
    "lint-staged": "^15.0.2",
    "postcss": "^8.4.31",
    "prettier": "3.0.3",
    "tailwindcss": "^3.3.5",
    "typescript": "^5.0.2",
    "vite": "^4.4.5"
  }
}

```

### core/src/main.tsx

```typescript
import { ConvexProvider, ConvexReactClient } from 'convex/react';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import CustomBackground from './components/CustomBackground';
import { HMSRoomProvider } from '@100mslive/react-sdk';

import './index.css';

if (!import.meta.env.VITE_CONVEX_URL) {
  throw new Error('Missing Convex Account Key');
}

const convexClient = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <ConvexProvider client={convexClient}>
      <HMSRoomProvider>
        <BrowserRouter>
          <CustomBackground>
            <App />
          </CustomBackground>
        </BrowserRouter>
      </HMSRoomProvider>
    </ConvexProvider>
  </React.StrictMode>
);

```

### core/src/App.tsx

```typescript
import { ClerkProvider } from '@clerk/clerk-react';
import { Route, Routes, useLocation } from 'react-router-dom';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import NavBar from './components/NavBar';
import Landing from './pages/content/Landing';
import RoomPage from './pages/content/RoomPage';
import Dashboard from './pages/dashboard/index';
import { JoinGroups, Lobby, Two } from './pages/onboarding/index';
import MatchPage from './pages/content/MatchPage';

if (!import.meta.env.VITE_CLERK_PUBLISHABLE_KEY) {
  throw new Error('Missing Publishable Key');
}

const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;

function App() {
  return (
    <>
      <ClerkProvider publishableKey={clerkPubKey}>
        <NavBar />
        <Navigation />
      </ClerkProvider>
    </>
  );
}

function OnboardNavigation() {
  const location = useLocation();
  return (
    <>
      <TransitionGroup component={null}>
        <CSSTransition key={location.key} classNames="fade" timeout={300}>
          <Routes location={location}>
            <Route path="/" element={<Landing />} />
            <Route
              path="/1"
              element={
                <>
                  <progress className="progress bg-200 h-3 mb-2 rounded-none" value={30} max={100} />
                  <JoinGroups />
                </>
              }
            />
            <Route
              path="/2"
              element={
                <>
                  <progress className="progress w-screen bg-200 h-3 mb-2 rounded-none" value={70} max={100} />
                  <Two />
                </>
              }
            />
          </Routes>
        </CSSTransition>
      </TransitionGroup>
    </>
  );
}

function Navigation() {
  return (
    <>
      <TransitionGroup component={null}>
        <CSSTransition classNames="fade" timeout={300}>
          <Routes>
            <Route path="/*" element={<OnboardNavigation />} />
            <Route
              path="/dashboard"
              element={
                <>
                  <Dashboard />
                </>
              }
            />
            <Route
              path="/room/:roomCode"
              element={
                <>
                  <RoomPage />
                </>
              }
            />
            <Route
              path="/match/:roomCode"
              element={
                <>
                  <MatchPage />
                </>
              } 
            />
            <Route path="/lobby" element={<Lobby />} />
          </Routes>
        </CSSTransition>
      </TransitionGroup>
    </>
  );
}

export default App;

```

### core/convex/_generated/server.js

```javascript
/* eslint-disable */
/**
 * Generated utilities for implementing server-side Convex query and mutation functions.
 *
 * THIS CODE IS AUTOMATICALLY GENERATED.
 *
 * Generated by convex@1.5.1.
 * To regenerate, run `npx convex dev`.
 * @module
 */

import {
  actionGeneric,
  httpActionGeneric,
  queryGeneric,
  mutationGeneric,
  internalActionGeneric,
  internalMutationGeneric,
  internalQueryGeneric,
} from "convex/server";

/**
 * Define a query in this Convex app's public API.
 *
 * This function will be allowed to read your Convex database and will be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const query = queryGeneric;

/**
 * Define a query that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to read from your Convex database. It will not be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const internalQuery = internalQueryGeneric;

/**
 * Define a mutation in this Convex app's public API.
 *
 * This function will be allowed to modify your Convex database and will be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const mutation = mutationGeneric;

/**
 * Define a mutation that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to modify your Convex database. It will not be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const internalMutation = internalMutationGeneric;

/**
 * Define an action in this Convex app's public API.
 *
 * An action is a function which can execute any JavaScript code, including non-deterministic
 * code and code with side-effects, like calling third-party services.
 * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
 * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
 *
 * @param func - The action. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
 */
export const action = actionGeneric;

/**
 * Define an action that is only accessible from other Convex functions (but not from the client).
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
 */
export const internalAction = internalActionGeneric;

/**
 * Define a Convex HTTP action.
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
 * as its second.
 * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
 */
export const httpAction = httpActionGeneric;

```

### core/src/pages/dashboard/index.tsx

```typescript
import { selectIsConnectedToRoom, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
import { useEffect, useState } from 'react';
import { Vortex } from 'react-loader-spinner';
import { useNavigate } from 'react-router-dom';
import RoomCard from '../../components/RoomCard';
import SideBar from '../../components/SideBar';
import rooms from '../../data/rooms.json';

export default function Dashboard() {
  const isConnected = useHMSStore(selectIsConnectedToRoom);
  const hmsActions = useHMSActions();
  const [isLoading, setLoading] = useState(false); // Add loading state
  const navigate = useNavigate();

  const handleRoomCardClick = (roomCode: string) => {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
      navigate(`/room/${roomCode}`);
    }, 3000);
  };

  useEffect(() => {
    window.onunload = () => {
      if (isConnected) {
        hmsActions.leave();
      }
    };

    return () => {
      window.onunload = null;
    };
  }, [hmsActions, isConnected]);

  if (isLoading) {
    return (
      <div className="flex flex-col justify-center items-center h-1/2 w-screen">
        <h1 className="text-7xl font-semibold text-gray-900">Joining room...</h1>
        <>
          <Vortex
            visible={true}
            height="100"
            width="100"
            ariaLabel="vortex-loading"
            wrapperClass="vortex-wrapper scale-150 my-12"
            colors={['#7E7CFD', '#D2D2FE', '#524FFD', '', '#A6A4FE', '#2522FC']}
          />
        </>
      </div>
    );
  }

  return (
    <div className="relative">
      <div className="flex flex-row">
        <SideBar />
        <div className="grid grid-cols-3 gap-4 p-8 h-full w-full">
          {rooms.map((room) => (
            <RoomCard
              key={room.roomCode}
              userName="hello"
              roomName={room.roomName}
              roomCode={room.roomCode}
              onClick={() => handleRoomCardClick(room.roomCode)}
            />
          ))}
        </div>
      </div>
    </div>
  );
}

```

### core/src/components/icons/index.tsx

```typescript
import {
  FaBinoculars,
  FaBirthdayCake,
  FaBlind,
  FaBookDead,
  FaBookOpen,
  FaBrain,
  FaBriefcase,
  FaCameraRetro,
  FaChild,
  FaDeaf,
  FaGlobeAmericas,
  FaHandHoldingMedical,
  FaHome,
  FaLandmark,
  FaMedal,
  FaPuzzlePiece,
  FaRainbow,
  FaRecycle,
  FaRibbon,
  FaStar,
  FaWheelchair,
} from 'react-icons/fa';

import { GiGamepad, GiPlantRoots, GiSolderingIron } from 'react-icons/gi';

export function ArrowIcon() {
  return (
    <div>
      <p className="text-2xl text-900 italic -translate-x-64 mb-1">It's completely free!</p>
      <img src="/arrow.png" alt="arrow" className="-translate-x-40 rotate-90" />
    </div>
  );
}

export const ICONS = [
  {
    name: 'Mental Health',
    icon: <FaBrain />,
  },
  {
    name: 'Physical Health',
    icon: <FaHandHoldingMedical />,
  },
  {
    name: 'Autism',
    icon: <FaPuzzlePiece />,
  },
  {
    name: 'Dyslexia',
    icon: <FaBookOpen />,
  },
  {
    name: 'Mobility Impairment',
    icon: <FaWheelchair />,
  },
  {
    name: 'Blind and Visually Impaired',
    icon: <FaBlind />,
  },
  {
    name: 'Deaf and Hard of Hearing',
    icon: <FaDeaf />,
  },
  {
    name: 'Cancer Survivors and Fighters',
    icon: <FaRibbon />,
  },
  {
    name: 'Parenting',
    icon: <FaChild />,
  },
  {
    name: 'LGBTQ+',
    icon: <FaRainbow />,
  },
  {
    name: 'Veterans',
    icon: <FaMedal />,
  },
  {
    name: 'Unemployment and Career Transition',
    icon: <FaBriefcase />,
  },
  {
    name: 'Immigrants and Refugees',
    icon: <FaGlobeAmericas />,
  },
  {
    name: 'Baking',
    icon: <FaBirthdayCake />,
  },
  {
    name: 'Gardening',
    icon: <GiPlantRoots />,
  },
  {
    name: 'Astronomy and Star Gazing',
    icon: <FaStar />,
  },
  {
    name: 'Indie Games',
    icon: <GiGamepad />,
  },
  {
    name: 'Videography and Photography',
    icon: <FaCameraRetro />,
  },
  {
    name: 'Environment',
    icon: <FaRecycle />,
  },
  {
    name: 'Science Fiction',
    icon: <FaBookDead />,
  },
  {
    name: 'Houses',
    icon: <FaHome />,
  },
  {
    name: 'Electronics and Tinkering',
    icon: <GiSolderingIron />,
  },
  {
    name: 'Bird Watching',
    icon: <FaBinoculars />,
  },
  {
    name: 'History',
    icon: <FaLandmark />,
  },
];

```

### core/src/pages/onboarding/index.tsx

```typescript
import { useEffect, useState } from 'react';
import { MdNavigateNext } from 'react-icons/md';
import { MagnifyingGlass } from 'react-loader-spinner';
import { useNavigate } from 'react-router-dom';
import IconButton from '../../components/IconButton';
import { ICONS } from '../../components/icons';

export function JoinGroups() {
  const navigate = useNavigate();
  const [selectedButtons, setSelectedButtons] = useState(new Array(ICONS.length).fill(false));

  const handleButtonClick = (index: number) => {
    const updatedSelectedButtons = [...selectedButtons];
    updatedSelectedButtons[index] = !selectedButtons[index];
    setSelectedButtons(updatedSelectedButtons);
  };

  return (
    <>
      <h1 className="text-4xl mx-16 mt-4">Tell us what your interests are</h1>
      <p className="text-xl mx-16 mt-4 italic font-light">This will help us match you with the right groups.</p>
      <div className="flex flex-col items-center justify-center">
        <div className="grid grid-cols-8 gap-8 mx-12 my-12">
          {ICONS.map((icon, index) => (
            <>
              <IconButton
                key={index}
                name={icon.name}
                icon={icon.icon}
                onClick={() => handleButtonClick(index)}
                selected={selectedButtons[index]}
              />
            </>
          ))}
        </div>
        <button className="btn bg-950 hover:bg-300 mb-4" onClick={() => navigate('/2')}>
          <MdNavigateNext className="text-white h-10 w-10" />
        </button>
      </div>
    </>
  );
}

export function Two() {
  const navigate = useNavigate();
  // TODO: If it's not their initial login, we'll need to navigate() to the lobby
  return (
    <>
      <div className="flex flex-col items-center justify-start space-y-4">
        <h1 className="text-4xl my-2">Let us learn a little bit more about you.</h1>
        <p className="text-xl italic font-light">Your response help us match you with the right groups.</p>
        <div className="flex flex-col items-center justify-center space-y-4 p-8">
          <textarea
            className="textarea textarea-bordered w-[150%] h-96 mb-4 bg-white opacity-75"
            placeholder="Who are you? What do you do for fun? Let's get to know each other..."
          ></textarea>
          <button className="btn bg-950 hover:bg-300" onClick={() => navigate('/dashboard')}>
            <span className="text-50">Done</span>
            <MdNavigateNext className="text-white h-10 w-10" />
          </button>
        </div>
      </div>
    </>
  );
}

export function Lobby() {
  const navigate = useNavigate();
  
  useEffect(() => {
    setTimeout(() => {
        navigate('/match/uid-silt-mus');
    }, 3000);
  }, []);

  return (
    <div className="flex flex-col justify-center items-center h-1/2 w-full my-24">
      <h1 className="text-7xl font-semibold text-900 text-center">Finding you a match...</h1>
      <MagnifyingGlass
        visible={true}
        height="100"
        width="100"
        ariaLabel="magnifying-glass-loading"
        wrapperStyle={{}}
        wrapperClass="scale-150 my-12"
        glassColor="#D2D2FE"
        color="#786CFD"
      />

      <div className="my-12 translate-y-3/4">
        <button
          className="btn btn-xs sm:btn-sm md:btn-md lg:btn-lg xl:btn-xl bg-950 hover:bg-300"
          onClick={() => navigate('/dashboard')}
        >
          <span className="text-50">Cancel</span>
        </button>
      </div>
    </div>
  );
}

```

### core/postcss.config.js

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

```

### core/vite.config.ts

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

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

```

### core/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

```

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