# Project export: BeSustainable

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: Making sustainable food choices one step at a time.
- Devpost: https://devpost.com/software/besustainable
- GitHub: https://github.com/IdkwhatImD0ing/BeSustainable
- Video: https://www.youtube.com/embed/ukji3S3eCow?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — Bill Zhang (42 commits), conniely (15 commits), Daniel Kim (15 commits), Restyled.io (8 commits)

## Devpost submission (written by the team)

### Inspiration

💡 As college students juggling heavy coursework, navigating the path to address global issues can feel overwhelming. Recognizing this challenge, we brainstormed solutions for busy individuals like us to contribute to sustainability. The result is BeSustainable. We are motivated by the idea that individual actions can have a ripple effect, sparking significant change. BeSustainable isn't just an app; it's a movement. Our belief is that by spreading the word about our app, we can empower the average person to contribute to creating a more sustainable world without interpreting their daily lives. We strive to make a difference with BeSustainable, one small step at a time.

### What it does

🪴 Encourage sustainability - The primary goal is to increase the sustainability habits of individuals in the world. Empower individuals - BeSustainable strives to uplift the average person to contribute small but impactful changes towards sustainability. Facilitate daily integration - We aim to smoothly integrate sustainable practices into people's lives, enhancing health and well-being without significant disruptions.

### How we built it

🛠️ Designs with Figma Frontend was built using MaterialsUI Backend was built using next.js Database MongoDB Spoonacular API Google OCR

### Challenges we ran into

⌛️ Design challenges include balancing creativity and feasibility like the flow of the camera page and wanting to add more features to increase user interactions. A frontend challenge was getting used to MaterialsUI Framework and knowing which components to use and was most convenient for our app. Knowing how to format each component to ensure that it follows the designs made for the app. Merge conflicts! We faced challenges in finding a quantitative way to create a sustainability score via a list of ingredients alongside gaining the data of the ingredients in an efficient manner from the barcode and ingredients list scans. We had trouble connecting the MongoDB with our project. We also had a difficult time working with Auth0 in order to create successful login/registration.

### Accomplishments we're proud of

👏🏼 We are proud to have created BeSustainable with the intention of increasing sustainability in average people’s lives one step at a time. Our application has a simple and easy-to-use interface that allows the user to quickly access the data through their mobile device. We are also proud of being able to learn new technology in order to create a working app.

### What we learned

📔 Our group gained insightful lessons through the development of our application BeSustainable. Some of which are: Discussing the flows and features of the application through the process so we’re on the same page. Creating a general timeline for completing tasks and assigning roles to quicken the development process. Discussing the flows and features of the application through the process so we’re on the same page. Creating a general timeline for completing tasks and assigning roles to quicken the development process.

### What's next

⏭️ We would in the future add an AI chatbot that will be able to give recommendation, feedback, as well as additional info the user asks for about any given ingredients! Furthermore, we would like to encourage users to exchange contacts through the app to interact with their friends' posts. As a user obtains a certain sustainability score, they would be able to connect with others around the world rather than just locally.

## README (from the GitHub repository)

This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.


## Detected evidence (automated analysis)

Indexed codebase: 36 recognized source files, 82 KB.
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- MongoDB (technology) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (47 of 47)

```
__tests__/link.test.js
.eslintrc.json
.gitignore
app/api/auth/[auth0]/route.js
app/api/post/caption.js
app/api/post/route.ts
app/api/scores/route.js
app/api/sustainability/gcpVision.js
app/api/sustainability/openaiVision.js
app/api/sustainability/route.js
app/api/sustainability/scores.json
app/api/sustainability/spoonacular.js
app/api/sustainability/utils.js
app/api/user/route.js
app/BottomNavbar.js
app/camera/Loading.js
app/camera/page.js
app/components/PostBlock.js
app/components/Tree.js
app/globals.css
app/home/page.js
app/layout.js
app/loading/Loading.js
app/loading/page.js
app/logout.js
app/page.js
app/profile/LineChart.js
app/profile/page.js
app/results/error.js
app/results/page.js
jest.config.js
jsconfig.json
lib/mongodb.js
lib/post-db.js
lib/user-db.js
middleware.ts
models/postModel.js
models/userModel.js
next.config.mjs
package.json
public/manifest.json
README.md
testcode.ps1
testCrud.js
testocr.ps1
testopenai.ps1
tsconfig.json
```

### Dependencies

- package.json: @auth0/nextjs-auth0@^3.5.0, @emotion/react@^11.11.3, @emotion/styled@^11.11.0, @ericblade/quagga2@^1.8.4, @fontsource/roboto@^5.0.8, @google-cloud/vision@^4.0.2, @mui/icons-material@^5.15.5, @mui/material@^5.15.5, @mui/material-next@^6.0.0-alpha.118, @typegoose/typegoose@^12.1.0, @zxing/library@^0.20.0, axios@^1.6.5, chart.js@^4.4.1, chartjs@^0.3.24, dotenv@^16.3.2, eslint@^8, eslint-config-next@14.1.0, google-gax@^4.1.0, jest@^29.7.0, jest-environment-jsdom@^29.7.0, mongodb@^6.3.0, mongoose@^8.1.0, next@14.1.0, next-pwa@^5.6.0, openai@^4.24.7, react@^18, react-chartjs-2@^5.2.0, react-dom@^18, typescript@5.3.3, uuid@^9.0.1

