# Project export: ClubHub

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: TreeHacks 2024
- Tagline: A one-stop shop for Stanford students to easily discover and join campus communities and events anytime, all year long.
- Devpost: https://devpost.com/software/clubhub-98zmpo
- GitHub: https://github.com/UnknownEclipse/treehacks-2024.git
- Team: 3 GitHub contributor(s) — Eclipse842 (16 commits), bkamm (4 commits), Natalia Linn (1 commits)

## Devpost submission (written by the team)

### Inspiration

Stanford organizes two club fairs each academic year, providing a primary but limited avenue for students to explore new clubs and communities on campus. Beyond these events, opportunities for discovering and engaging with campus organizations are scarce. Given the evolving nature of student interests and schedules in college, a more consistent and accessible source of information of active groups would benefit both students are large and club leaders, proving opportunities to make new connections, learn new skills, and grow club enagement, by attracting new members.

### What it does

ClubHub is a personal portal for discovering and joining social groups on campus. It’s key features are the directory, inbox, and group profile pages. Directory - A filterable database of groups on-campus. Clubs are tagged with “Interests” (relevant keywords that describe their programming), “Meeting Day” (weekday of their most consistent meetings), and “Commitment Level” (expected engagement on a scale from I-III). The filters assist users in searching for clubs that align with their interests, schedules, and involvement preference. Also included is a button to join each club, which takes you to the club’s website. Inbox - A dedicated destination for newsletters and club announcements. The goal is to separate club/event newsletters to prevent clutter and distraction from personal and professional emails in students’ main inboxes. In a student’s ClubHub portal, they can manage all group and event related announcements on their Inbox page with the ability to reply, archive, and delete old messages. Group Profiles - Each group has a profile that includes their logo, tags, pictures, and a bio. The Build Backend: API - Rust, Framework - Axum Frontend: React.js, Next.js, CSS, HTML, TypeScript Design: Figma Mockup

### Challenges we ran into

All three of us came in without any experience building a website One of our teammate’s laptop’s screen repeatedly crashed at the beginning of the hackathon Learning how to deal with async API calls in React components Translating complex Figma designs to CSS (originally we tried to use Chakra FigPilot, but we encountered bugs with the plug-in, so we went the manual route instead) Building an ideal data model that will allow for extensibility in the future

### What's next

Even though we didn’t get to implement everything we had planned for, we’re passionate about the idea and have high hopes for the continued development of the project. Here are a few of our next steps: Include more groups Implement profiles for each group Include more filters (time of day, group size, etc.) and capabilities (filter by intersection) Implement the inbox Incorporate subscribable mailing lists Allow users to sign-in and follow groups and/or subscribe to mailing lists Create a campus events page with RSVP functionality

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 35 recognized source files, 46 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
- Rust (language) — detected in the code
- SQL (language) — detected in the code
- TypeScript (language) — detected in the code
- Next.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (45 of 45)

```
.DS_Store
backend/.DS_Store
backend/.gitignore
backend/Cargo.lock
backend/Cargo.toml
backend/client.ts
backend/doc/doc.ts
backend/sql/example.sql
backend/sql/schema.sql
backend/src/connection.rs
backend/src/example.rs
backend/src/handlers.rs
backend/src/handlers/group_search.rs
backend/src/handlers/group.rs
backend/src/handlers/groups_all.rs
backend/src/handlers/mark_as_read.rs
backend/src/handlers/tags_all.rs
backend/src/handlers/user_inbox.rs
backend/src/main.rs
backend/src/types.rs
frontend/.gitignore
frontend/package.json
frontend/public/index.html
frontend/public/manifest.json
frontend/public/robots.txt
frontend/README.md
frontend/src/App.css
frontend/src/App.js
frontend/src/App.test.js
frontend/src/components/Directory.jsx
frontend/src/components/DropDown.jsx
frontend/src/components/GroupsTable.jsx
frontend/src/components/Home.jsx
frontend/src/components/Inbox.jsx
frontend/src/components/TopBar.jsx
frontend/src/css/directory.css
frontend/src/css/dropdown.css
frontend/src/css/groupstable.css
frontend/src/css/inbox.css
frontend/src/css/topbar.css
frontend/src/index.css
frontend/src/index.js
frontend/src/reportWebVitals.js
frontend/src/setupTests.js
LICENSE
```

### Dependencies

- backend/Cargo.toml: axum@0.7.4, chrono@0.4.34, color-eyre@0.6.2, fallible-iterator@0.3.0, once_cell@1.19.0, rusqlite@0.31.0, serde@1.0.196, tokio@1.36.0, tower-http@0.5.1, tracing@0.1.40, tracing-subscriber@0.3.18
- frontend/package.json: @testing-library/jest-dom@^5.17.0, @testing-library/react@^13.4.0, @testing-library/user-event@^13.5.0, axios@^1.6.7, react@^18.2.0, react-dom@^18.2.0, react-router-dom@^6.22.1, react-scripts@5.0.1, web-vitals@^2.1.4

