# Project export: Slug Board

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: CruzHacks 2024
- Tagline: 😎 Centralized digital bulletin board for all campus events. Ready to view anywhere!
- Devpost: https://devpost.com/software/slug-board
- GitHub: https://github.com/EH67/cruzhacks_2024
- Demo: https://www.figma.com/proto/zAiVcFD0rHB3nDeWqDoxa8/CruzHack%3A-Event-Board?type=design&node-id=55-353&t=wW3bI658YEtUfgj6-1&scaling=scale-down-width&page-id=0%3A1&starting-point-node-id=55%3A353&mode=design
- Team: 1 GitHub contributor(s) — Emily Ho (16 commits)

## Devpost submission (written by the team)

### Overview

Embark on a journey with Slug Board as we aim to revolutionize campus life at UCSC! College is a kaleidoscope of experiences, from forming lifelong friendships to discovering new passions. However, the dynamic campus environment often means missing out on valuable events. In response, we're building a centralized, open-sourced web application that facilitates easy communication and group sourcing. Say goodbye to FOMO and embrace a platform where the UCSC community can share, connect, and discover hidden gems together. Key Features 🔑 Discover the essence of Slug Board, a web application designed to bring accessibility to the forefront. With a centralized bulletin board, seamless flyers to organization navigation, and effortless event sharing, we're simplifying your campus experience. No need to follow numerous social media accounts—Slug Board lets you find events that match your interests without the clutter. Empower yourself by uploading your own events and spreading awareness within the vibrant UCSC community. Functionality ⚙️ Say hello to a digital bulletin board that serves as the heartbeat of club and orgs events on campus. Slug Board provides clubs with a user-friendly platform to effortlessly showcase their events, fostering better visibility and engagement among students. Technological Stack 🛠️ Our technological journey includes: React.js for frontend development Firebase for backend support Python APIs for additional features Bootstrap for a responsive sleek user interface Figma for planning, design, and prototyping Challenges Faced 🏋️‍♂️ While building the platform, we encountered challenges of various experience backgrounds, exploring React.js intricacies. Issues related to states, hooks, image upload, asynchronous JavaScript rendering, responsive design, and web scraping were all daunting hurdles yet we overcame them. We transformed obstacles into opportunities for learning and growth. Accomplishments 🏆 Our team's dedication shone through as we successfully implemented image upload functionality, created a distinctive banana slug logo, and wrote APIs for seamless website integration. Beyond technical triumphs, our all-female, Taiwanese heritage team takes pride in contributing to the community that has brought us immense joy. What's Next for Slug Board 🔮 As we look ahead, Slug Board envisions an administrative system to tackle spam effectively and plans to introduce a hover feature for event details. Our commitment to community collaboration remains unwavering, propelling us toward a future of enhanced inclusivity and accessibility. In essence, Slug Board isn't just an app—it's a movement towards a more connected and informed campus life. Join us as we continue to evolve, contribute, and make a lasting impact on the UCSC community.

## README (from the GitHub repository)

# Cruzhacks 2024

## Cloning the Repository
`git clone git@github.com:EH67/cruzhacks_2024.git`



## Detected evidence (automated analysis)

