# Project export: UCSC Made Easy

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: Empowering students with an intuitive 4-year course planner, delivering essential information in an easily digestible format.
- Devpost: https://devpost.com/software/ucsc-made-easy
- GitHub: https://github.com/nhi1e/ucsc-made-easy-frontend
- Video: https://www.youtube.com/embed/SuaNyoMAwjk?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — nhi1e (26 commits), waylonwilliams (25 commits)

## Devpost submission (written by the team)

### Inspiration

UCSC's provided 4-year course planning template is not helpful. Trying to find and understand all of the information required to build a simply livable schedule takes far more work than it should. The need to navigate multiple tabs to access course prerequisites, manually track fulfilled requirements and earned credits, and potentially schedule advisor meetings for unresolved issues adds unnecessary complexity. UCSC Made Easy does just that, it makes the whole process easy.

### What it does

UCSC Made Easy allows you to input your courses from all 4+ years of your education, and it will automatically serve you all of the necessary information such as course information and links, major requirement satisfaction, general education requirement satisfaction, and your total credit count.

### How we built it

The foundation of this project is its data, with information on 10k+ courses and major requirements from UCSC. Using React and Tailwind CSS we crafted a user-friendly interface designed to make course planning easy for anyone. Our Flask backend efficiently stores users' plans in the MySQL database under a UUID, eliminating the need for unnecessary engagement like logging in.

### Challenges we ran into

Web scraping proved challenging and time-consuming, and the management of a substantial list of courses across many user inputs posed latency challenges.

### Accomplishments we're proud of

We are very proud of our UI. Both of us have an interest in design and wanted to see how well we could do, and we are content with this result.

### What we learned

With each React project, we gain increased comfort in handling intricate components, and this endeavor, in particular, presented the most dynamic elements to manage. Additionally, our introduction to Tailwind was great, and we are pleased to have discovered it.

### What's next

Refining the application to be good enough for serious use. More web scraping and ensuring that data is accurate, polishing the UI to be as smooth as possible, and hopefully getting in touch with the school to share this platform and help students.

## README (from the GitHub repository)

# [UCSC Made Easy](https://ucscmadeeasy.vercel.app/)