### Recent commits (newest first)

- Type
- Fix styling
- Some more updates, -pwa
- Final updates
- Works
- Merge branch 'main' of https://github.com/IdkwhatImD0ing/BeSustainable
- FInal
- fixed styling
- EVEYRTHIANS GWORKING
- Images work
- Merge branch 'main' of https://github.com/IdkwhatImD0ing/BeSustainable
- All calls work
- results page and error page
- Merge pull request #25 from IdkwhatImD0ing/progress
- Merge branch 'main' into progress
- Works yay
- Merge pull request #24 from IdkwhatImD0ing/LineChart
- Line Chart
- Fixed existing user problem
- Change

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

### package.json

```
{
  "name": "app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "test": "jest"
  },
  "dependencies": {
    "@auth0/nextjs-auth0": "^3.5.0",
    "@emotion/react": "^11.11.3",
    "@emotion/styled": "^11.11.0",
    "@ericblade/quagga2": "^1.8.4",
    "@fontsource/roboto": "^5.0.8",
    "@google-cloud/vision": "^4.0.2",
    "@mui/icons-material": "^5.15.5",
    "@mui/material": "^5.15.5",
    "@mui/material-next": "^6.0.0-alpha.118",
    "@typegoose/typegoose": "^12.1.0",
    "@zxing/library": "^0.20.0",
    "axios": "^1.6.5",
    "chart.js": "^4.4.1",
    "chartjs": "^0.3.24",
    "dotenv": "^16.3.2",
    "google-gax": "^4.1.0",
    "mongodb": "^6.3.0",
    "mongoose": "^8.1.0",
    "next": "14.1.0",
    "next-pwa": "^5.6.0",
    "openai": "^4.24.7",
    "react": "^18",
    "react-chartjs-2": "^5.2.0",
    "react-dom": "^18",
    "uuid": "^9.0.1"
  },
  "devDependencies": {
    "eslint": "^8",
    "eslint-config-next": "14.1.0",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "typescript": "5.3.3"
  }
}

```

### app/layout.js

```javascript
import './globals.css'
import {UserProvider} from '@auth0/nextjs-auth0/client'
import BottomNav from './BottomNavbar'
import Logout from './logout'

export const viewport = {
  themeColor: '#000000',
  initialScale: '1.0',
  maximumScale: '1.0',
  userScalable: 'no',
  colorScheme: 'light dark',
}

export const metadata = {
  title: 'BeSustainable',
  applicationName: 'BeSustainable',
  description: 'BeReal but for food',
  manifest: '/manifest.json',
  icons: [
    {
      rel: 'apple-touch-icon',
      url: '/icons/apple-touch-icon.png',
    },
    {
      rel: 'icon',
      url: '/icons/favicon-32x32.png',
    },
    {
      rel: 'icon',
      url: '/icons/favicon-16x16.png',
    },
  ],
  appleWebApp: {
    capable: 'yes',
    title: 'BeSustainable',
    statusBarStyle: 'default',
  },
}

export default function RootLayout({children}) {
  return (
    <UserProvider>
      <html lang="en">
        <body>
          <Logout />
          <BottomNav />
          {children}
        </body>
      </html>
    </UserProvider>
  )
}

```

### app/page.js

