# Project export: FoodieFlicks

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: Cal Hacks 10.0
- Tagline: Write food reviews, rate your experiences, upload photos, but remain private. Visible only to your circle.
- Devpost: https://devpost.com/software/foodieflicks
- GitHub: https://github.com/yipqp/calhacks23
- Demo: https://calhacks23-hor53h6sw-yipqp.vercel.app/
- Team: 2 GitHub contributor(s) — Richard (8 commits), edennle20 (3 commits)

## Devpost submission (written by the team)

### Inspiration

Have you ever wanted to post your billionth food review on Instagram but you're too embarrassed to let everyone that follows you know about it? Perhaps you just don't want to ruin someone's livelihood with your absolutely degrading review of their restaurant. Maybe you're skeptical of a restaurant's reviews. That's where FoodieFlicks comes in!

### What it does

FoodieFlicks is a website that allows users to have a space to post food pictures paired with a rating and a review, but you decide who sees these posts. This gives the website an air of privacy that most social media accounts won't have. Additionally with the benefit of a trusted collection of recommendations that you can share selectively. Our features: Posting food pictures because the phone eats first! Rating the food/restaurant from the lowest being 1 and the highest being 5, through a range slider. A diary entry or review of your food, up to you!

### How we built it

We used React and Typescript with TailwindCSS. The images displayed on the website were based off of real images of our meals or snacks to support the practicality of the app. For our backend, we used Convex. Then, we would deploy our website on Vercel.

### Challenges we ran into

As a team of two first timers, we ran into a lot of obstacles and had a learning curve to overcome. For one, this was the first time for us doing backend work and one of us was an inexperienced coder who wasn't experienced in front-end development either. Additionally, we were learning our second framework, Convex, in under 24 hours. At times, it was difficult to not feel impatient. It was extremely frustrating to figure out how to successfully authenticate user log in with Clerk.

### Accomplishments we're proud of

First and foremost, we're proud to have submitted a project for our first major Hackathon! However, our smaller accomplishments also brought us just as much joy. We were struggling on one of the main parts of our promises, which was sharing the food images. The technical problem was being able to input the image into the database. When the time had come, this small win brought us so much joy to keep pushing forward.

### What we learned

While we literally learned Convex, we also learned to persevere through our obstacles. It was not easy as a duo, but we learned to work with each other's strengths and create an application that we are extremely proud of.

### What's next

FoodieFlicks is on its path to including the collection of previous entries from the user and user authentication to ensure privacy. It will also be coming into fruition as a mobile application that's also viable as a widget! We hope to integrate more of the social features of this design, including adding comments, reactions, and fully implementing the "close circle" system.

## README (from the GitHub repository)

# CalHacks2023


## Detected evidence (automated analysis)