### Recent commits (newest first)

- completely rewrite ui using raw html/css
- Many API changes to work with frontend work
- Fix search
- Backend updates
- Add descriptions
- Fix group search again
- Fix CORS
- Add CORS, add group fields, update /groups/search endpoint
- tag data added
- Finished API helpers.
- Created directory page and first API helper function.
- Add groupid, streamline some interfaces
- Change default port to 3001
- Add `GET /tags/all`
- Add fields to groups table
- Set up Next.js frontend with Chakra UI and Tailwind CSS
- Add more endpoints
- Add group search support
- Improvements and new API calls
- Add `commitment` and `interests` fields

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

### backend/Cargo.toml

```
[package]
name = "backend"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = { version = "0.7.4", features = ["json"] }
chrono = { version = "0.4.34", features = ["serde"] }
color-eyre = "0.6.2"
fallible-iterator = "0.3.0"
once_cell = "1.19.0"
rusqlite = { version = "0.31.0", features = ["chrono"] }
serde = { version = "1.0.196", features = ["derive"] }
tokio = { version = "1.36.0", features = ["full"] }
tower-http = { version = "0.5.1", features = ["cors"] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }

```

### frontend/package.json

```
{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.6.7",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.22.1",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

```

### frontend/src/index.js

```javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

```

### frontend/src/App.js

```javascript
import "./App.css";
import Home from "./components/Home";
import Directory from "./components/Directory";
import Inbox from "./components/Inbox";
import { Routes, Route, BrowserRouter } from "react-router-dom";

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/directory" element={<Directory />} />
          <Route path="/inbox" element={<Inbox />} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;

```

### backend/src/main.rs

```rust
use std::{
    fmt::Debug,
    net::{Ipv4Addr, SocketAddrV4},
};

use axum::http::StatusCode;
use color_eyre::Result;
use tokio::{fs, net::TcpListener};
use tracing::{error, info, instrument};
use tracing_subscriber::util::SubscriberInitExt;

use crate::{connection::connect, handlers::router};

mod connection;
mod example;
mod handlers;
mod types;

#[tokio::main]
async fn main() -> Result<()> {
    main2().await
}

async fn main2() -> Result<()> {
    tracing_subscriber::fmt().pretty().finish().init();

    _ = fs::remove_file("treehacks.db").await;

    info!("start");

    setup().await?;
    example::example_setup().await?;

    let port = 3001;
    let addr = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port);
    let listener = TcpListener::bind(addr).await?;

    info!("listening on {addr}");

    axum::serve(listener, router()).await?;
    Ok(())
}

#[instrument]
async fn setup() -> color_eyre::Result<()> {
    let sql = fs::read_to_string("sql/schema.sql").await?;
    let conn = connect().await?;
    conn.pragma_update(None, "FOREIGN_KEY", "ON")?;
    conn.execute_batch(&sql)?;
    Ok(())
}

trait IntoStatusCode {
    type Value;

    fn into_status_code(self) -> Result<Self::Value, StatusCode>;
}

impl<T, E> IntoStatusCode for Result<T, E>
where
    E: Debug,
{
    type Value = T;

    fn into_status_code(self) -> Result<Self::Value, StatusCode> {
        match self {
            Ok(v) => Ok(v),
            Err(err) => {
                error!("error occurred {err:?}");
                Err(StatusCode::INTERNAL_SERVER_ERROR)
            }
        }
    }
}

```

### backend/client.ts

```typescript
interface Group {
  id: number;
  name: string;
  email: string | null;
  url: string | null;
  description: string | null;
  profile_photo_url: string | null;
  tags: string[];
}

let response = await fetch("http://localhost:3001/groups/all");

let body = await response.arrayBuffer();
let body_str = new TextDecoder("utf-8").decode(body);
let value: Group[] = JSON.parse(body_str);

console.log(value);

```

### frontend/src/setupTests.js

```javascript
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

```

### frontend/src/App.test.js

```javascript
import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

```

### frontend/src/reportWebVitals.js

```javascript
const reportWebVitals = onPerfEntry => {
  if (onPerfEntry && onPerfEntry instanceof Function) {
    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
      getCLS(onPerfEntry);
      getFID(onPerfEntry);
      getFCP(onPerfEntry);
      getLCP(onPerfEntry);
      getTTFB(onPerfEntry);
    });
  }
};

export default reportWebVitals;

```

### frontend/src/index.css

```css
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

```

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