```javascript
'use client'
import {useRouter} from 'next/navigation'
import {Stack, Button, Box, Typography} from '@mui/material'

export default function Landing() {
  const router = useRouter()
  return (
    <Stack
      backgroundColor="white"
      direction="column"
      display="flex"
      width="100%"
      height="100vh"
      justifyContent="center"
      alignItems="center"
      spacing={3}
      paddingTop="100px"
      position="relative"
      paddingX="20%"
    >
      <Typography
        sx={{
          position: 'relative',
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
          fontFamily: 'Helvetica',
          fontSize: '36px',
          fontWeight: '700',
          color: 'rgba(63, 170, 114, 1)',
        }}
      >
        BeSustainable
      </Typography>
      <Typography
        sx={{
          position: 'relative',
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
          fontFamily: 'Helvetica',
          fontSize: '20px',
          fontWeight: '400',
          color: 'rgba(63, 170, 114, 1)',
        }}
      >
        Making sustainable food choices one step at a time.
      </Typography>
      <Button
        onClick={() => {
          router.push('/api/auth/login')
        }}
        sx={{
          color: 'rgba(26, 28, 28, 0.5)',
          zIndex: 'auto', // Change this to "auto"
          marginBottom: '65%', // Change this to "0%"
          marginTop: '-20px',
        }}
        variant="outlined"
      >
        Get Started
      </Button>
      <Box position="fixed" width="100%" bottom={0}>
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="100%"
          height="259"
          viewBox="0 0 390 259"
          preserveAspectRatio="none"
          fill="none"
        >
          <path
            d="M349.262 15.9632C342.321 23.419 346.87 5.34183 352.447 2.58477C353.777 2.29149 355.043 2.03411 356.203 1.81615C354.757 1.77027 353.509 2.05962 352.447 2.58477C340.937 5.12118 324.547 10.3425 329.408 15.9632C325.804 13.4779 319.207 7.57056 321.661 3.82352C316.98 7.39213 307.812 15.8294 308.586 21.0293C306.596 18.6078 302.711 13.4014 303.098 11.9485C301.288 13.5864 298.124 17.1894 298.984 19.2766C296.154 14.3527 291.55 4.23613 294.059 0C290.401 3.57906 283.139 11.6684 282.927 15.7822C280.023 14.4797 275.547 11.7893 274.529 8.50733C272.538 11.5661 268.912 18.3529 270.332 21.0293C268.126 20.3602 263.714 18.6396 263.714 17.1102C262.208 17.397 259.227 18.7926 259.356 22.0808C259.517 26.1911 252.9 11.0882 257.419 5.5441C254.029 7.23282 247.25 11.9103 247.25 17.1102C245.367 14.7843 242.02 8.87056 243.699 3.82352C240.309 5.32106 233.627 10.3617 234.014 18.5441C231.97 17.7794 227.881 15.6764 227.881 13.3823C226.536 15.5808 224.104 20.8191 225.137 24.1838C220.617 22.6862 212.934 17.722 218.357 9.84556C215.883 12.522 211.03 18.9646 211.417 23.3235C208.888 21.3798 204.056 16.3838 204.96 11.9485C202.216 13.3186 196.341 16.7852 194.792 19.6911C192.101 19.4043 187.528 17.0338 190.756 9.84556C188.012 10.1005 182.04 13.1529 180.103 23.3235C178.381 18.2573 175.261 7.6088 176.552 5.5441C173.378 8.85781 167.093 16.8044 167.352 22.0808C165.522 20.5514 162.09 17.1676 162.993 15.8676C162.133 17.0147 160.669 19.6529 161.702 21.0293C159.173 20.5195 154.665 18.372 156.86 13.8603C154.546 14.4019 150.016 16.5941 150.403 21.0293C148.574 18.7352 145.045 13.4397 145.561 10.6103C143.786 14.2107 140.235 21.9661 140.235 24.1838V24.7573C139.158 20.0097 137.168 9.84556 137.813 7.1691C134.531 10.1005 127.967 16.8426 127.967 20.3602C126.299 20.1691 122.899 19.0029 122.641 15.8676C121.349 16.696 118.864 19.0985 119.251 22.0808C118.014 20.6789 115.377 16.9764 114.732 13.3823C113.171 15.6446 110.051 20.7999 110.051 23.3235C110.051 26.4779 106.984 17.9705 105.047 10.6103C102.195 14.2745 96.7826 22.2338 97.9448 24.7573C96.3845 23.1004 93.5867 19.0029 94.878 15.8676C93.5867 16.8235 91.0041 19.1941 91.0041 21.0293C89.8204 19.723 87.679 16.4602 88.5829 13.8603C86.8074 15.7083 83.2563 19.9396 83.2563 22.0808V22.6543C81.1042 21.571 77.4455 17.9132 80.0281 11.9485C77.3379 14.1152 72.1834 19.2896 73.0873 22.6543C70.5048 21.6985 66.1144 19.0029 69.2135 15.8676C67.3842 17.2377 63.9838 20.3985 65.0168 22.0808C61.8962 21.4754 56.5911 18.6014 60.3358 11.9485C56.5695 14.2107 49.7151 19.4044 52.4268 22.0808C55.1385 24.7573 52.911 23.196 51.4583 22.0808C50.7051 20.0416 48.6819 16.0588 46.6158 16.4411C46.8848 18.321 46.9708 22.0808 45.163 22.0808C42.9032 22.0808 39.8365 16.1544 40.3207 13.3823C39.137 14.7205 36.8987 17.9897 37.4152 20.3602C38.0609 23.3235 30.7974 13 32.5729 10.6103C30.7697 11.6977 27.1812 14.7577 27.11 18.3666C27.1093 18.4029 27.1088 18.4393 27.1088 18.4757C26.6502 17.795 25.7636 16.2586 25.8859 15.5584C24.714 16.1419 22.4925 17.5421 22.9817 18.4757C21.3919 17.5421 20.2302 14.48 20.2302 14.48C20.2302 14.48 18.9434 17.3156 19.0491 19.5522C18.2256 18.2072 14.9344 16.0657 13.0459 14.975C13.6573 15.5195 12.7911 18.3136 12.2816 19.6425C9.83585 19.0684 9.32629 15.5484 9.37726 13.8603C8.04291 12.7664 7.02872 16.8304 6.58759 19.5824C6.31924 18.1923 4.06226 16.226 2.59271 15.1523C2.54382 15.0589 2.4631 14.9974 2.34583 14.975C2.42511 15.0309 2.5076 15.0901 2.59271 15.1523C3.0535 16.0331 0.684784 19.7553 -0.558502 20.3602C-1.93369 21.0293 0.342987 573 0.342987 573H190.756H390V13.8598C386.026 11.4599 383.367 27.4218 383.367 25.088C383.367 23.3321 377.81 16.4426 374.536 12.6325C374.379 12.385 374.183 12.156 373.944 11.9485C374.13 12.1628 374.328 12.3915 374.536 12.6325C376.332 15.4745 372.91 20.7432 370.456 23.6282C370.306 23.5331 370.144 23.4316 369.969 23.3235C366.178 20.9783 365.536 13.3611 365.689 9.84556C363.366 9.84556 361.562 16.4093 360.951 19.6911C358.016 18.6206 357.588 10.9796 357.741 7.29302C354.317 6.32158 350.662 12.6683 349.262 15.9632Z"
            fill="#F0C571"
          />
        </svg>
      </Box>
    </Stack>
  )
}

```