Indexed codebase: 26 recognized source files, 28 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
- TypeScript (language) — detected in the code
- Vercel (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (32 of 32)

```
.eslintrc.cjs
.gitignore
convex/_generated/api.d.ts
convex/_generated/api.js
convex/_generated/dataModel.d.ts
convex/_generated/server.d.ts
convex/_generated/server.js
convex/auth.config.js
convex/entries.ts
convex/README.md
convex/schema.ts
convex/tsconfig.json
convex/users.ts
index.html
package.json
postcss.config.js
README.md
src/App.css
src/App.tsx
src/components/CreateModal.tsx
src/components/DiaryContainer.tsx
src/components/DiaryEntry.tsx
src/components/MyDiary.tsx
src/components/NavBar.tsx
src/components/NavLink.tsx
src/index.css
src/main.tsx
src/vite-env.d.ts
tailwind.config.js
tsconfig.json
tsconfig.node.json
vite.config.ts
```

### Dependencies

- package.json: @clerk/clerk-react@^4.27.1, @types/node@^20.8.9, @types/react@^18.2.15, @types/react-dom@^18.2.7, @typescript-eslint/eslint-plugin@^6.0.0, @typescript-eslint/parser@^6.0.0, @vitejs/plugin-react@^4.0.3, autoprefixer@^10.4.16, convex@^1.5.1, eslint@^8.45.0, eslint-plugin-react-hooks@^4.6.0, eslint-plugin-react-refresh@^0.4.3, postcss@^8.4.31, react@^18.2.0, react-dom@^18.2.0, react-error-overlay@^6.0.9, react-router-dom@^6.17.0, tailwindcss@^3.3.5, typescript@^5.0.2, vite@^4.4.5

### Recent commits (newest first)

- Edit html title
- Edit title
- Edit create modal
- Rebuild (2)
- Rebuild
- Clean for deployment
- Set up React Routers
- Merge pull request #1 from yipqp/features/create-modal
- Merge branch 'main' into features/create-modal
- Allow user to add entry
- Design components
- Build homepage
- Initial commit

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

### package.json

```
{
  "name": "calhacks23",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "@clerk/clerk-react": "^4.27.1",
    "convex": "^1.5.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.17.0"
  },
  "devDependencies": {
    "@types/node": "^20.8.9",
    "@types/react": "^18.2.15",
    "@types/react-dom": "^18.2.7",
    "@typescript-eslint/eslint-plugin": "^6.0.0",
    "@typescript-eslint/parser": "^6.0.0",
    "@vitejs/plugin-react": "^4.0.3",
    "autoprefixer": "^10.4.16",
    "eslint": "^8.45.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.3",
    "postcss": "^8.4.31",
    "react-error-overlay": "^6.0.9",
    "tailwindcss": "^3.3.5",
    "typescript": "^5.0.2",
    "vite": "^4.4.5"
  }
}

```

### src/main.tsx

```typescript
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { ConvexReactClient } from "convex/react";
import { ClerkProvider, useAuth } from "@clerk/clerk-react";
import { ConvexProviderWithClerk } from "convex/react-clerk";
import { useNavigate } from "react-router-dom";

const convex = new ConvexReactClient(
  "https://efficient-basilisk-885.convex.cloud"
);

const clerkPubKey = process.env.REACT_APP_CLERK_PUBLISHABLE_KEY;

if (!clerkPubKey) {
  throw new Error("Missing Publishable Key");
}

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <ClerkProvider
      publishableKey={clerkPubKey}
      navigate={(to) => useNavigate()(to)}
    >
      <ConvexProviderWithClerk client={convex} useAuth={useAuth}>
        <App />
      </ConvexProviderWithClerk>
    </ClerkProvider>
  </React.StrictMode>
);

```

### src/App.tsx

```typescript
import "./App.css";
import { useState } from "react";
import NavBar from "./components/NavBar";
import NavLink from "./components/NavLink";
import DiaryContainer from "./components/DiaryContainer";
import CreateModal from "./components/CreateModal";
import {
  SignInButton,
  SignOutButton,
  SignedIn,
  SignedOut,
} from "@clerk/clerk-react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import MyDiary from "./components/MyDiary";

function App() {
  const [showCreate, setShowCreate] = useState(false);

  return (
    <BrowserRouter>
      <div className="flex flex-col bg-orange-50">
        {showCreate ? (
          <CreateModal toggleModal={setShowCreate}></CreateModal>
        ) : null}
        <NavBar>
          <a
            onClick={() => setShowCreate(true)}
            className="hover:cursor-pointer"
          >
            Create
          </a>
          <SignedIn>
            <NavLink
              name="My Diary"
              to="my-diary"
              children={undefined}
            ></NavLink>
            <NavLink name="" to="/">
              <SignOutButton />
            </NavLink>
          </SignedIn>
          <SignedOut>
            <SignInButton mode="modal" />
          </SignedOut>
        </NavBar>
        <Routes>
          <Route path="/" element={<DiaryContainer />} />
          <Route path="/my-diary/" element={<MyDiary />} />
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

```

### convex/_generated/server.js

```javascript
/* eslint-disable */
/**
 * Generated utilities for implementing server-side Convex query and mutation functions.
 *
 * THIS CODE IS AUTOMATICALLY GENERATED.
 *
 * Generated by convex@1.5.1.
 * To regenerate, run `npx convex dev`.
 * @module
 */

import {
  actionGeneric,
  httpActionGeneric,
  queryGeneric,
  mutationGeneric,
  internalActionGeneric,
  internalMutationGeneric,
  internalQueryGeneric,
} from "convex/server";

/**
 * Define a query in this Convex app's public API.
 *
 * This function will be allowed to read your Convex database and will be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const query = queryGeneric;

/**
 * Define a query that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to read from your Convex database. It will not be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const internalQuery = internalQueryGeneric;

/**
 * Define a mutation in this Convex app's public API.
 *
 * This function will be allowed to modify your Convex database and will be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const mutation = mutationGeneric;

/**
 * Define a mutation that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to modify your Convex database. It will not be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const internalMutation = internalMutationGeneric;

/**
 * Define an action in this Convex app's public API.
 *
 * An action is a function which can execute any JavaScript code, including non-deterministic
 * code and code with side-effects, like calling third-party services.
 * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
 * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
 *
 * @param func - The action. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
 */
export const action = actionGeneric;

/**
 * Define an action that is only accessible from other Convex functions (but not from the client).
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
 */
export const internalAction = internalActionGeneric;

/**
 * Define a Convex HTTP action.
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
 * as its second.
 * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
 */
export const httpAction = httpActionGeneric;

```

### postcss.config.js

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

```

### tailwind.config.js

```javascript
/** @type {import('tailwindcss').Config} */
export default {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    fontFamily: {
      'inter': ['inter', 'sans-serif']
    },
    extend: {},
  },
  plugins: [],
};

```

### vite.config.ts

```typescript
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react";

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

// })

export default defineConfig(({ command, mode }) => {
  const env = loadEnv(mode, process.cwd(), "");
  return {
    plugins: [react()],
    define: {
      "process.env.REACT_APP_CLERK_PUBLISHABLE_KEY": JSON.stringify(
        env.REACT_APP_CLERK_PUBLISHABLE_KEY
      ),
    },
  };
});

```

### index.html

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://fonts.googleapis.com/css?family=Inter:400,700&display=swap"
      rel="stylesheet"
    />
    <link
      rel="shortcut icon"
      href="./src/assets/Logo.svg"
      type="image/x-icon"
    />
    <title>FoodieFlicks</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

```

### src/vite-env.d.ts

```typescript
/// <reference types="vite/client" />

```

### src/index.css

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

```

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