Indexed codebase: 32 recognized source files, 43 KB.
- CSS (language) — 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
- Firebase (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (38 of 38)

```
backend/chloe.py
backend/clubs.json
backend/main.py
backend/test.py
backend/utils.py
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/CheckBox/CheckBox.js
frontend/src/components/DropDown/DropDown.js
frontend/src/components/HomeButton/HomeButton.js
frontend/src/components/ImageComponent/ImageComponent.js
frontend/src/components/ImageUpload/ImageUpload.js
frontend/src/index.css
frontend/src/index.js
frontend/src/reportWebVitals.js
frontend/src/sample.json
frontend/src/setupTests.js
frontend/src/views/BoardPage/BoardPage.css
frontend/src/views/BoardPage/BoardPage.js
frontend/src/views/BoardPage/Flyer/Flyer.js
frontend/src/views/ClubPage/ClubPage.css
frontend/src/views/ClubPage/ClubPage.js
frontend/src/views/EventPage/copy.js
frontend/src/views/EventPage/Event/Event.css
frontend/src/views/EventPage/Event/Event.js
frontend/src/views/EventPage/EventPage.css
frontend/src/views/EventPage/EventPage.js
README.md
TestComponent/TestComponent_commented.js
TestComponent/TestComponent.css
TestComponent/TestComponent.js
```

### Dependencies

- 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.5, react@^18.2.0, react-dom@^18.2.0, react-router-dom@^6.21.3, react-scripts@5.0.1, web-vitals@^2.1.4

### Recent commits (newest first)

- final submission (for now)
- push
- more changes
- almost done
- good progress
- moving files to local made it SO MUCH FASTER
- push
- push
- random commit
- progress
- random
- image FINALLY shows up, form is working, pushing before we delete event and newevent
- some progress
- before we lose it
- it works
- initial commit

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

### frontend/package.json

```
{
  "name": "frontend-p2",
  "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.5",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.21.3",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "set DISABLE_ESLINT_PLUGIN=true && 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"
    ]
  }
}

```

### backend/main.py

```python
from flask import Flask, jsonify, request, send_file
from google.cloud import storage
import io
from flask_cors import CORS

app = Flask(__name__)
CORS(app) #enables CORS for all routes

import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
import os

# Use the application default credentials.
cred = credentials.ApplicationDefault()

# initialize firebase app
firebase_admin.initialize_app(cred)
db = firestore.client()

# get bucket name from env vars
bucket_name = os.environ.get('BUCKET_NAME')
if not bucket_name:
    print("ERROR: BUCKET NAME NOT DEFINED")
    exit(1)

def upload_bytes_to_gcs (bucket, blob, bytes_to_upload):
    '''
    Helper function for uploading bytes (image file bytes for this project) to Google Cloud Storage
    '''

    # Initialize a client
    client = storage.Client()

    # Get the bucket
    bucket = client.get_bucket(bucket_name)

    # Create a blob (object) in the bucket
    blob = bucket.blob(blob)

    # Upload the bytes to the blob
    res = blob.upload_from_string(bytes_to_upload, content_type="image/png")

    print(f"response is {res}")
    return res

def get_gcs_blob(bucket_name, blob_name):
    """
    Get a Google Cloud Storage blob.

    Args:
    - bucket_name (str): The name of the GCS bucket.
    - blob_name (str): The name of the blob (object) within the bucket.

    Returns:
    - google.cloud.storage.blob.Blob: The GCS blob object or None if not found.
    """
    # Initialize a client
    client = storage.Client()

    # Get the bucket
    bucket = client.get_bucket(bucket_name)

    # Get the blob (object) within the bucket
    blob = bucket.blob(blob_name)

    # Check if the blob exists
    if not blob.exists():
        print(f"Blob '{blob_name}' not found in bucket '{bucket_name}'.")
        return None

    return blob

def get_image_bytes(bucket_name, blob_name):
    blob = get_gcs_blob(bucket_name, blob_name)
    if not blob:
        return None
    
    return blob.download_as_bytes()

def create_event_to_firebase (collection_name, document_name, json_to_dump):
    doc_ref = db.collection(collection_name).document(document_name)
    res = doc_ref.set(json_to_dump)
    print(res)

def get_all_document_names(collection_name):
    # Reference to the collection
    collection_ref = db.collection(collection_name)

    # Get all documents in the collection
    documents = collection_ref.get()

    # Extract document names
    document_names = [doc.id for doc in documents]

    return document_names

def get_all_documents(collection_name):
    # Reference to the collection
    collection_ref = db.collection(collection_name)

    # Get all documents in the collection
    documents = collection_ref.get()

    # Extract document names
    # document_names = [doc.id for doc in documents]e\
    documents_json = {x.id: x.to_dict() for x in documents}
    return documents_json


def get_firestore_document(collection_name, document_name):
    # Initialize Firestore client
    db = firestore.Client()

    # Construct the reference to the document
    doc_ref = db.collection(collection_name).document(document_name)

    # Get the document snapshot
    doc_snapshot = doc_ref.get()

    # Check if the document exists
    if doc_snapshot.exists:
        # Return the document data
        return doc_snapshot.to_dict()
    else:
        print(f"Document '{document_name}' not found in collection '{collection_name}'.")
        return None

@app.route('/')
def hello_world():
    return jsonify(message='Hello, World!')

@app.route('/UploadEventJson/<uuid>', methods=['POST'])
def upload_event_json_post(uuid):
    try:
        data = request.get_json(force=True)
        
        res_json = {} # make a new json to write to db to get rid of junk from data

        # if tags is not in json, add 1
        if 'tags' not in data:
            res_json['tags'] = []
        else:
            res_json['tags'] = data['tags']

        # add the rest of the columns if they don't exist yet
        columns = ['club_or_affiliation', 'eventname', 'date', 'time', 'flyer', 'social_link', 'location', 'type', 'filename']
        for col in columns:
            if col not in data:
                res_json[col] = None
            else:
                res_json[col] = data[col]

        # assemble flyer link
        # if 'filename' not in data:
        #     flyer = f'gs://{bucket_name}/{uuid}.png'
        # else:
        #     flyer = f'gs://{bucket_name}/{data["filename"]}'
        flyer = f'gs://{bucket_name}/{uuid}.png'
        res_json['flyer'] = flyer

        # TODO upload to firebase (uuid as the document)
        create_event_to_firebase('events', uuid, res_json)

        # return at the very least the uuid (see if that's posisble to get)
        return data, 200
    except Exception as e:
        print(f"Exception in /UploadEventJson: {e}")
        return {"exception": str(e)}, 400
    

@app.route('/FetchImage/<uuid>', methods=['GET'])
def fetch_image_json_get(uuid):
    img_bytes = get_image_bytes(bucket_name, f'{uuid}.png')
    # res = send_file(img_bytes, mimetype='image/png')
    # print(res)
    # return res, 200
    # Return the image bytes as a file response
    return send_file(
        io.BytesIO(img_bytes),
        mimetype='image/png',  # Adjust mimetype as per your image type
        as_attachment=True,
        download_name=f'{uuid.split("/")[-1]}.png'  # Use the last part of the blob_name as the download name
    )

@app.route('/UploadImage/<uuid>', methods=['POST'])
def upload_event_image_post(uuid):
    # get image bytes
    bytes_to_send = request.data

    # assemble blob name
    blob_name = f'{uuid}.png'

    # upload file to GCS
    upload_bytes_to_gcs(bucket_name, blob_name, bytes_to_send)

    return {}, 200

@
[truncated — 3464 more characters]
```

### 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 React from "react";
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import EventPage from './views/EventPage/EventPage';
import ClubPage from './views/ClubPage/ClubPage';
import BoardPage from './views/BoardPage/BoardPage';
function App() {
  return (
    <div className="App">

      {/** Everything outside the BrowserRouter tag will be rendered on every single page (ex: nav bar and footer)*/}
      <BrowserRouter>
        <Routes>
          {/** For each path we have, add a Route tag to define which element to render depending on the path. */}
          <Route path="/" element={ <EventPage/> }></Route>
          <Route path="/ClubPage" element={ <ClubPage/> }></Route>
          <Route path="/EventPage" element={ <EventPage/> }></Route>
          <Route path="/BoardPage" element={ <BoardPage/> }></Route>
        </Routes>
      </BrowserRouter>

    </div>
  );
}

export default App;

```

### TestComponent/TestComponent.js

```javascript
import React from "react";
import PropTypes from "prop-types"; 
import './TestComponent.css'; // replace "TestComponent" with your own component

export default class TestComponent extends React.Component { // replace "TestCOmponent" with your own component
  static propTypes = { // define any props here
    name: PropTypes.string,
  };

  render() {
    return (
    <div className="cards">
      Component renders correctly! (Delete this once you start working)
      {/** start your html here! (delete this whole line, including the {}) */}
    </div>
    );
  }
}
```

### backend/chloe.py

```python
# script for dumping all the club info into firebase (web scraped by chloe!)

import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
import os
import json

# Use the application default credentials.
cred = credentials.ApplicationDefault()

# initialize firebase app
firebase_admin.initialize_app(cred)
db = firestore.client()

# get bucket name from env vars
bucket_name = os.environ.get('BUCKET_NAME')
if not bucket_name:
    print("ERROR: BUCKET NAME NOT DEFINED")
    exit(1)


    
def create_club_to_firebase (collection_name, document_name, json_to_dump):
    doc_ref = db.collection(collection_name).document(document_name)
    res = doc_ref.set(json_to_dump)
    print(res)

# access the file
with open("clubs.json", "r") as f:
    data = json.load(f)
for name, info in data.items():
    print(name)
    create_club_to_firebase('clubs', name, {'insta': info})
'''sample: create_club_to_firebase('clubs', 'Taiwanese Student Association', {'insta': '@ucsc_tsa'})'''
```

### TestComponent/TestComponent_commented.js

```javascript
// This is the commented version of TestComponent.js. This won't be needed inside your component folder

import React from "react";
import PropTypes from "prop-types"; 
import './TestComponent.css'; // replace "TestComponent" with your own component

export default class TestComponent extends React.Component { // replace "TestCOmponent" with your own component
  // This portion defines all the types that the prop variables will be
  // props variables are essentially data you can pass from parent components to the child component (here)
  // Information on Props: https://www.w3schools.com/react/react_class.asp
  // Information on propTypes: https://www.npmjs.com/package/prop-types
  static propTypes = {
    name: PropTypes.string,
  };

  
  // This is where you'll be coding the HTML. It goes inside the return()
  // Everything enclosed in {} will be treated as javascript
  render() {
    return (
    <div className="cards">
      TestComponent Component renders correctly! (Delete this once you start working)
      {/** start your html here! (delete this whole line, including the {}) */}
    </div>
    );
  }
}
```

### backend/test.py

```python
import requests
import uuid

url = 'http://localhost:8080'

def test_upload_event():
    event_uuid = '12345uuid'
    with open('graph.png', 'rb') as infile:
        # upload the json
        req_url = f'{url}/UploadEventJson/{event_uuid}'
        req_json = {
            "club_or_affiliation": "TSA",
            "eventname": "Boba making",
            "date": "1/19/2024",
            "time": "15:00",
            "flyer": "gs://BUCKET/graph.png",
            "social_link": "insta/discord",
            "location": "C9 Community Room",
            "tags": ["tag 1", "tag2"]
        }
        res = requests.post(req_url, json=req_json)
        print(res.text)

        # upload the image
        req_url = f'{url}/UploadImage/{event_uuid}'
        # res = requests.post(req_url, files={'file': infile})
        res = requests.post(req_url, data=infile)
        print(res.text)

def test_query_event():
    req_url = f'{url}/QueryEvent'
    req_json = {
        'club_or_affiliation': ['Taiwanese Student Association (TSA)', "African Caribbean People's Coalition", 'abc']
    }
    res = requests.post(req_url, json=req_json)
    return res

api_call = test_query_event()
print(f'response is {api_call.text}')
```

### 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();
});

```

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