### app/loading/page.js

```javascript
import Loading from './Loading'

export default function Page() {
  return <Loading />
}

```

### app/results/page.js

```javascript
'use client'

import {Stack, Button, Box, Typography, LinearProgress} from '@mui/material'
import Image from 'next/image'
import {useState, useEffect} from 'react'
import {useRouter} from 'next/navigation'
import {useUser} from '@auth0/nextjs-auth0/client'
import Error from './error'
import Loading from '../camera/Loading'

export default function Results() {
  const [ingredients, setIngredients] = useState()
  const [score, setScore] = useState(0)
  const [image, setImage] = useState('')
  const [mode, setMode] = useState('')
  const [loading, setLoading] = useState(false)
  const router = useRouter()
  const {user} = useUser()

  useEffect(() => {
    if (!user) {
      return
    }
    const score = localStorage.getItem('score')
    setIngredients(localStorage.getItem('ingredients'))
    setScore(score)
    setImage(localStorage.getItem('image'))
    setMode(localStorage.getItem('mode'))
    // convert to number
    fetch('/api/scores', {
      method: 'POST',
      body: JSON.stringify({
        userId: user.sid,
        increase: Number(score),
      }),
    })
  }, [user])

  // If ingredients list is empty, but not undefined
  if (ingredients && ingredients.length === 0) {
    return <Error />
  }

  if (loading) {
    return <Loading />
  }

  return (
    <Stack
      direction="column"
      width="100%"
      height="100vh"
      backgroundColor="White"
      justifyContent="center"
      alignItems="center"
      spacing={3}
      paddingTop="20%"
      overflow="hidden"
      paddingX="10%"
    >
      <Typography
        variant="h2"
        sx={{
          justifyContent: 'center',
          fontFamily: 'Helvetica',
          fontSize: '30px',
          fontStyle: 'normal',
          fontWeight: 700,
          color: 'rgba(63, 170, 114, 1)',
        }}
      >
        Results
      </Typography>

      <Image
        src={image} // Dynamic source, ensure it's a valid path or URL
        alt="Result" // Always provide an alt attribute for accessibility
        width={450} // Width of the image in pixels
        height={300} // Height of the image in pixels
        layout="fixed" // This will keep the dimensions you provided
      />

      <Typography
        width="100%"
        align="center"
        sx={{
          fontFamily: 'Helvetica',
          fontSize: '24px',
          color: 'black',
          fontWeight: '700',
        }}
      >
        {ingredients}
      </Typography>

      <Typography
        sx={{
          fontFamily: 'Helvetica',
          fontWeight: '700',
          fontSize: '24px',
          color: 'rgba(26, 28, 28, 1)',
        }}
      >
        {score ? score.slice(0, 5) : 0}%
      </Typography>
      <Box width="100%">
        <LinearProgress variant="determinate" value={score} color="success" />
      </Box>

      {mode === 'upload' && (
        <Button
          onClick={() => {
            setLoading(true)
            fetch('/api/post', {
              method: 'POST',
              body: JSON.stringify({
                userId: user.sid,
                image: image,
              }),
            }).then(() => {
              router.push('/home')
            })
          }}
          sx={{width: '331px', height: '60px', color: 'rgba(63, 170, 114, 1)'}}
        >
          Post to social media
        </Button>
      )}
    </Stack>
  )
}

```

### app/profile/page.js

