# Project export: Slug Club

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 2025
- Tagline: Ever had a hard time looking for the right club at UCSC? Check out our website and find a new community!
- Devpost: https://devpost.com/software/slug-club-hub
- GitHub: https://github.com/arshiabyya/CruzHacks
- Demo: https://github.com/arshiabyya/CruzHacks2025
- Team: 2 GitHub contributor(s) — arshiabyya (9 commits), kamaDe25 (3 commits)

## Devpost submission (written by the team)

### Inspiration

-Our team knows that UCSC offers several enriching clubs. However, it can be difficult to find a club that's suited to a student's schedule and interests. Currently, there is no centralized forum for clubs to post about their meetings–for students, it is difficult to manage Instagram stories or Discord messages that may easily vanish. To help students learn more about what clubs are best for them, we decided to make an online forum where clubs can post their meeting information for the students to access.

### What it does

-Our website has an add post functionality where club officers can post meeting locations, times, events, and event descriptions for students to view in an easily readable tile format. The posts are displayed on the homepage, where students can scroll and view the club meetings. Posts can also be deleted in case club members face any conflicts and meetings have to be cancelled.

### How we built it

-Two of our members followed a tutorial on Geeksforgeeks online so our team could understand how the backend worked. Our other two team members created a UI on figma and used Lovable AI generative software to get some code. Our team members had to make lots of fixed to the AI generated code to get it to work.

### Challenges we ran into

-We had difficulty connecting the backend and frontend because we hadn’t set up MongoDB properly at first. We got help from a mentor and had to download Docker properly for the databases on all of our member's laptops for our website to work properly.

### Accomplishments we're proud of

-For some of us, this was our first time working on a web app and using the MERN stack, so we’re proud of the progress we made during the short time frame we had. We also were able to utilize an AI tool called Lovable to translate our Figma prototype into actual HTML/CSS/JS, which greatly expedited our design process.

### What we learned

-We learned how to use Docker to containerize a MongoDB image, debug connection failures by inspecting the Network tab, use the MERN stack to develop a web app, implement a Figma design into actual code, and most importantly, how to work with a team on GitHub– specifically how to clone, pull, push, and handle merge conflicts.

### What's next

Hub -We would like to fix some bugs in our website, such as the effects of the delete button only being shown after hitting refresh and only being able to show up to 8 events at a time. -We would like to create a search page with a search query and a filter system so users can have an easier time finding the right type of club for them. -We want to allow admin to add images to our “Add an Event page” to make our website look better.

## README (from the GitHub repository)

# 

## Detected evidence (automated analysis)

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

## Codebase structure (from repository index)

### Files (37 of 37)

```
.gitignore
backend/package.json
backend/README.md
backend/server.js
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/App.tsx
frontend/src/components/ClubCard.tsx
frontend/src/components/ClubDetail.js
frontend/src/components/ClubList.js
frontend/src/components/Header.tsx
frontend/src/components/ui/button.js
frontend/src/components/ui/input.js
frontend/src/components/ui/sonner.tsx
frontend/src/components/ui/textarea.js
frontend/src/components/ui/toaster.tsx
frontend/src/components/ui/tooltip.tsx
frontend/src/index.css
frontend/src/index.html
frontend/src/index.js
frontend/src/main.js
frontend/src/package.json
frontend/src/pages/AddEvent.tsx
frontend/src/pages/Index.tsx
frontend/src/pages/NotFound.tsx
frontend/src/postcss.config.js
frontend/src/reportWebVitals.js
frontend/src/setupTests.js
frontend/src/tailwind.config.js
README.md
```

### Dependencies

- backend/package.json: axios@^1.8.4, body-parser@^2.2.0, cors@^2.8.5, dotenv@^16.5.0, express@^5.1.0, mongoose@^8.13.2, react-router-dom@^7.5.0
- frontend/package.json: @testing-library/dom@^10.4.0, @testing-library/jest-dom@^6.6.3, @testing-library/react@^16.3.0, @testing-library/user-event@^13.5.0, axios@^1.8.4, react@^19.1.0, react-dom@^19.1.0, react-router-dom@^7.5.0, react-scripts@5.0.1, react-toastify@^11.0.5, web-vitals@^2.1.4
- frontend/src/package.json: @vitejs/plugin-react@^4.0.0, autoprefixer@^10.4.14, postcss@^8.4.24, react@^18.2.0, react-dom@^18.2.0, tailwindcss@^3.3.2, vite@^4.3.9

