# Project export: SlugHug

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: SlugHug is a way for anyone to anonymously send a fleeting thought out into the world and receive a memorable reply back, whether they want to vent about their day or just send good vibes.
- Devpost: https://devpost.com/software/slughug
- GitHub: https://github.com/marcus-leung/SlugHug
- Demo: https://slughug.us/
- Result: winner (Health Hacks; [Sponsor - Axure])
- Team: 4 GitHub contributor(s) — Marcus Leung (43 commits), Aniket Tyagi (31 commits), PangYuanbo (15 commits), Konan Le (7 commits)

## Devpost submission (written by the team)

### Inspiration

When we were drafting ideas for CH 2024, we wanted to create something that we would use ourselves. We went through a several ideas pertaining to our local communities, but the idea that stuck out the most to us: we called it SlugHug. It was inspired by the video game "Kind Words", and our distaste for modern-day doomscrolling. We saw the potential for that concept of fleeting, ephemeral messages to be more than just a game - we saw it as the potential to be an entire platform. Somewhere were people would not only using it to relax and for fun, but also the potential for ease of access to legitimate professional health, and potentially more.

### What it does

Users can compose messages (slugs) and write whatever they want - troll messages will get flagged by a sentiment analysis AI and sent for review. This person's thoughts are distributed to other users, who will be able to read and respond once only (sending a hug), promoting meaningful, therapeutic writing and conversation, almost like a journal where you know that someone out there would understand you. Once you read a response to your slug, it is gone forever - only the memory of it remains.

### How we built it

We began by using Axure RP 10 to prototype the website flow and design. It helped us iron out some kinks with the ideas we had, such as helping us reduce the amount of pages we had planned down to a one-page responsive design. We used the MERN tech stack to develop this.

### Challenges we ran into

We ran into a few different problems. Learning an entirely new tech stack, in particular the middleware, was difficult. Balancing time, merge conflicts, documentation, tech stack compatibility, MacOS vs Windows compatibility and cross-platform development were all things we had to consider when building this.

### Accomplishments we're proud of

The overall design and flow of the website. There are bubbly cute characters. The fact that several of us took the plunge and learned something completely new - Auth0, UI, Graphic Design, Databases, React...

### What we learned

On top of learning the MERN tech stack, we learned the importance of documentation and making sure the things we developed were easily used by others around us.

### What's next

In the middle of making our prototypes, we realized that the concept of SlugHug could easily be extended to more than just therapeutic purposes. The nature of our design means it doesn't have to be bound by any specific label. It can expand do other communities such as students and teachers, hobbyists, concert-goers, and more.

## README (from the GitHub repository)

# SlugHug
Cruzhacks 2024

SlugHug is a way for anyone to anonymously send a fleeting thought out into the world and receive a memorable reply back, whether they want to vent about their day or just send good vibes. 