[Link to the back end repo](https://github.com/waylonwilliams/ucsc-made-easy-backend)

https://github.com/waylonwilliams/ucsc-made-easy-backend/assets/145303505/8d3b7c52-2688-4d69-af73-1961a857f5dd

## Motivation

UCSC's provided course planning tools are unhelpful and leave many students without the knowledge needed for successful course planning. This app brings all necessary information into one place to make the process of course planning fast and intuitive for students unfamiliar with their requirements.

## Features

- Accurate course info for all 4000+ courses (ge fulfillment, credits, prerequisites, link, etc)
- Other credits accounted for such as AP exams, test outs, etc
- Major requirements for all majors across different catalog years
- Real-time requirement and prerequisite satisfaction check
- Downloading your plan as a pdf
- Store user plans in database to be reloaded upon return

## How to run

Visit the app on the web [here](https://ucscmadeeasy.vercel.app/)

To run locally,

Clone both the front-end and back-end repos

```
git clone https://github.com/waylonwilliams/ucsc-made-easy-backend.git
git clone https://github.com/nhi1e/ucsc-made-easy-frontend.git
```

In one terminal, navigate to the front-end repo and run

```
npm i
npm run dev
```

In a new terminal, navigate to the back-end repo and run

```
docker build -t course-planner .
docker run -it -p 5000:8080 course-planner
```

Or, if you don't have Docker
```
pip install -r requirements.txt
flask run
```


## Detected evidence (automated analysis)

Indexed codebase: 33 recognized source files, 190 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
- Flask (technology) — claimed on Devpost, not found in the code
- Python (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (36 of 36)

```
.eslintrc.cjs
.gitignore
index.html
package.json
postcss.config.js
README.md
src/App.jsx
src/components/CourseBox.jsx
src/components/CourseSelect.jsx
src/components/Credits.jsx
src/components/CustomOption.jsx
src/components/DarkModeButton.jsx
src/components/ExportButton.jsx
src/components/GE.jsx
src/components/Info.jsx
src/components/Left.jsx
src/components/MainSelect.jsx
src/components/Major.jsx
src/components/Navbar.jsx
src/components/NoteButton.jsx
src/components/PrereqBubble.jsx
src/components/Quarters.jsx
src/components/Right.jsx
src/components/SelectNewCourse.jsx
src/components/ToastContext.jsx
src/components/Years.jsx
src/data/AP_list.js
src/data/CS_requirements.js
src/data/GE.js
src/data/Majors_list.js
src/data/real_course_list.js
src/data/years.js
src/index.css
src/main.jsx
tailwind.config.js
vite.config.js
```

### Dependencies

- package.json: @types/react@^18.2.43, @types/react-dom@^18.2.17, @vitejs/plugin-react@^4.2.1, autoprefixer@^10.4.17, eslint@^8.55.0, eslint-plugin-react@^7.33.2, eslint-plugin-react-hooks@^4.6.0, eslint-plugin-react-refresh@^0.4.5, jspdf@^2.5.1, postcss@^8.4.33, prettier@^3.2.4, react@^18.2.0, react-dom@^18.2.0, react-select@^5.8.0, react-toastify@^10.0.4, tailwindcss@^3.4.1, uuid@^9.0.1, vite@^5.0.8

### Recent commits (newest first)

- update api routes
- adding transitions to selects
- new prereq bubbles
- Update README.md
- Update README.md
- Update README.md
- Merge remote-tracking branch 'refs/remotes/origin/main'
- setup production and development builds, npm run dev fetches from local host and build fetches from hosted backend
- Update README.md
- change readme
- fix ap fetch
- vercelly
- light mode
- color changes
- color changes
- need to redo
- more light mode scaffolding
- light mode
- fetching properly, should work on vercel
- removed strict mode

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

### package.json

```
{
  "name": "ucsc-made-easy-frontend",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "NODE_ENV=development vite",
    "build": "NODE_ENV=production vite build",
    "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "jspdf": "^2.5.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-select": "^5.8.0",
    "react-toastify": "^10.0.4",
    "uuid": "^9.0.1"
  },
  "devDependencies": {
    "@types/react": "^18.2.43",
    "@types/react-dom": "^18.2.17",
    "@vitejs/plugin-react": "^4.2.1",
    "autoprefixer": "^10.4.17",
    "eslint": "^8.55.0",
    "eslint-plugin-react": "^7.33.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.5",
    "postcss": "^8.4.33",
    "prettier": "^3.2.4",
    "tailwindcss": "^3.4.1",
    "vite": "^5.0.8"
  }
}

```

### src/main.jsx

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

ReactDOM.createRoot(document.getElementById("root")).render(<App />);

```

### src/App.jsx

```javascript
import Right from "./components/Right";
import Left from "./components/Left";
import { useEffect, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import { ToastProvider } from "./components/ToastContext";

var courses_format = [
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
];
var satisfied_format = [
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [], // for major reqs 16
  [], // for ge reqs 17
  0, // for credits 18
  ["Course Info Here", [], ""], // for course info 19
];
var ap_format = [];
let client_id = localStorage.getItem("client_id");
if (client_id === null) {
  client_id = uuidv4();
  if (typeof Storage !== "undefined") {
    localStorage.setItem("client_id", client_id);
  }
}

export default function App() {
  const [courses, setCourses] = useState(courses_format);
  const [satisfied, setSatisfied] = useState(satisfied_format);
  const [apCredit, setAPCredit] = useState(ap_format);
  const [colorMode, setColorMode] = useState(true); // true is dark, false is light
  // see below in the first div how to render different styles depending on the mode
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const dataFetch = async () => {
      const requestOptions = {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          client_id: client_id,
        }),
      };

      if (import.meta.env.PROD) {
        // production mode
        await fetch("https://ume.fly.dev/", requestOptions)
          .then((response) => response.json())
          .then((data) => {
            setCourses(data.schedule);
            setSatisfied(data.prereq);
            setAPCredit(data.ap_courses);
            setLoading(false);
          });
      } else {
        // development
        await fetch("http://127.0.0.1:5000/", requestOptions)
          .then((response) => response.json())
          .then((data) => {
            setCourses(data.schedule);
            setSatisfied(data.prereq);
            setAPCredit(data.ap_courses);
            setLoading(false);
          });
      }
    };
    dataFetch();
  }, []);

  if (loading) {
    return <></>; // could add some cute loading slug here
  } else {
    return (
      <ToastProvider>
        <div
          className={
            "transition-all duration-300 w-full min-h-screen text-white select-none " +
            (colorMode ? "bg-black-dark2" : "bg-white")
          }
        >
          <div className="flex">
            <Right
              satisfied={satisfied}
              courses={courses}
              apCredit={apCredit}
              colorMode={colorMode}
              setColorMode={setColorMode}
            />
            <Left
              courses={courses}
              setCourses={setCourses}
              client_id={client_id}
              satisfied={satisfied}
              setSatisfied={setSatisfied}
              apCredit={apCredit}
              setAPCredit={setAPCredit}
              colorMode={colorMode}
            />
          </div>
        </div>
      </ToastProvider>
    );
  }
}

