# Project export: Prompt

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: Share 30 second doodles with your friends and the world! Everyone receives the same prompt each day, and gets 30 seconds to draw it!
- Devpost: https://devpost.com/software/prompt-m18l4k
- GitHub: https://github.com/KartavyaSharma/prompt
- Demo: https://prompt-woad.vercel.app/
- Team: 4 GitHub contributor(s) — Kev Wang (25 commits), Rahul Shah (17 commits), JackyJerkyYt (15 commits), Kartavya Sharma (9 commits)

## Devpost submission (written by the team)

### Inspiration

We started off with one goal: to build an app that was super fun to use, and enables people to connect with each others in a novel way! This goal got us thinking about the apps and games that have the mechanic of bringing users back on a daily interval. Examples of this include BeReal, which asks users to upload a picture within 2 minutes of a notification every day, and Wordle, a word game where the guess word resets every day. This mechanic reduces the friction to return and update your friends. Another thing we thought about is what people enjoyed doing online. One thing that was univerally loved was skribbl.io, an online drawing game. Thus, we decided to center our app around doodling!

### What it does

Every day, the app has a new prompt for all users to draw. When you open the app and press "start", the prompt is revealed and you get 30 seconds to draw it! Once the time is up, the doodle is automatically uploaded and shared with friends (and the world)! In the Gallery page, you can view and "heart" doodles from others. You swipe through the doodles in an interface akin to Tiktok or flashcards. In the Live page, you can view a live-updating stream of doodles submitted from around the world!

### How we built it

We needed reactive components that can easily update as the database updates, as well as the ability to easily write and deploy a full-stack app. Thus, Convex was a perfect fit! On the frontend, we used React with Vite, and its deployment was done via Vercel. In addition, we mocked up designs with Figma, which helped greatly with establishing a good-looking, consistent design for the website.

### Challenges we ran into

Some of our features, such as Gallery, required interacting with beta features on Convex, notably paginated queries. We encountered several bugs and discrepancies between the documentation and the implemented library. The Convex team has been extremely helpful for getting this app online, and we could not have made this without them! Another large challenge was building the frontend to be responsive and work well across different screen sizes and modes of interaction (mouse vs touch). For much of us, it was the first time working on things at this level of difficulty on the frontend side. However, the biggest challenge we faced was the fact that someone we planned on partnering with, who we relied on to have frontend development experience, actually found out he got his application rejected when he tried to enter on Saturday! This put a wrench in our gears, but fortunately we were able to quickly find Rahul, who was looking for a group to join, and he was able to help us with the frontend code.

### Accomplishments we're proud of

The fact that it actually works is something of a big feat already, and we are very very proud of that! We were working with a lot of technologies we were unfamiliar with, such as Convex, React, and CSS. Any one of the parts failing would mean that our project would not work at all. Somehow, we managed to get all of the different moving parts to work and work together! We are especially proud of how the app looks! The design went through many iterations, and none of us have much experience in UI/UX design. Moreover, we managed to implement the design, barring some non-essential aesthetic features, completely in React.

### What we learned

We all learned a lot about frontend development, UI/UX design, and working with the Convex library. But more importantly, this was the first time we competed in a hackathon for two of us, and we learned that they are a lot of fun! Getting the entire hackathon experience was very worthwhile, despite the headaches and the sleep deprivation.

### What's next

Many other hackers we talked to said they would actually use this app if it were to actually be published, which is awesome. We hope to polish this app up (add security, design for scalability, incorporate more features) and deploy it to the world soon!

## README (from the GitHub repository)

# Convex Hack Pack