```javascript
'use client'

import {useState, useEffect} from 'react'
import {Avatar, Stack, Typography} from '@mui/material'
import Box from '@mui/material/Box'
import Image from 'next/image'

import {useUser} from '@auth0/nextjs-auth0/client'
import LineChartComponent from './LineChart'

export default function Profile() {
  const {user} = useUser()
  const [scores, setScores] = useState()

  useEffect(() => {
    if (!user) {
      return
    }
    fetch('/api/scores?userId=' + user.sid)
      .then((res) => res.json())
      .then((data) => {
        setScores(data)
      })
  }, [user])

  return (
    <Stack
      width="100%"
      height="100vh"
      backgroundColor="white"
      justifyContent="flex-start"
      alignItems="center"
      direction="column"
      paddingTop="15%"
      spacing={3}
    >
      <Typography
        variant="h2"
        sx={{
          justifyContent: 'center',
          fontFamily: 'Helvetica',
          fontSize: '30px',
          fontStyle: 'normal',
          fontWeight: 700,
          color: 'rgba(63, 170, 114, 1)',
        }}
      >
        Profile
      </Typography>
      <Stack
        width="100%"
        direction="row"
        justifyContent="center"
        alignItems="center"
        sx={{marginTop: '16%'}}
        spacing={5}
      >
        <Avatar
          src={user ? user.picture : ''}
          sx={{width: '100px', height: '100px'}}
        />

        <Stack direction="column">
          <Typography
            variant="h5"
            sx={{
              color: 'gray',
              marginRight: '10%',
              fontFamily: 'Helvetica',
            }}
          >
            {user ? user.name : ''}
          </Typography>
          <Typography
            sx={{
              color: 'gray',
              marginRight: '10%',
              fontFamily: 'Helvetica',
            }}
          >
            #12345
          </Typography>
        </Stack>
      </Stack>
      <Typography zIndex={50} color="black">
        Way to Go Green
      </Typography>
      <Box
        zIndex={50}
        sx={{
          justifyContent: 'center',
          backgroundColor: 'white',
        }}
      >
        {scores && <LineChartComponent data={scores} />}
      </Box>
      <Box
        height="100%"
        position="fixed"
        left="0"
        top="0"
        paddingTop="40%"
        width="100%"
        overflow="hidden"
        zIndex="1"
      >
        {/* <Image
          src="/woman.png" // Relative path to your image file
          alt="Woman"
          layout="fill" // This allows the image to fill the parent container
          objectFit="contain" // This can be 'contain', 'cover', etc. depending on how you want the image to fit
          objectPosition="110px bottom" // Positions the image at the bottom and 110px from the left
          style={{
            position: 'absolute',
            zIndex: '4',
          }}
        /> */}

        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="100%"
          height="1134"
          viewBox="0 0 390 1134"
          preserveAspectRatio="none"
          fill="none"
        >
          <path
            d="M349.262 31.592C342.321 46.3476 346.87 10.5718 352.447 5.1154C353.777 4.53499 355.043 4.02561 356.203 3.59427C354.757 3.50347 353.509 4.07611 352.447 5.1154C340.937 10.1351 324.547 20.4684 329.408 31.592C325.804 26.6735 319.207 14.9826 321.661 7.56696C316.98 14.6295 307.812 31.3272 308.586 41.6183C306.596 36.8259 302.711 26.5222 303.098 23.6468C301.288 26.8883 298.124 34.0187 298.984 38.1496C296.154 28.4048 291.55 8.38355 294.059 0C290.401 7.08316 283.139 23.0925 282.927 31.2338C280.023 28.6562 275.547 23.3317 274.529 16.8365C272.538 22.8901 268.912 36.3214 270.332 41.6183C268.126 40.2941 263.714 36.8889 263.714 33.8621C262.208 34.4297 259.227 37.1916 259.356 43.6992C259.517 51.8337 252.9 21.9442 257.419 10.9721C254.029 14.3142 247.25 23.5711 247.25 33.8621C245.367 29.2589 242.02 17.5553 243.699 7.56696C240.309 10.5307 233.627 20.5065 234.014 36.6998C231.97 35.1864 227.881 31.0245 227.881 26.4844C226.536 30.8354 224.104 41.2021 225.137 47.861C220.617 44.8973 212.934 35.0729 218.357 19.4849C215.883 24.7818 211.03 37.5321 211.417 46.1585C208.888 42.3119 204.056 32.4244 204.96 23.6468C202.216 26.3582 196.341 33.2189 194.792 38.9698C192.101 38.4023 187.528 33.7108 190.756 19.4849C188.012 19.9894 182.04 26.0303 180.103 46.1585C178.381 36.1322 175.261 15.0583 176.552 10.9721C173.378 17.5301 167.093 33.2568 167.352 43.6992C165.522 40.6724 162.09 33.9757 162.993 31.4029C162.133 33.673 160.669 38.8942 161.702 41.6183C159.173 40.6094 154.665 36.3592 156.86 27.4302C154.546 28.5022 150.016 32.8406 150.403 41.6183C148.574 37.0781 145.045 26.5979 145.561 20.9983C143.786 28.1239 140.235 43.4722 140.235 47.861V48.9961C139.158 39.6004 137.168 19.4849 137.813 14.1881C134.531 19.9894 127.967 33.3325 127.967 40.2941C126.299 39.9157 122.899 37.6078 122.641 31.4029C121.349 33.0424 118.864 37.797 119.251 43.6992C118.014 40.9246 115.377 33.5973 114.732 26.4844C113.171 30.9615 110.051 41.1643 110.051 46.1585C110.051 52.4012 106.984 35.5647 105.047 20.9983C102.195 28.25 96.7826 44.0019 97.9448 48.9961C96.3845 45.7171 93.5867 37.6078 94.878 31.4029C93.5867 33.2946 91.0041 37.9861 91.0041 41.6183C89.8204 39.0329 87.679 32.5758 88.5829 27.4302C86.8074 31.0876 83.2563 39.4617 83.2563 43.6992V44.8342C81.1042 42.6903 77.4455 35.4512 80.0281 23.6468C77.3379 27.9347 72.1834 38.1753 73.0873 44.8342C70.5048 42.9425 66.1144 37.6078 69.2135 31.4029C67.3842 34.1144 63.9838 40.3697 65.0168 43.6992C61.8962 42.5011 56.5911 36.8133 60.3358 23.6468C56.5695 28.1239 49.7151 38.4023 52.4268 43.6992C55.1385 48.9961 52.911 45.9062 51.4583 43.6992C50.7051 39.6635 48.6819 31.7812 46.6158 32.5379C46.8848 36.2584 46.9708 43.6992 45.163 43.6992C42.9032 43.6992 39.8365 31.9704 40.3207 26.4844C39.137 29.1328 36.8987 35.6026 37.4152 40.2941C38.0609 46.1585 30.7974 25.7277 32.5729 20.9983C30.7697 23.1504 27.1812 29.2063 27.11 36.3486C
[truncated — 1260 more characters]
```