```

### postcss.config.js

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

```

### vite.config.js

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

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

```

### index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/icons/slug.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ucsc, made easy</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

```

### tailwind.config.js

```javascript
/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
    colors: {
      gray: {
        DEFAULT: '#e5e7eb',
        dark: '#C8CCD5',
        darker: '#70747B',
        100:'#f2f2f2',
        200:'#ABB1BF',
      },
      blue: {
        DEFAULT:'#3b82f6',
        pastel:'#778DA9',
        light: '#C8D2E3',
        lighter:'#D9E0EC',
        dark:'#C6D0E2',
        dark2:'#A4B4D1',
        pastel_dark:'#8298C0',
      },
      yellow: {
        DEFAULT: '#F7DB6A',
        pastel:'#FAEDCB',
        pastel_dark:'#EEBD3E',
      },
      green: {
        DEFAULT: '#bbf7d0',
        pastel:'#D2E3C8',
        light:'#B6D2A6',
        pastel_dark:'#91B946'
      },
      red: {
        DEFAULT: '#EF6262',
        pastel:'#F2C6DE',
        light:'#E3C8D2'
      },
      orange: {
        pastel: '#F7D9C4',
        pastel_dark:'#EB9F6A',
      },
      white: {
        DEFAULT: "#F6F6F5",
        dark1: "#E7E8E7", 
        dark2: "#DBD9D9",
        dark3: "#C2BFBF",
        dark4:"#A9A5A5",
      },
      black: {
        DEFAULT: "#000000",
        light1:'#4C5464',
        light2:'#2D374C',
        dark1: '#212A3E',
        dark2:'#1A202C',
        dark3:'#101725',
      }

    }
  },
  plugins: [],
}
```

### src/index.css

```css
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@200;400;800&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;

* {
    margin: 0;
    font-family: 'Kanit', sans-serif;
    color: #333;
    background-color: '#F0F0F0';
  }
.Toastify__toast--error {
    color: #F2C6DE;
}

```

### src/data/years.js

```javascript
const years = [
	{value: 0, label: "2024"},
	{value: 1, label: "2023"},
	{value: 2, label: "2022"},
	{value: 3, label: "2021"},
    {value: 4, label: "2020"},
]
export default years;
```

### src/data/GE.js

```javascript
const ge = [
	{value: 0, label: "CC"},
	{value: 1, label: "ER"},
	{value: 2, label: "IM"},
	{value: 3, label: "MF"},
	{value: 4, label: "SI"},
	{value: 5, label: "SR"},
	{value: 6, label: "TA"},
	{value: 7, label: "PE"},
	{value: 8, label: "PR"},
	{value: 9, label: "ELWR"},
	{value: 10, label: "Composition"},
]
export default ge;
```

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