### Recent commits (newest first)

- Trying to commit overnight changes Merge branch 'main' of https://github.com/arshiabyya/CruzHacks
- Praying that this is supposed to be Arshia's overnight changes.
- Merge pull request #4 from arshiabyya/arshia
- Added delete function
- Cleaned comments and UI
- Merge pull request #3 from arshiabyya/arshia
- accidentally didnt merge properly, now fixed!
- Merge pull request #2 from arshiabyya/arshia
- Fixed post backend
- Merge pull request #1 from arshiabyya/arshia
- README
- initial commit

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

### backend/package.json

```
{
  "name": "cruzhacks",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "axios": "^1.8.4",
    "body-parser": "^2.2.0",
    "cors": "^2.8.5",
    "dotenv": "^16.5.0",
    "express": "^5.1.0",
    "mongoose": "^8.13.2",
    "react-router-dom": "^7.5.0"
  }
}

```

### frontend/package.json

```
{
  "name": "myapp",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/dom": "^10.4.0",
    "@testing-library/jest-dom": "^6.6.3",
    "@testing-library/react": "^16.3.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.8.4",
    "react": "^19.1.0",
    "react-dom": "^19.1.0",
    "react-router-dom": "^7.5.0",
    "react-scripts": "5.0.1",
    "react-toastify": "^11.0.5",
    "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/package.json

```
{
  "name": "myapp",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.0.0",
    "autoprefixer": "^10.4.14",
    "postcss": "^8.4.24",
    "tailwindcss": "^3.3.2",
    "vite": "^4.3.9"
  }
}

```

### backend/server.js

```javascript
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const bodyParser = require('body-parser');

const app = express();
const PORT = process.env.PORT || 5000;

app.use(cors());
app.use(express.json());
app.use(bodyParser.json());

// MongoDB URI from environment variables
const mongoURI = 'mongodb+srv://User:123@cluster0.uen4yeh.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0';