### app/camera/page.js

```javascript
'use client'
import React, {useEffect, useState, useRef} from 'react'
import {BrowserMultiFormatReader, NotFoundException} from '@zxing/library'
import {Box, Typography, Button, Stack, ButtonGroup} from '@mui/material'
import CircleIcon from '@mui/icons-material/Circle'
import PanoramaFishEyeIcon from '@mui/icons-material/PanoramaFishEye'
import Item from '@mui/material/ListItem'
import Loading from './Loading'
import {useRouter} from 'next/navigation'

const ScannerCamera = () => {
  const [mode, setMode] = useState('scanner') // ['scanner', 'ingredients', 'upload'
  const [loading, setLoading] = useState(false)
  const router = useRouter()

  const videoRef = useRef()

  const startVideoStream = async (videoElementRef, useBackCamera = true) => {
    try {
      const constraints = {
        video: {
          facingMode: useBackCamera ? 'environment' : 'user', // 'environment' for back camera, 'user' for front
        },
      }
      const stream = await navigator.mediaDevices.getUserMedia(constraints)
      videoElementRef.current.srcObject = stream
      videoElementRef.current.play()
    } catch (error) {
      console.error('Error accessing the camera:', error)
    }
  }

  useEffect(() => {
    if (mode === 'scanner') {
      const codeReader = new BrowserMultiFormatReader()
      // Start decoding from the default video device.
      codeReader.decodeFromVideoDevice(null, 'video', (result, err) => {
        if (result) {
          // Save image to local storage
          const imageData = captureImage()
          localStorage.setItem('image', imageData)
          codeReader.reset()
          setLoading(true)
          fetch('/api/sustainability', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              mode: 'upc',
              code: result.text,
            }),
          })
            .then((res) => res.json())
            .then((data) => {
              localStorage.setItem(
                'ingredients',
                data.ingredients.matched_ingredients,
              )
              localStorage.setItem('mode', 'scanner')
              localStorage.setItem('score', data.score)
              router.push('/results')
            })
        }

        if (err && !(err instanceof NotFoundException)) {
          console.error(err)
        }
      })

      // Cleanup: Reset the code reader when the component is unmounted.
      return () => {
        codeReader.reset()
      }
    } else {
      startVideoStream(videoRef)
    }
  }, [mode, router])

  // Capture image and convert to base64
  const captureImage = () => {
    const video = videoRef.current
    if (!video) {
      return
    }

    const canvas = document.createElement('canvas')
    canvas.width = video.videoWidth
    canvas.height = video.videoHeight
    const ctx = canvas.getContext('2d')
    ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
    const imageData = canvas.toDataURL('image/png')
    return imageData
  }

  const handleCircleClick = async () => {
    setLoading(true)
    if (mode !== 'scanner') {
      const imageData = captureImage()

      // Determine the mode for the API ('ocr' or 'vision')
      const apiMode = mode === 'ingredients' ? 'ocr' : 'vision'

      // Prepare the body of the POST request
      const requestBody = {
        mode: apiMode,
        image: imageData, // Ensure this is in the correct format expected by your API
      }

      try {
        const response = await fetch('/api/sustainability', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(requestBody),
        })

        const data = await response.json()
        if (response.ok) {
          localStorage.setItem('image', imageData)
          localStorage.setItem(
            'ingredients',
            data.ingredients.matched_ingredients,
          )
          localStorage.setItem('score', data.score)
          localStorage.setItem('mode', mode)
          router.push('/results')
          // Handle successful response (e.g., display ingredients and score)
        } else {
          console.error('Server responded with an error:', data.error)
          // Handle errors (e.g., show error message to the user)
        }
      } catch (error) {
        console.error('Error making the request:', error)
        // Handle network errors (e.g., show error message to the user)
      }
    }
  }
  // Change state of camera to scan ingredients
  const handleIngredientsClick = () => {
    setMode('ingredients')
  }

  // Change state of camera to scan qr code
  const handleScannerClick = () => {
    setMode('scanner')
  }

  // Change state of camera to upload a photo
  const handleUploadClick = () => {
    setMode('upload')
  }

  if (loading) {
    return <Loading />
  }

  return (
    <Stack
      direction="column"
      justifyContent="center"
      alignItems="center"
      width="100%"
      height="100vh"
      spacing={5}
      overflow="hidden"
      backgroundColor="white"
    >
      <Typography
        variant="h2"
        sx={{
          justifyContent: 'center',
          fontFamily: 'Helvetica',
          fontSize: '30px',
          fontStyle: 'normal',
          fontWeight: 700,
          color: 'rgba(63, 170, 114, 1)',
        }}
      >
        BeSustainable
      </Typography>

      <Box width="450px" height="300px">
        <video
          id="video"
          ref={videoRef}
          style={{
            position: 'relative',
            height: '100%',
            width: '100%',
            padding: '20px',
          }}
        />
      </Box>

      <Stack
        direction="column"
        spacing={5}
        sx={{
          position: 'relative',
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
        }}
      >
        <ButtonGroup
          size="small
[truncated — 3105 more characters]
```