Hello, hackers! Welcome to [Convex](https://convex.dev), a full-featured backend
platform you can use to rapidly prototype any hackathon app you can think of!

## So you want to build an app, ASAP

If you're building an app for a hackathon, there's no time to waste! Convex is
the perfect partner for hackers on a deadline because:

- you get a cloud-hosted database & serverless backend for free
- your frontend clients get automatic, realtime data updates
- you can flexibly change your database schema as your product evolves
- you get file storage, text- and vector-based search, cron jobs, and lots more
  out of the box

Whatever your idea is, Convex can help you make it happen!

## What is this hack pack?

This repository is your starting point for getting hacking with Convex. It
includes:

- A simple Convex demo app to demonstrate the basics
- Hands-on exercises to get you started (below)
- Lots of further reading links & resources to keep you going (below)

## Before you begin

- Sign up for a free Convex account at [convex.dev](https://convex.dev)
- [highly recommended] Sign up for a [Github](https://github.com) account if you
  don't have one already
- Get your development environment set up:
  - Make sure you have [git](https://github.com/git-guides/install-git)
    installed, or install it if needed
  - Install/update [Node.js](https://nodejs.org/en/download) (which includes
    [npm](https://www.npmjs.com/)) to the latest LTS version
- [optional] Read up on the technologies we'll be using:
  - [JavaScript](https://developer.mozilla.org/en-US/docs/Learn/JavaScript) and
    [TypeScript](https://www.typescriptlang.org/) programming languages
  - [React](https://react.dev/) JS/TS frontend framework
  - [Vite](https://vitejs.dev/) JS/TS build & development tool
  - and of course, [Convex](https://docs.convex.dev)!

## Install the hack pack

- Download a local copy of this repository from Github using `git`:

  ```
  git clone https://github.com/get-convex/convex-hack-pack.git
  ```

  (Note: if you prefer, you can also clone the repo with the
  [Github CLI](https://cli.github.com/) or
  [download a ZIP file](https://github.com/get-convex/convex-hack-pack/archive/refs/heads/main.zip)
  of the contents)

- Install the project dependencies:

  ```
  cd convex-hack-pack
  npm install
  ```

- Run the following command, then follow the prompts to configure a new project
  in your Convex account and start the development server for the demo site:

  ```
  npm run dev
  ```

- You should see the demo app automatically open in your web browser (if not,
  navigate to [localhost:5173](http://localhost:5173))
- In the demo app, type in a new app idea and click "Save", and/or click the
  "Generate a random app idea" button, and you should see the ideas appear!

You may have noticed that "Include random ideas" checkbox in the demo app
doesn't work! Don't worry, we're going to fix it. But before we do, let's take a
closer look at our data in the Convex dashboard.

---

# Exercises

## Exercise 1: Update your data

- In the browser, navigate to the `convex-hack-pack` project in your
  [Convex dashboard](https://dashboard.convex.dev) (if it didn't open
  automatically) - you'll be taken to the 'Data' tab where you should see the
  `ideas` table and any documents inside it
- Edit data:
  - Double-click in the 'idea' field of any document and edit the text
  - Back in your demo app, you'll see the text has automatically updated!
- Add new data:
  - In the dashboard `ideas` table, click the "Add documents" button on the top
    right
  - In the document editor that opens, type a new app idea to fill out the
    `idea` property (e.g. `"A brainstorming app for developers"` - in quotes,
    because it's a string value)
  - Click "Save" to save the new document
  - In both the dashboard and the demo app, you should now see your new idea!

But we're not done yet - that "Include random ideas" checkbox in our app still
doesn't work! Let's fix that.

## Exercise 2: Update your backend

- Update your `listIdeas` function:

  - In your code editor, open `convex/myFunctions.ts`
  - In the `listIdeas` query function, add an additional argument named
    `includeRandom` to the `args` object, whose value is a boolean
    (`v.boolean()`). The `args` object should now look like this:

    ```js
    args: {
        includeRandom: v.boolean()
    },
    ```

  - In the `handler` function, add an `if` conditional based on the value of
    `args.includeRandom`: if true, return the same query results as before, but
    if false, filter the data to only return documents where the `random` field
    is _not_ equal to `true`, like so:

    ```js
    handler: async (ctx, args) => {
      if (args.includeRandom) {
        return await ctx.db.query("ideas").collect(); // Returns all documents in the 'ideas' table
      } else {
        return await ctx.db
         .query("ideas")
         .filter((q) => q.neq(q.field("random"), true)) // Only returns documents whose 'random' field is not equal to `true`
         .collect();
      }
    },
    ```

  - Save the `myFunctions.ts` file, and in the terminal where you have
    `npm run dev` running, you should see a log line that says "Convex functions
    ready!" (this means your new function code has been successfully deployed)

- Test out your updated function
  - Go to your [Convex dashboard](https://dashboard.convex.dev), navigate to the
    "Functions" tab (`</>`) and open `myFunctions:listIdeas`. You should now see
    the new version of your code there!
  - Click the "Run function" button to try out your new function in the
    dashboard
  - In the "Arguments" panel, edit the value of `includeRandom` and verify that
    you see the correct results in the "Query outcome" panel!

Great, we've confirmed in the Convex dashboard that our backend change was
successful!

But now when visiting [localhost:5173](http://localhost:5173) you'll see a whole
lot of nothing. That's because our backend function change broke the frontend
code that invokes that function! Let's fix it and get our ideas back.

## Exercise 3: Update your frontend

- In your code editor, open `src/App.tsx`
- In the `App` function, find the line near the top where `ideas` is defined
  using the `useQuery()` hook to call the `api.myFunctions.listIdeas` query
  function
- The `useQuery` hook can take an optional second argument, an `args` object
  that matches the `args` validator of the given query function. Update the call
  to `useQuery()` to pass `{ includeRandom }` as the second argument, like so:
  ```js
  const ideas = useQuery(api.myFunctions.listIdeas, { includeRandom })
  ```
- Now, not only are the ideas displaying properly, but when you (un)check the
  "Include random ideas" checkbox you should see the results update accordingly!

## Bonus: Challenge Exercises

If you've got extra time, try your hand at implementing some new features for
the app!

### Challenge 1: Delete idea button

Currently, there is no way for a user to delete an idea from the page. Your
challenge is to add a button that fixes that!

Hints:

- In `convex/myFunctions.ts` you'll need to create a new mutation function
  `deleteIdea`, which
  [deletes a document](https://docs.convex.dev/database/writing-data#deleting-documents)
  from the `ideas` table using its
  [Convex document ID](https://docs.convex.dev/database/document-ids)
- In `src/App.tsx`, you'll need the `useMutation` hook to invoke your new
  `deleteIdea` function as needed from the frontend
- In `src/App.tsx`, you can add a new button using the `Button` component (see
  the "Generate a random app idea" button for an example)

### Challenge 2: Prevent duplicates

At the moment, the app doesn't prevent you from adding the same idea twice (try
it!), so we might save duplicate ideas to the database. Your challenge is to fix
that by making sure that we check for duplicates before saving a new idea!

Hints:

- In `convex/myFun

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 29 recognized source files, 196 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
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (37 of 37)

```
_gitignore
.eslintrc.cjs
.gitignore
components.json
convex/_generated/api.d.ts
convex/_generated/api.js
convex/_generated/dataModel.d.ts
convex/_generated/server.d.ts
convex/_generated/server.js
convex/functions.ts
convex/http.ts
convex/README.md
convex/schema.ts
convex/tsconfig.json
index.html
package.json
postcss.config.js
README.md
src/App.css
src/App.tsx
src/components/button.css
src/components/button.tsx
src/components/modal.css
src/components/modal.tsx
src/main.tsx
src/pages/devpost.md
src/pages/Draw.css
src/pages/Draw.tsx
src/pages/Gallery.css
src/pages/Gallery.tsx
src/pages/Live.css
src/pages/Live.tsx
src/vite-env.d.ts
tsconfig.json
tsconfig.node.json
vercel.json
vite.config.ts
```

### Dependencies

- package.json: @hookform/resolvers@^3.3.2, @radix-ui/react-checkbox@^1.0.4, @radix-ui/react-icons@^1.3.0, @radix-ui/react-label@^2.0.2, @radix-ui/react-slot@^1.0.2, @react-spring/web@^9.7.3, @types/node@^20.7.0, @types/react@^18.2.21, @types/react-dom@^18.2.7, @types/react-modal@^3.16.2, @typescript-eslint/eslint-plugin@^6.7.0, @typescript-eslint/parser@^6.7.0, @vitejs/plugin-react@^4.0.4, autoprefixer@^10.4.16, class-variance-authority@^0.7.0, clsx@^2.0.0, cmdk@^0.2.0, convex@^1.4.1, date-fns@^2.30.0, eslint@^8.49.0, eslint-plugin-react-hooks@^4.6.0, eslint-plugin-react-refresh@^0.4.3, npm-run-all@^4.1.5, postcss@^8.4.30, react@^18.2.0, react-day-picker@^8.9.1, react-dom@^18.2.0, react-hook-form@^7.47.0, react-modal@^3.16.1, react-router-dom@^6.17.0, typescript@^5.2.2, vite@^4.4.9, zod@^3.22.4

### Recent commits (newest first)

- Actually fix QR location
- move qr sample to public
- Add devpost and fix live
- kurt live page changes latest
- kurt live page changes latest
- kurt live page changes latest
- kurt changes live
- Add better gallery animations
- fixed the live feed backend
- Fix home page text
- Add vercel config
- newest img's first
- Merge branch 'main' of github.com:KartavyaSharma/prompt
- add logo
- Final (i believe) changes to drawing sizes
- Ok this css should work
- Modify css slightly
- Play around with gallery changes for mobile
- Increase margin for mobile
- Fix for mobile

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

### src/pages/devpost.md

```markdown
## Inspiration

We started off with one goal: to build an app that was super fun to use, and enables people to connect with each others in a novel way! This goal got us thinking about the apps and games that have the mechanic of bringing users back on a daily interval. Examples of this include BeReal, which asks users to upload a picture within 2 minutes of a notification every day, and Wordle, a word game where the guess word resets every day. This mechanic reduces the friction to return and update your friends.

Another thing we thought about is what people enjoyed doing online. One thing that was  univerally loved was [skribbl.io](skribbl.io), an online drawing game. Thus, we decided to center our app around doodling!

## What it does

Every day, the app has a new prompt for all users to draw. When you open the app and press "start", the prompt is revealed and you get 30 seconds to draw it! Once the time is up, the doodle is automatically uploaded and shared with friends (and the world)!

In the Gallery page, you can view and "heart" doodles from others. You swipe through the doodles in an interface akin to Tiktok or flashcards. 

In the Live page, you can view a live-updating stream of doodles submitted from around the world!

## How we built it

We needed reactive components that can easily update as the database updates, as well as the ability to easily write and deploy a full-stack app. Thus, Convex was a perfect fit! On the frontend, we used React with Vite, and its deployment was done via Vercel.

In addition, we mocked up designs with Figma, which helped greatly with establishing a good-looking, consistent design for the website.

## Challenges we ran into

Some of our features, such as Gallery, required interacting with beta features on Convex, notably paginated queries. We encountered several bugs and discrepancies between the documentation and the implemented library. The Convex team has been extremely helpful for getting this app online, and we could not have made this without them!

Another large challenge was building the frontend to be responsive and work well across different screen sizes and modes of interaction (mouse vs touch). For much of us, it was the first time working on things at this level of difficulty on the frontend side.

However, the biggest challenge we faced was the fact that someone we planned on partnering with, who we relied on to have frontend development experience, actually found out he got his application rejected when he tried to enter on Saturday! This put a wrench in our gears, but fortunately we were able to quickly find Rahul, who was looking for a group to join, and he was able to help us with the frontend code.

## Accomplishments that we're proud of

The fact that it actually works is something of a big feat already, and we are very very proud of that!

We were working with a lot of technologies we were unfamiliar with, such as Convex, React, and CSS. Any one of the parts failing would mean that o
[truncated — 1022 more characters]
```

### package.json

```
{
  "name": "convex-hack-pack",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "npm-run-all dev:init --parallel dev:frontend dev:backend",
    "dev:frontend": "vite --open",
    "dev:backend": "convex dev",
    "dev:init": "convex dev --until-success --typecheck=disable && convex dashboard",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx ",
    "preview": "vite preview"
  },
  "dependencies": {
    "@hookform/resolvers": "^3.3.2",
    "@radix-ui/react-checkbox": "^1.0.4",
    "@radix-ui/react-icons": "^1.3.0",
    "@radix-ui/react-label": "^2.0.2",
    "@radix-ui/react-slot": "^1.0.2",
    "@react-spring/web": "^9.7.3",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.0.0",
    "cmdk": "^0.2.0",
    "convex": "^1.4.1",
    "date-fns": "^2.30.0",
    "react": "^18.2.0",
    "react-day-picker": "^8.9.1",
    "react-dom": "^18.2.0",
    "react-hook-form": "^7.47.0",
    "react-modal": "^3.16.1",
    "react-router-dom": "^6.17.0",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@types/node": "^20.7.0",
    "@types/react": "^18.2.21",
    "@types/react-dom": "^18.2.7",
    "@types/react-modal": "^3.16.2",
    "@typescript-eslint/eslint-plugin": "^6.7.0",
    "@typescript-eslint/parser": "^6.7.0",
    "@vitejs/plugin-react": "^4.0.4",
    "autoprefixer": "^10.4.16",
    "eslint": "^8.49.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.3",
    "npm-run-all": "^4.1.5",
    "postcss": "^8.4.30",
    "typescript": "^5.2.2",
    "vite": "^4.4.9"
  }
}

```

### src/main.tsx

```typescript
import { ConvexProvider, ConvexReactClient } from "convex/react";
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);

ReactDOM.createRoot(document.getElementById("root")!).render(
  <BrowserRouter>
    <ConvexProvider client={convex}>
      <App />
    </ConvexProvider>
  </BrowserRouter>
);

```

### src/App.tsx

```typescript
import { useState } from "react";
import "./App.css";
import { Route, Routes } from "react-router-dom";
import { Link } from "react-router-dom";

import Draw from './pages/Draw';
import Gallery from './pages/Gallery';
import Live from './pages/Live';

const Navbar = () => {
  const [menuOpen, setMenuOpen] = useState(false);

  return (
    <nav>
      <div id="navbar">
        <Link id="navLogo" to="/">
          <img src="./logo.svg" alt="Logo"/>
        </Link>

        <div id="navLinks">
          <Link to="/leaderboard" className="navLink" onClick={() => setMenuOpen(false)}>Leaderboard</Link>
          <Link to="/gallery" className="navLink" onClick={() => setMenuOpen(false)}>Gallery</Link>
          <Link to="/live" className="navLink" onClick={() => setMenuOpen(false)}>Live</Link>
        </div>
        <img id="navHamburger" src={menuOpen ? "./x.svg" : "./hamburger.svg"} alt="Menu Toggle" onClick={() => setMenuOpen(!menuOpen)} />
      </div>

      { menuOpen && 
        <div id="navMenu">
          <Link to="/leaderboard" className="navLink" onClick={() => setMenuOpen(false)}>Leaderboard</Link>
          <Link to="/gallery" className="navLink" onClick={() => setMenuOpen(false)}>Gallery</Link>
          <Link to="/live" className="navLink" onClick={() => setMenuOpen(false)}>Live</Link>
        </div>
      }
    </nav>
  )
};

function App() {
  return (
    <>
      <Navbar />
      <main>
        <Routes>
          <Route path="/" element={<Draw/>} />
          <Route path="/live" element={<Live/>} />
          <Route path="/gallery" element={<Gallery/>} />
        </Routes>
      </main>
    </>
  );
}

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.4.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: {
    autoprefixer: {},
  },
}

```

### vite.config.ts

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});

```

### index.html

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Convex Hack Pack</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" />

```

### convex/schema.ts

```typescript
// OPTIONAL: Rename this file to `schema.ts` to declare the shape
// of the data in your database.
// See https://docs.convex.dev/database/schemas.

import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema(
  {
    doodles: defineTable({
      body: v.string(),
      author: v.string(),
      format: v.string()
    })
  },
);
```

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