// Connect to MongoDB Atlas
mongoose.connect(mongoURI, {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

const connection = mongoose.connection;
connection.once('open', () => {
  console.log('MongoDB database connection established successfully');
});

// Club model
const Club = mongoose.models.Club || mongoose.model('Club', {
    title: String,
    topic: String,
    event: String,
    location: String,
    description: String,
  });
  

// Club routes
app.get('/api/clubs', async (req, res) => {
  try {
    const clubs = await Club.find();
    res.json(clubs);
  } catch (error) {
    res.status(500).send('Error fetching clubs');
  }
});

app.get('/api/clubs/:id', async (req, res) => {
  try {
    const club = await Club.findById(req.params.id);
    if (!club) {
      return res.status(404).send('Club not found');
    }
    res.json(club);
  } catch (error) {
    res.status(500).send('Error fetching club');
  }
});

app.post('/api/clubs', async (req, res) => {
    console.log('Received POST /api/clubs:', req.body); // Debug log
  
    try {
      const body = {
        ...req.body,
        title: req.body.club, // Map club to title
        topic: 'General', // Add topic if needed
      };
  
      const newClub = new Club(body);
      await newClub.save();
      res.status(201).json(newClub);
    } catch (err) {
      console.error('Error saving to MongoDB:', err);
      res.status(500).json({ message: 'Server error' });
    }
  });

  app.delete('/api/clubs/:id', async (req, res) => {
  console.log('Received DELETE request for club ID:', req.params.id); // Log the incoming request

  try {
    const clubId = req.params.id;  // Get the club ID from the URL parameters
    console.log('Deleting club with ID:', clubId); // Log to make sure the correct ID is received

    // Attempt to delete the club by its ID
    const deletedClub = await Club.findByIdAndDelete(clubId);  // This is where the deletion happens

    if (!deletedClub) {
      return res.status(404).json({ message: 'Club not found' });  // If the club doesn't exist
    }

    console.log('Club deleted successfully:', deletedClub); // Log successful deletion
    res.status(200).json({ message: 'Club deleted successfully' });  // Send success response
  } catch (error) {
    console.error('Error deleting club:', error);  // Log any errors
    res.status(500).json({ message: 'Error deleting club' });  // Send error response
  }
});

  
  

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

```

### frontend/src/main.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>
);

reportWebVitals();
```

### frontend/src/index.js

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

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

reportWebVitals();

```

### frontend/src/App.tsx

```typescript
import React from "react";
import { Toaster } from './components/ui/toaster';
import { Toaster as Sonner } from "./components/ui/sonner";
import { TooltipProvider } from "./components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Index from "./pages/Index";
import NotFound from "./pages/NotFound";
import AddEvent from "./pages/AddEvent";

const queryClient = new QueryClient();

const App = () => (
  <QueryClientProvider client={queryClient}>
    <TooltipProvider>
      <Toaster />
      <Sonner />
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<Index />} />
          <Route path="/add-event" element={<AddEvent />} />
          {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
          <Route path="*" element={<NotFound />} />
        </Routes>
      </BrowserRouter>
    </TooltipProvider>
  </QueryClientProvider>
);

export default App;
```

### frontend/src/App.js

```javascript
import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import './App.css';

import Header from './components/Header.tsx';
import ClubCard from './components/ClubCard.tsx';
import AddEvent from './pages/AddEvent.tsx';

function App() {
  const [clubs, setClubs] = useState([]);

  useEffect(() => {
    fetch('http://localhost:5000/api/clubs')
      .then((res) => res.json())
      .then((data) => setClubs(data))
      .catch((err) => console.error('Error fetching clubs:', err));
  }, []);

  return (
    <Router>
      <Routes>
        <Route path="/add-event" element={<AddEvent />} />
        <Route
          path="/"
          element={
            <div className="app-container">
              <Header />
              <main className="club-grid">
                {clubs.length > 0 ? (
                  clubs.map((club) => (
                    <ClubCard key={club._id} club={club} />
                  ))
                ) : (
                  <p>Loading clubs...</p>
                )}
              </main>
            </div>
          }
        />
      </Routes>
    </Router>
  );
}

export default App;

```

### frontend/src/pages/Index.tsx

```typescript
import React, { JSX, useEffect, useState } from 'react';
import Header from '../components/Header';
import ClubCard from '../components/ClubCard';

interface Club {
  _id: string;
  title: string;
  topic: string;
  event: string;
  location: string;
  description: string;
}

const Index = (): JSX.Element => {
  const [clubs, setClubs] = useState<Club[]>([]);

  useEffect(() => {
    fetch('http://localhost:5000/api/clubs')
      .then(res => res.json())
      .then(data => {
        console.log(data);
        setClubs(data);
      });
  }, []);

  const handleDelete = async (id: string) => {
    console.log("Sending DELETE request for club ID:", id); // This should log when you click "OK" in the confirmation
    try {
      const res = await fetch(`http://localhost:5000/api/clubs/${id}`, {
        method: 'DELETE',
      });
  
      if (res.ok) {
        console.log("Club deleted successfully");
        setClubs((prev) => prev.filter((club) => club._id !== id)); // Update state to remove the deleted club
      } else {
        const errorText = await res.text();
        console.error('Failed to delete:', errorText);
      }
    } catch (error) {
      console.error('Error deleting club:', error);
    }
  };
  
  
  
  

  return (
    <div className="app-container">
      <Header />
      <main className="club-grid">
        {clubs.length > 0 ? (
          clubs.map((club) => (
            <ClubCard key={club._id} club={club} onDelete={handleDelete} />
          ))
        ) : (
          <p>Loading clubs...</p>
        )}
      </main>
    </div>
  );
};

export default Index;

```

### frontend/src/postcss.config.js

```javascript
module.exports = {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
  };
```

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