### app/home/page.js

```javascript
'use client'

import {useState, useEffect} from 'react'
import {Box, Stack, Typography} from '@mui/material'
import Image from 'next/image'
import PostBlock from '../components/PostBlock'
import {useUser} from '@auth0/nextjs-auth0/client'

export default function Home() {
  const {user} = useUser()
  const [posts, setPosts] = useState([])

  useEffect(() => {
    if (!user) return
    const fetchData = async () => {
      await fetch('/api/user', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({userId: user.sid}),
      })

      const data = await fetch('/api/post?userId=' + user.sid)
      const posts = await data.json()
      setPosts(posts)
    }

    fetchData()
  }, [user])

  const Icon = () => {
    return (
      <svg
        xmlns="http://www.w3.org/2000/svg"
        width="32"
        height="32"
        viewBox="0 0 32 32"
        fill="none"
      >
        <path
          d="M16 3.33331C17.5681 3.33283 19.0951 3.83503 20.3569 4.76621C21.6186 5.69738 22.5486 7.00848 23.0104 8.50708C23.4722 10.0057 23.4414 11.6128 22.9227 13.0927C22.4039 14.5725 21.4244 15.8471 20.128 16.7293C22.3782 17.5546 24.3299 19.0346 25.7319 20.9785C27.1339 22.9225 27.9219 25.2416 27.9947 27.6373C28.0015 27.7703 27.9816 27.9033 27.9364 28.0285C27.8911 28.1537 27.8212 28.2686 27.7309 28.3665C27.6406 28.4643 27.5317 28.5432 27.4105 28.5984C27.2894 28.6536 27.1584 28.684 27.0253 28.6879C26.8922 28.6918 26.7597 28.6691 26.6355 28.6211C26.5113 28.5731 26.3979 28.5008 26.302 28.4084C26.2062 28.316 26.1297 28.2054 26.0772 28.0831C26.0246 27.9607 25.997 27.8291 25.996 27.696C25.9166 25.0979 24.8287 22.6329 22.9629 20.8232C21.0971 19.0135 18.5999 18.0015 16.0007 18.0015C13.4014 18.0015 10.9043 19.0135 9.03844 20.8232C7.17261 22.6329 6.08473 25.0979 6.00533 27.696C5.99738 27.9612 5.88439 28.2124 5.69123 28.3943C5.49806 28.5762 5.24055 28.6739 4.97533 28.666C4.71012 28.658 4.45892 28.545 4.27701 28.3519C4.0951 28.1587 3.99738 27.9012 4.00533 27.636C4.07833 25.2405 4.86653 22.9217 6.26848 20.978C7.67044 19.0343 9.622 17.5546 11.872 16.7293C10.5756 15.8471 9.59611 14.5725 9.07734 13.0927C8.55856 11.6128 8.52782 10.0057 8.98961 8.50708C9.45141 7.00848 10.3814 5.69738 11.6431 4.76621C12.9049 3.83503 14.4319 3.33283 16 3.33331ZM10.6667 10.6666C10.6667 12.0811 11.2286 13.4377 12.2288 14.4379C13.229 15.4381 14.5855 16 16 16C17.4145 16 18.771 15.4381 19.7712 14.4379C20.7714 13.4377 21.3333 12.0811 21.3333 10.6666C21.3333 9.25216 20.7714 7.8956 19.7712 6.89541C18.771 5.89522 17.4145 5.33331 16 5.33331C14.5855 5.33331 13.229 5.89522 12.2288 6.89541C11.2286 7.8956 10.6667 9.25216 10.6667 10.6666Z"
          fill="#3A4320"
        />
      </svg>
    )
  }

  return (
    <Stack
      direction="column"
      position="relative"
      width="100%"
      height="100vh"
      backgroundColor="white"
      justifyContent="flex-start"
      alignItems="flex-start"
      paddingTop="20%"
      paddingX="10%"
      spacing={2}
      overflow="hidden"
      zIndex={-10}
    >
      <Stack
        direction="row"
        width="100%"
        justifyContent="space-between"
        alignItems="center"
        zIndex={50}
      >
        <Typography
          sx={{
            fontFamily: 'Helvetica',
            fontSize: '20px',
            fontWeight: '400',
            color: ' rgba(26, 28, 28, 1)',
          }}
        >
          Welcome home,
        </Typography>
        <Icon />
      </Stack>
      <Typography
        zIndex={50}
        sx={{
          fontFamily: 'Helvetica',
          fontSize: '32px',
          fontWeight: '700',
          color: ' rgba(26, 28, 28, 1)',
        }}
      >
        {user ? user.name : ''}
      </Typography>

      <div
        style={{
          position: 'absolute',
          right: '0',
          zIndex: '50',
          width: '221px', // Ensure the container has the same dimensions as the image
          height: '166px',
        }}
      >
        <Image
          src="/cart.png" // Relative path to your image file
          alt="Cart"
          width={221} // Width of the image in pixels
          height={166} // Height of the image in pixels
        />
      </div>

      <Typography
        zIndex={50}
        sx={{
          fontFamily: 'Helvetica',
          fontSize: '32px',
          fontWeight: '700',
          color: 'black',
        }}
      >
        Time to Feast
      </Typography>

      <Stack
        direction="column"
        width="100%"
        spacing={3}
        justifyContent="flex-start"
        alignItems="center"
        overflow="auto"
        zIndex={50}
      >
        {posts.map((post, index) => (
          <PostBlock
            key={index}
            imageUrl={post.imageLink}
            caption={post.caption}
          />
        ))}
      </Stack>

      <Box
        height="100%"
        position="fixed"
        left="0"
        top="0"
        paddingTop="40%"
        width="100%"
        overflow="hidden"
        zIndex={1}
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="100%"
          height="1134"
          viewBox="0 0 390 1134"
          preserveAspectRatio="none"
          fill="none"
        >
          <path
            d="M349.262 31.592C342.321 46.3476 346.87 10.5718 352.447 5.1154C353.777 4.53499 355.043 4.02561 356.203 3.59427C354.757 3.50347 353.509 4.07611 352.447 5.1154C340.937 10.1351 324.547 20.4684 329.408 31.592C325.804 26.6735 319.207 14.9826 321.661 7.56696C316.98 14.6295 307.812 31.3272 308.586 41.6183C306.596 36.8259 302.711 26.5222 303.098 23.6468C301.288 26.8883 298.124 34.0187 298.984 38.1496C296.154 28.4048 291.55 8.38355 294.059 0C290.401 7.08316 283.139 23.0925 282.927 31.2338C280.023 28.6562 275.547 23.3317 274.529 16.8365C272.538 22.8901 268.912 36.3214 270.332 41.6183C268.126 40.2941 263.714 36.8889 263.714 33.8621C262.208 34.4297 259.227 37.1916 259.356 43.6992C259.517 51.8337 252.9 21.9442 257.419 10.
[truncated — 3371 more characters]
```