Domain: [slughug.us](https://slughug.us/)

Collaborators: Marcus Leung, Aniket Tyagi, Konan Le, Yuanbo (Aaron) Pang

## Inspiration
When we were drafting ideas for CH 2024, we wanted to create something that we would use ourselves. We went through a several ideas pertaining to our local communities, but the idea that stuck out the most to us: we called it SlugHug. It was inspired by the video game "Kind Words", and our distaste for modern-day doomscrolling. We saw the potential for that concept of fleeting, ephemeral messages to be more than just a game - we saw it as the potential to be an entire platform. Somewhere were people would not only using it to relax and for fun, but also the potential for ease of access to legitimate professional health, and potentially more.

## What it does
Users can compose messages (slugs) and write whatever they want - troll messages will get flagged by a sentiment analysis AI and sent for review. This person's thoughts are distributed to other users, who will be able to read and respond once only (sending a hug), promoting meaningful, therapeutic writing and conversation, almost like a journal where you know that someone out there would understand you. Once you read a response to your slug, it is gone forever - only the memory of it remains.

## How we built it
We began by using Axure RP 10 to prototype the website flow and design. It helped us iron out some kinks with the ideas we had, such as helping us reduce the amount of pages we had planned down to a one-page responsive design. We used the MERN tech stack to develop this.

## Challenges we ran into
We ran into a few different problems. Learning an entirely new tech stack, in particular the middleware, was difficult. Balancing time, merge conflicts, documentation, tech stack compatibility, MacOS vs Windows compatibility and cross-platform development were all things we had to consider when building this.

## Accomplishments that we're proud of
The overall design and flow of the website. There are bubbly cute characters. The fact that several of us took the plunge and learned something completely new - Auth0, UI, Graphic Design, Databases, React...

## What we learned
On top of learning the MERN tech stack, we learned the importance of documentation and making sure the things we developed were easily used by others around us.

## What's next for SlugHug
In the middle of making our Axure prototypes, we realized that the concept of SlugHug could easily be extended to more than just therapeutic purposes. Communities. The nature of our design means it doesn't have to be bound by any specific label. It can expand do other communities such as students and teachers, hobbyists, concert-goers, and more.

## Misc

Setup:     
run "npm install @auth0/auth0-react" in slughub (client) directory

Running:      
run "npm start" in server directory         
run "npm start" in slughug directory        

Deploying:       
"npm run build"      








## Detected evidence (automated analysis)

Indexed codebase: 26 recognized source files, 44 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
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (31 of 31)

```
README.md
server/config.env
server/db/conn.js
server/models/Users.js
server/package.json
server/routes/inbox.js
server/routes/message.js
server/routes/user.js
server/server.js
slughug/.gitignore
slughug/package.json
slughug/public/index.html
slughug/public/manifest.json
slughug/src/App.css
slughug/src/App.js
slughug/src/App.test.js
slughug/src/components/Account.jsx
slughug/src/components/AuthenticationSection.jsx
slughug/src/components/Grid.jsx
slughug/src/components/LoginButton.jsx
slughug/src/components/Logout.jsx
slughug/src/components/Main.jsx
slughug/src/components/Read.jsx
slughug/src/components/Reply.jsx
slughug/src/components/Slug.jsx
slughug/src/components/Write.jsx
slughug/src/index.css
slughug/src/index.js
slughug/src/reportWebVitals.js
slughug/src/setupTests.js
slughug/tailwind.config.js
```

### Dependencies

- server/package.json: axios@^1.6.5, axois@^0.0.1-security, body-parser@^1.20.2, cors@^2.8.5, dotenv@^16.3.2, express@^4.18.2, kill-port@^2.0.1, mongodb@^6.3.0, mongoose@^8.1.0, multer@^1.4.5-lts.1, react@^18.2.0
- slughug/package.json: @auth0/auth0-react@^2.2.4, @testing-library/jest-dom@^5.17.0, @testing-library/react@^13.4.0, @testing-library/user-event@^13.5.0, react@^18.2.0, react-dom@^18.2.0, react-scripts@^5.0.1, tailwindcss@^3.4.1, web-vitals@^2.1.4

### Recent commits (newest first)

- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Connecting to sentiment API
- Final push, fixed response type
- Add new slugs and response slugs
- Changes, added inbox pulls, some backend changes
- Send requests work
- Update message for db implementation
- Adding sentiment analysis API
- update UI
- Merge branch 'main' of https://github.com/marcus-leung/SlugHug
- changed a portion of the background
- changed the user http requests
- Fixed sliding slugwindow
- merge complete
- refining UI elements

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

### server/package.json

```
{
  "name": "server",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^1.6.5",
    "axois": "^0.0.1-security",
    "body-parser": "^1.20.2",
    "cors": "^2.8.5",
    "dotenv": "^16.3.2",
    "express": "^4.18.2",
    "kill-port": "^2.0.1",
    "mongodb": "^6.3.0",
    "mongoose": "^8.1.0",
    "multer": "^1.4.5-lts.1",
    "react": "^18.2.0"
  },
  "description": ""
}

```

### slughug/package.json

```
{
  "name": "slughug",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@auth0/auth0-react": "^2.2.4",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "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"
    ]
  },
  "devDependencies": {
    "tailwindcss": "^3.4.1"
  }
}

```

### server/server.js

```javascript
const express = require("express");
const app = express();
const cors = require("cors");
require("dotenv").config({ path: "./config.env" });
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());

// Request routing and handleing
app.use(require("./routes/user"));
app.use(require("./routes/message"))
app.use(require("./routes/inbox"))

// get driver connection
const dbo = require("./db/conn");

app.listen(port, async () => {
  // perform a database connection when server starts
  await dbo.connectToServer(function (err) {
    if (err) console.error(err);
  });
  console.log(`Server is running on port: ${port}`);

  /* successful CRUD database push
  let myobj = {
    name: "test_guy",
    age: 89,
    gpa: 3.2,
  };
  dbo.getDb().collection("users").insertOne(myobj, function (err, res) {
    if (err) throw err;
    response.json(res);
  });
  */
});

// Create a user
async function createUser(data) {
  try {
    const response = await fetch('http://localhost:5000/users/add', {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data)
    })

    const result = await response.json();
    console.log("Success:", result)
  } catch (err) {
    console.error(err)
  }
}

// Create a message
async function createMessage(data) {
  try {
    const response = await fetch('http://localhost:5000/messages/add', {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data)
    })

    const result = await response.json();
    console.log("Success:", result)
  } catch (err) {
    console.error(err)
  }
}

// Create a response to a given userID, it's in the data object
async function createResponse(data) {
  try {
    const response = await fetch('http://localhost:5000/inbox/add', {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data)
    })

    const result = await response.json();
    console.log("Success:", result)
  } catch (err) {
    console.error(err)
  }
}

// Get the messages all
async function getMessages() {
  try {
    const response = await fetch('http://localhost:5000/messages')
    const result = await response.json();

    console.log("Success:", result)
  } catch (err) {
    console.error(err)
  }
}

// Get an inbox by inbox receiver
async function getInbox(id) {
  try {
    const response = await fetch('http://localhost:5000/inbox/get/'+id)
    const result = await response.json();

    console.log("Success:", result)
  } catch (err) {
    console.error(err)
  }
}
module.exports ={
    createUser,
    createMessage,
    createResponse,
    getMessages
}

// Delete the message by message id
async function deleteMessage(id) {
  try {
    const response = await fetch('http://localhost:5000/messages/delete/'+id, {
      method: "DELETE",
      headers: {
        "Content-Type": "application/json",
      }
    })
    const result = await response.json();

    console.log("Success:", result)
  } catch (err) {
    console.error(err)
  }
}

// Delete an inbox by inbox id
async function deleteInbox(id) {
  try {
    const response = await fetch('http://localhost:5000/inbox/delete/'+id, {
      method: "DELETE",
      headers: {
        "Content-Type": "application/json",
      }
    })
    const result = await response.json();

    console.log("Success:", result)
  } catch (err) {
    console.error(err)
  }
}

// Delete a user by table id
async function deleteUser(id) {
  try {
    const response = await fetch('http://localhost:5000/user/delete/'+id, {
      method: "DELETE",
      headers: {
        "Content-Type": "application/json",
      }
    })
    const result = await response.json();

    console.log("Success:", result)
  } catch (err) {
    console.error(err)
  }
}
```

### slughug/src/App.js

```javascript
import './App.css';
import Main from './components/Main';
import AuthenticationSection from "./components/AuthenticationSection";

function App() {
  return (
    <div className="App">
        <AuthenticationSection />
        <Main />
    </div>
  );
}

export default App;

```

### slughug/src/index.js

```javascript
import React from 'react';
import { createRoot } from 'react-dom/client'; // Import createRoot from react-dom/client
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Auth0Provider } from '@auth0/auth0-react'; // Import Auth0Provider from @auth0/auth0-react

// Get the DOM element with the ID 'root' and create a root render node
const container = document.getElementById('root');
const root = createRoot(container); // Create root with createRoot, no need to use ReactDOM again

// Render the Auth0Provider and App component into the root node using root.render
root.render(
    <React.StrictMode>
        <Auth0Provider
            domain="dev-slughub.us.auth0.com"
            clientId="KE2HGKGJMudGNuy2UORc4j35VvSxOkHd"
            authorizationParams={{
                redirect_uri: "http://localhost:3000" // 设置重定向的URL为localhost:3000
            }}
        >
            <App />
        </Auth0Provider>

    </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();

```

### slughug/src/components/Main.jsx

```javascript
import React, { useState } from "react";
import Account from "./Account";
import Grid from "./Grid";
import Write from "./Write";
import SlugIcon from "../assets/SlugIcon.png";
import composeButton from "../assets/compose.png";
import backgroundImage from '../assets/bgdone.png';
import { useAuth0 } from "@auth0/auth0-react";

const Main = () => {
  const [isAccountOpen, setIsAccountOpen] = useState(false);
  const [isNewMessageOpen, setNewMessageOpen] = useState(false);
  const [writeContent, setWriteContent] = useState("");
  const [writeHead, setWriteHead] = useState("");
  const [userData, updateUserData] = useState("");
  const { user, isAuthenticated } = useAuth0();

  const openMessage = () => {
    setNewMessageOpen(false);
  };

  const closeMessage = () => {
    setNewMessageOpen(false);
  };

  const onMessageChange = (content) => {
    setWriteContent(content);
  };

  const onHeadChange = (content) => {
    setWriteHead(content);
  };

  // Create a message
  async function createMessage(data) {
    try {
      const response = await fetch('http://localhost:5000/messages/add', {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(data)
      })

      const result = await response.json();
      console.log("Success:", result)
    } catch (err) {
      console.error(err)
    }
  }

  // Get user server id from auth0 username
  async function getUser(name) {
    try {
      const response = await fetch('http://localhost:5000/users/getID/'+name)
      updateUserData(await response.json());

      console.log("Success:", userData)
    } catch (err) {
      console.error(err)
    }
  }

  // Meaningcloud Sentiment Analysis API
  function analyzeSentiment(text) { 
    //event.preventDefault(); 
    const apiKey = "e1c2247e1f181a436994dbbc262adffb"; 
    const url = `https://api.meaningcloud.com/sentiment-2.1?key=${apiKey}&txt=${encodeURIComponent( text )}&lang=en`; 

    fetch(url) 
    .then((response) => { 
      if (response.ok) { 
        return response.json(); 
      } else { 
        throw new Error("Request failed"); 
      } 
    }) 
    .then((data) => { 
      const sentiment = data.score_tag; 
      // console.log(`Text: ${text}`); 
      // console.log(`Sentiment: ${sentiment}`); 
      if (sentiment == "P+") { 
        return "Strong Positive"; 
      } if (sentiment == "P") { 
        return "Positive"; 
      } if (sentiment == "NEU") { 
        return "Neutral"; 
      } if (sentiment == "N") { 
        return "Negative"; 
      } if (sentiment == "N+") { 
        return "Strong Negative"; 
      } if (sentiment == "NONE") { 
        return "Without Polarity"; 
      } 
    }) 
    .catch((error) => { 
      console.error("Error:", error); 
    }); 
  }

  const handleSend = () => {
    setNewMessageOpen(false);
    // Get User
    console.log(user)
    //getUser(user.email)

    const writeObj = {
      messageSender: null, 
      messageHead: writeHead, 
      messageContent: writeContent, 
      messageType: "regular", 
      messageVisibility: "private"
    }

    // THIS BAD BOY SENDS THAT DATA YKNOW
    const sentiment = analyzeSentiment(writeObj.messageContent)
    console.log(sentiment)
    if(sentiment != "Strong Negative" || sentiment != "Negative") {
      console.log("WE'RE POSTING A MESSAGE BOIS")
      console.log(writeObj)
      createMessage(writeObj)
    }
  }


  return (
    <div style={{ 
      backgroundImage: `url(${backgroundImage})`,
      backgroundSize: 'cover',
      backgroundPosition: 'center',
      minHeight: '100vh', // Ensures it covers the whole viewport height
      minWidth: '100vw'  // Ensures it covers the whole viewport width
    }}>
    <div className="flex min-h-screen justify-center items-center">
      <div className="absolute top-0 left-0 rounded p-4" onClick={() => setNewMessageOpen(true)}>
      <div onClick={() => setNewMessageOpen(true)}>
        {/* new button for compose */}
        <img src={composeButton} alt="Send Message" height={75} width={75} className="cursor-pointer" draggable="false"/>
      </div>
      </div>


      {isNewMessageOpen && (
          <Write
            open={isNewMessageOpen}
            close={closeMessage}
            send={handleSend}
            content={writeContent}
            head={writeHead}
            onHeadChange={onHeadChange}
            onMessageChange={onMessageChange}
          />
      )}

      <div className="absolute top-0 right-0 rounded p-4">
        <button onClick={() => setIsAccountOpen(true)}>
          <img alt="Slug Icon" src={SlugIcon} height={75} width={75} draggable="false"/>
        </button>
      </div>
      {isAccountOpen && (
        <div className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-50">
          <Account onClick={() => setIsAccountOpen(false)} />
        </div>
      )}

      <Grid />
    </div>
    </div>
  );
};

export default Main;

```

### slughug/tailwind.config.js

```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}


```

### slughug/src/index.css

```css
@tailwind base;
@tailwind components;
@tailwind utilities;

```

### server/models/Users.js

```javascript
const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema({
    name: String, 
    email: String, 
    auth0: String
})

const UserModel = mongoose.model("users", UserSchema);
module.exports = UserModel
```

### slughug/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';

```

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