### app/api/user/route.js

```javascript
import {NextResponse} from 'next/server'
import {createUser} from '../../../lib/user-db'

export async function POST(request) {
  try {
    // Parse the request body
    const body = await request.json()
    const {userId} = body

    if (!userId) {
      return NextResponse.json(
        {error: 'User ID is required'},
        {
          status: 400,
        },
      )
    }

    // Call your helper function to create a user
    const {newUser, code} = await createUser(userId)

    // Return the new user in the response
    return NextResponse.json(newUser, {
      status: code,
    })
  } catch (error) {
    console.error('Error in POST request: ', error)
    return NextResponse.json(
      {error: error.message},
      {
        status: 500, // 500 for Internal Server Error
      },
    )
  }
}

```

### app/api/scores/route.js

```javascript
import {NextResponse} from 'next/server'
import {updateScore, getScores} from '../../../lib/user-db'

export async function POST(request) {
  try {
    // Parse the request body
    const body = await request.json()
    const {userId, increase} = body

    if (!userId || increase === undefined) {
      return NextResponse.json(
        {error: 'Missing required fields'},
        {
          status: 400,
        },
      )
    }

    // Call your helper function to update the user's score
    const updatedUser = await updateScore(userId, increase)

    // Return the updated user in the response
    return NextResponse.json(updatedUser, {
      status: 200,
    })
  } catch (error) {
    console.error('Error in POST request for updateScore: ', error)
    return NextResponse.json(
      {error: error.message},
      {
        status: 500,
      },
    )
  }
}

export async function GET(request) {
  try {
    // Extract user ID from the query parameters
    const userId = request.nextUrl.searchParams.get('userId')

    if (!userId) {
      return NextResponse.json(
        {error: 'User ID is required'},
        {
          status: 400,
        },
      )
    }

    // Call your helper function to get the scores
    const scores = await getScores(userId)

    // Return the scores in the response
    return NextResponse.json(scores, {
      status: 200,
    })
  } catch (error) {
    console.error('Error in GET request for getScores: ', error)
    return NextResponse.json(
      {error: error.message},
      {
        status: 500,
      },
    )
  }
}

```

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