# Project export: TimeSync.ai

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: TreeHacks 2024
- Tagline: Say goodbye to manually scheduling huge meetings, powered by AI
- Devpost: https://devpost.com/software/timesync-4x8pub
- GitHub: https://github.com/ibrahimeatspie/timesync
- Video: https://www.youtube.com/embed/75U9pDMDCRU?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — Ibrahim Shah (9 commits), Gabriel (7 commits), loccx (5 commits)

## Devpost submission (written by the team)

### Inspiration

As aspiring startup founders, we are always looking for pain points and problems to solve. As such, we decided to ask our professors for the problems they faced. But no matter how hard we tried to meet with them, we could never get a meeting scheduled due how difficult it is to coordinate meetings across 4+ unique schedules. It was only 5 weeks later that we actually got to meet with our professor, but more importantly, had an epiphany to use AI to do annoying, tricky scheduling for us. And the more we asked around, we found that professors specifically hate having to schedule 10+ people research meetings because of how hard it is to schedule meetings.

### What it does

Picture this: you are a professor and you need to meet with 10+ of your research students (our prof has this problem). Just tell TimeSync to schedule a meeting with John, Sarah, Brady, etc.. and it'll automatically find the best time available and put it on all of your Google calendars. Seamlessly. No more finicky when2meets or horrid email chains.

### How we built it

Our project was constructed from the ground up using a robust stack of modern web technologies. We chose Next.js as the foundation for its SSR capabilities and seamless integration with React, enhancing the overall user experience and performance. For our database and ORM, we integrated Prisma with Supabase, leveraging Supabase's scalable backend services and built-in authentication to secure and manage user data efficiently. The core feature, vector search, was implemented using Convex, enabling us to perform sophisticated queries to find optimal scheduling slots based on user availability and preferences. This choice allowed for a more dynamic and intelligent matching process, significantly improving the scheduling experience. To ensure a high degree of interoperability and maintainability across our server and client-side code, we utilized fully typed server actions. This approach guaranteed type safety and facilitated easier development and debugging by making our codebase more predictable and less prone to errors. Additionally, we embraced the power of AI in our development process by using v0.dev for generating UI components. This innovative tool accelerated our UI development, allowing us to quickly prototype and refine our application's interface based on AI-generated suggestions, which were then customized to fit our specific needs and design guidelines. By combining these technologies, we built a comprehensive solution from scratch, designed to address the complexities of scheduling for users by offering a seamless, integrated experience that leverages the best of modern web development and AI innovations.

### Challenges we ran into

We had lots of issues with configuration. Whether that be through being banned from AWS on Stanford’s public wifi, or having issue setting up authentication with Supabase, many hurdles caused us to lose massive amounts of time. With Supabase specifically, we had trouble accessing our user session on both the frontend as well as the backend. Additionally, this was our first time using OpenAI’s function calling functionality. As such, we only found one small example by OpenAI on how to implement function calling, but besides that, there was nothing, and we had to try many different config files to get it working properly.

### Accomplishments we're proud of

We are proud of how simple our product is. Just tell it to schedule a meeting for you, even if its with 30 people, and it’ll do it. Our UI is clean and our product is easy to use, and most importantly, it solves a problem we and many others have. On the technical side of things, we thought that it was amazing how our LLM is able to interact with all aspects of our app; whether it be our frontend talking to our user, or literally querying our backend and extracting Google provider tokens to create calendar events for our users. We never anticipated that the LLM could reach outside of its container and interact on the full stack.

### What we learned

One of the main things we learned was how to work with vector embeddings. It was really cool to apply a mathematical side of AI to our calendar data in order to find the best We learned a lot about how to work with LLM. Specifically, getting an LLM to extract pieces of data from what our user types, and feeding it into a function that we can use to actually schedule meetings was cool. Another thing we learned was how to get an LLM to call functions for us and to query our data for us. We wanted it to be so easy for our users to schedule meetings that we want them to just mention the names of the individuals who they want to meet with, and our LLM will be able to find those people in our database and access their google calendars for them, scheduling meetings accordingly.

### What's next

for TimeSync In the future, we would like to fine tune our model in order to understand more scheduling prompts from users. Additionally, we would like to add a Stripe integration in order to accept payments, as we can see this being a Software as a Service startup, competing with the likes of Calendly.

## 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.tsx`. 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: 50 recognized source files, 93 KB.
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Supabase (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- PostgreSQL (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (60 of 60)

```
.eslintrc.json
.gitignore
bun.lockb
components.json
convex.json
next.config.mjs
package.json
postcss.config.js
prisma/migrations/20240217192746_init/migration.sql
prisma/migrations/migration_lock.toml
prisma/schema.prisma
prisma/seed.ts
README.md
src/actions.ts
src/app/404.tsx
src/app/auth/confirm/route.ts
src/app/ConvexClientProvider.tsx
src/app/dashboard/calendar/page.tsx
src/app/dashboard/layout.tsx
src/app/dashboard/meetings/Meetings.tsx
src/app/dashboard/meetings/page.tsx
src/app/dashboard/org/JoinOrganizations.tsx
src/app/dashboard/org/MyOrganizations.tsx
src/app/dashboard/org/page.tsx
src/app/dashboard/page.tsx
src/app/dashboard/people/page.tsx
src/app/dashboard/UserButton.tsx
src/app/error/page.tsx
src/app/globals.css
src/app/layout.tsx
src/app/page.tsx
src/app/private/page.tsx
src/components/chat_box.tsx
src/components/chat_interface.tsx
src/components/ui/avatar.tsx
src/components/ui/button.tsx
src/components/ui/calendar.tsx
src/components/ui/card.tsx
src/components/ui/dropdown-menu.tsx
src/components/ui/form.tsx
src/components/ui/input.tsx
src/components/ui/label.tsx
src/components/ui/popover.tsx
src/components/ui/separator.tsx
src/convex/_generated/api.d.ts
src/convex/_generated/api.js
src/convex/_generated/dataModel.d.ts
src/convex/_generated/server.d.ts
src/convex/_generated/server.js
src/convex/calendars.ts
src/convex/schema.ts
src/lib/core.ts
src/lib/db/index.ts
src/lib/openai.ts
src/lib/supabase/client.ts
src/lib/supabase/server.ts
src/lib/utils.ts
src/middleware.ts
tailwind.config.ts
tsconfig.json
```

### Dependencies

- package.json: @air/react-drag-to-select@^5.0.8, @hookform/resolvers@^3.3.4, @prisma/client@5.9.1, @radix-ui/react-avatar@^1.0.4, @radix-ui/react-dropdown-menu@^2.0.6, @radix-ui/react-label@^2.0.2, @radix-ui/react-popover@^1.0.7, @radix-ui/react-separator@^1.0.3, @radix-ui/react-slot@^1.0.2, @supabase/auth-helpers-nextjs@^0.9.0, @supabase/auth-helpers-react@^0.4.2, @supabase/ssr@^0.1.0, @supabase/supabase-js@^2.39.6, @types/node@^20, @types/react@^18, @types/react-datepicker@^6.0.1, @types/react-dom@^18, autoprefixer@^10.0.1, class-variance-authority@^0.7.0, clsx@^2.1.0, convex@^1.9.0, core-js@^3.36.0, date-fns@^3.3.1, eslint@^8, eslint-config-next@14.1.0, lucide-react@^0.331.0, next@14.1.0, next-safe-action@^6.1.0, openai@^4.28.0, postcss@^8, prisma@5.9.1, react@^18, react-datepicker@^6.1.0, react-day-picker@^8.10.0, react-dom@^18, react-dropdown-menu@^0.0.2, react-hook-form@^7.50.1, react-hot-toast@^2.4.1, react-schedule-selector@^3.0.2, styled-components@^6.1.8, tailwind-merge@^2.2.1, tailwindcss@^3.3.0, tailwindcss-animate@^1.0.7, typescript@^5, v0@^2.1.1, zod@^3.22.4

### Recent commits (newest first)

- finalize
- feat: various tweaks and fixes
- mathcing main to be ibrahim
- finished chat bot with prisma selection
- making ibrahim match main
- feat: convex vector embedding search
- making ibrahim match main
- fix calendar
- clerk to supa
- remove export metadata
- baseline LLM done
- baseline LLM done
- deprecated calendar
- calendar component done
- merge conflict bun
- Merge branch 'main' of github.com:ibrahimeatspie/caliv4 into key
- Calendar
- feat: add ui and pages
- zact added
- init

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

### package.json

```
{
  "name": "caliv4",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "db:seed": "prisma db seed",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@air/react-drag-to-select": "^5.0.8",
    "@hookform/resolvers": "^3.3.4",
    "@prisma/client": "5.9.1",
    "@radix-ui/react-avatar": "^1.0.4",
    "@radix-ui/react-dropdown-menu": "^2.0.6",
    "@radix-ui/react-label": "^2.0.2",
    "@radix-ui/react-popover": "^1.0.7",
    "@radix-ui/react-separator": "^1.0.3",
    "@radix-ui/react-slot": "^1.0.2",
    "@supabase/auth-helpers-nextjs": "^0.9.0",
    "@supabase/auth-helpers-react": "^0.4.2",
    "@supabase/ssr": "^0.1.0",
    "@supabase/supabase-js": "^2.39.6",
    "@types/react-datepicker": "^6.0.1",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.0",
    "convex": "^1.9.0",
    "core-js": "^3.36.0",
    "date-fns": "^3.3.1",
    "lucide-react": "^0.331.0",
    "next": "14.1.0",
    "next-safe-action": "^6.1.0",
    "openai": "^4.28.0",
    "react": "^18",
    "react-datepicker": "^6.1.0",
    "react-day-picker": "^8.10.0",
    "react-dom": "^18",
    "react-dropdown-menu": "^0.0.2",
    "react-hook-form": "^7.50.1",
    "react-hot-toast": "^2.4.1",
    "react-schedule-selector": "^3.0.2",
    "styled-components": "^6.1.8",
    "tailwind-merge": "^2.2.1",
    "tailwindcss-animate": "^1.0.7",
    "v0": "^2.1.1",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "autoprefixer": "^10.0.1",
    "eslint": "^8",
    "eslint-config-next": "14.1.0",
    "postcss": "^8",
    "prisma": "5.9.1",
    "tailwindcss": "^3.3.0",
    "typescript": "^5"
  },
  "prisma": {
    "seed": "bun ./prisma/seed.ts"
  }
}

```

### src/app/layout.tsx

```typescript
import "./globals.css";
import { Inter } from "next/font/google";
import { Toaster } from "react-hot-toast";
import ConvexClientProvider from "./ConvexClientProvider";
const inter = Inter({ subsets: ["latin"] });

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Toaster />
        <ConvexClientProvider>{children}</ConvexClientProvider>
      </body>
    </html>
  );
}

```

### src/app/page.tsx

```typescript
import Link from "next/link";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import {
  BellIcon,
  CalendarCheckIcon,
  CalendarPlusIcon,
  PhoneCallIcon,
} from "lucide-react";

export default function Page() {
  return (
    <div className="flex flex-col min-h-[100dvh]">
      <header className="px-4 lg:px-6 py-4 lg:py-6 flex items-center">
        <div className="flex items-center space-x-2">
          <img
            alt="Logo"
            className="rounded-full"
            height="40"
            src="/placeholder.png"
            style={{
              aspectRatio: "40/40",
              objectFit: "cover",
            }}
            width="40"
          />
          <span className="font-semibold">TimeSync Inc</span>
        </div>
        <nav className="ml-auto flex gap-4 sm:gap-6">
          <Link
            className="text-sm font-medium hover:underline underline-offset-4"
            href="#"
          >
            Features
          </Link>
          <Link
            className="text-sm font-medium hover:underline underline-offset-4"
            href="#"
          >
            Pricing
          </Link>
          <Link
            className="text-sm font-medium hover:underline underline-offset-4"
            href="#"
          >
            About
          </Link>
          <Link
            className="text-sm font-medium hover:underline underline-offset-4"
            href="#"
          >
            Contact
          </Link>
        </nav>
      </header>
      <main className="flex-1">
        <section className="w-full py-12 md:py-24 lg:py-32">
          <div className="container flex flex-col items-center justify-center gap-4 px-4 md:px-6 text-center">
            <div className="space-y-2 text-center">
              <h1 className="text-3xl font-bold tracking-tighter sm:text-4xl md:text-5xl">
                Simple Appointments
              </h1>
              <p className="mx-auto max-w-[600px] text-gray-500 md:text-xl/relaxed lg:text-base/relaxed xl:text-xl/relaxed dark:text-gray-400">
                Easy appointment scheduling for your business. Let your
                customers book their time with you.
              </p>
            </div>
            <div className="mx-auto w-full max-w-sm space-y-2">
              <form className="flex space-x-2">
                <Input
                  className="max-w-lg flex-1"
                  placeholder="Enter your email"
                  type="email"
                />
                <Button asChild type="submit">
                  <Link href="/dashboard">Sign Up</Link>
                </Button>
              </form>
              <p className="text-xs text-gray-500 dark:text-gray-400">
                Sign up to get notified when we launch.
                <Link className="underline underline-offset-2" href="#">
                  Terms & Conditions
                </Link>
              </p>
            </div>
          </div>
        </section>
        <section className="w-full py-12 md:py-24 lg:py-32 border-t">
          <div className="container grid items-center gap-4 px-4 md:px-6">
            <div className="space-y-2">
              <h2 className="text-3xl font-bold tracking-tighter md:text-4xl/tight">
                Features
              </h2>
              <p className="max-w-[600px] text-gray-500 md:text-xl/relaxed lg:text-base/relaxed xl:text-xl/relaxed dark:text-gray-400">
                Let your team focus on shipping features instead of managing
                infrastructure with automated CI/CD.
              </p>
            </div>
            <div className="mx-auto w-full max-w-3xl grid gap-4 md:gap-6 lg:grid-cols-2">
              <div className="flex flex-col items-center space-y-2">
                <CalendarCheckIcon className="h-10 w-10" />
                <div className="space-y-2 text-center">
                  <h3 className="font-bold">Easy Scheduling</h3>
                  <p className="text-sm text-gray-500 dark:text-gray-400">
                    Customers can easily book appointments at their convenience.
                  </p>
                </div>
              </div>
              <div className="flex flex-col items-center space-y-2">
                <CalendarPlusIcon className="h-10 w-10" />
                <div className="space-y-2 text-center">
                  <h3 className="font-bold">Multiple Providers</h3>
                  <p className="text-sm text-gray-500 dark:text-gray-400">
                    Perfect for businesses with multiple service providers.
                  </p>
                </div>
              </div>
              <div className="flex flex-col items-center space-y-2">
                <BellIcon className="h-10 w-10" />
                <div className="space-y-2 text-center">
                  <h3 className="font-bold">Reminders</h3>
                  <p className="text-sm text-gray-500 dark:text-gray-400">
                    Automated reminders to reduce no-shows.
                  </p>
                </div>
              </div>
              <div className="flex flex-col items-center space-y-2">
                <PhoneCallIcon className="h-10 w-10" />
                <div className="space-y-2 text-center">
                  <h3 className="font-bold">Virtual Appointments</h3>
                  <p className="text-sm text-gray-500 dark:text-gray-400">
                    Support for video calls for remote consultations.
                  </p>
                </div>
              </div>
            </div>
          </div>
        </section>
      </main>
      <footer className="flex flex-col gap-2 sm:flex-row py-6 w-full shrink-0 items-center px-4 md:px-6 border-t">
        <nav className="flex flex-col gap-2 sm:flex-row">
          <Link
            className="text-xs hover:underline underline-offset-4 inline-block"
            href="#"
          >
            Terms of Service
          </Link>
    
[truncated — 544 more characters]
```

### src/app/error/page.tsx

```typescript
export default function ErrorPage() {
  return <p>Sorry, something went wrong</p>;
}

```

### src/app/private/page.tsx

```typescript
import { cookies } from "next/headers";
import { redirect } from "next/navigation";

import { createClient } from "@/lib/supabase/server";

export default async function PrivatePage() {
  const cookieStore = cookies();
  const supabase = createClient(cookieStore);

  const { data, error } = await supabase.auth.getUser();
  if (error || !data?.user) {
    redirect("/");
  }

  return <p>Hello {data.user.email}</p>;
}

```

### src/lib/db/index.ts

```typescript
import { PrismaClient, type Prisma } from "@prisma/client";

declare global {
  // eslint-disable-next-line no-var
  var prisma: PrismaClient | undefined;
}
const prismaOptions: Prisma.PrismaClientOptions = {
  log: ["error", "warn"],
  errorFormat: "minimal",
};

export const prisma = globalThis.prisma || new PrismaClient(prismaOptions);

if (process.env.NODE_ENV !== "production") {
  globalThis.prisma = prisma;
}

export default prisma;

```

### src/lib/supabase/server.ts

```typescript
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { type cookies } from "next/headers";

export function createClient(cookieStore: ReturnType<typeof cookies>) {
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value;
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value, ...options });
          } catch (error) {
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: "", ...options });
          } catch (error) {
            // The `delete` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  );
}

```

### src/app/dashboard/page.tsx

```typescript
"use client";

import { Button } from "@/components/ui/button";
import { useRef, useState } from "react";
import { useAction } from "next-safe-action/hooks";
import { Input } from "@/components/ui/input";
import ChatBox from "@/components/chat_box";
import { callFunction } from "@/actions";

export default function Component() {
  const [chat, setChat] = useState([
    {
      time: new Date().toLocaleTimeString(),
      text: "What can I help you schedule?",
    },
  ]);

  const inputRef = useRef<HTMLInputElement>(null);

  const { execute, result, status } = useAction(callFunction, {
    onSuccess: (data) => {
      setChat((prev) => [
        ...prev,
        {
          text: data[0]?.message.content ?? "No response",
          time: new Date().toLocaleTimeString(),
        },
      ]);
    },
  });

  return (
    <main className="flex w-full flex-1 flex-col gap-4 p-4 md:gap-8 md:p-10">
      <div className="flex gap-4 w-full flex-row">
        <div className="flex flex-col w-full max-w-2xl h-screen">
          <header className="sticky top-0 z-10">
            <div className="container px-4">
              <div className="h-14 flex items-center">
                <h1 className="text-lg font-semibold">Chat with GPT</h1>
              </div>
            </div>
          </header>
          <div className="flex-1">
            <div className="container px-4 py-6 flex flex-col gap-4">
              <div className="grid gap-2">
                {chat.map((item, index) => (
                  <div key={index}>
                    <ChatBox time={item.time} text={item.text} />
                  </div>
                ))}
              </div>
              <div className="flex gap-2">
                <Input
                  className="flex-1"
                  placeholder="Type your message here."
                  ref={inputRef}
                  onKeyDown={(e) => {
                    if (e.key === "Enter") {
                      if (inputRef.current && inputRef.current.value) {
                        let currText = inputRef.current.value;
                        setChat((prev) => [
                          ...prev,
                          { text: currText, time: Date().toString() },
                        ]);
                        execute({ stuff: inputRef.current.value });
                        inputRef.current.value = "";
                      }
                    }
                  }}
                />
                <Button
                  onClick={async () => {
                    if (inputRef.current && inputRef.current.value) {
                      let currText = inputRef.current.value;
                      setChat((prev) => [
                        ...prev,
                        { text: currText, time: Date().toString() },
                      ]);
                      execute({ stuff: inputRef.current.value });
                      inputRef.current.value = "";
                    }
                  }}
                >
                  Send
                </Button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </main>
  );
}

```

### src/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.9.0.
 * 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;

```

### src/app/dashboard/layout.tsx

```typescript
import Link from "next/link";
import {
  BellIcon,
  CalendarIcon,
  HomeIcon,
  Package2Icon,
  PackageIcon,
  SearchIcon,
  SettingsIcon,
  UsersIcon,
} from "lucide-react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { cookies } from "next/headers";
import { createClient } from "@/lib/supabase/server";
import UserButton from "./UserButton";
import prisma from "@/lib/db";

export default async function Layout({
  children,
}: Readonly<{ children: React.ReactNode }>) {
  const cookieStore = cookies();
  const supabase = createClient(cookieStore);
  const { data: userData } = await supabase.auth.getUser();
  const { data: sessionData } = await supabase.auth.getSession();

  if (sessionData?.session && sessionData.session.provider_token) {
    await prisma.profile.upsert({
      where: { supabaseId: sessionData.session.user.id },
      update: {},
      create: {
        supabaseId: sessionData.session.user.id,
        providerToken: sessionData.session.provider_token,
        email: sessionData.session.user.email,
        displayName: sessionData.session.user.user_metadata.full_name,
      },
    });
  }

  return (
    <div className="grid min-h-screen w-full lg:grid-cols-[280px_1fr]">
      <div className="hidden border-r bg-gray-100/40 lg:block dark:bg-gray-800/40">
        <div className="flex h-full max-h-screen flex-col gap-2">
          <div className="flex h-[60px] items-center border-b px-6">
            <Link className="flex items-center gap-2 font-semibold" href="/">
              <Package2Icon className="h-6 w-6" />
              <span className="">TimeSync</span>
            </Link>
            <Button className="ml-auto h-8 w-8" size="icon" variant="outline">
              <BellIcon className="h-4 w-4" />
              <span className="sr-only">Toggle notifications</span>
            </Button>
          </div>
          <div className="flex-1 overflow-auto py-2">
            <nav className="grid items-start px-4 text-sm font-medium">
              <Link
                className="flex items-center gap-3 rounded-lg px-3 py-2 text-gray-500 transition-all hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-50"
                href="/dashboard"
              >
                <HomeIcon className="h-4 w-4" />
                Home
              </Link>
              <Link
                className="flex items-center gap-3 rounded-lg px-3 py-2 text-gray-500 transition-all hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-50"
                href="/dashboard/org"
              >
                <UsersIcon className="h-4 w-4" />
                Organizations
              </Link>
              <Link
                className="flex items-center gap-3 rounded-lg px-3 py-2 text-gray-500 transition-all hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-50"
                href="/dashboard/meetings"
              >
                <PackageIcon className="h-4 w-4" />
                Meetings
              </Link>
              <Link
                className="flex items-center gap-3 rounded-lg px-3 py-2 text-gray-500 transition-all hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-50"
                href="/dashboard/calendar"
              >
                <CalendarIcon className="h-4 w-4" />
                Calendar
              </Link>
              <Link
                className="flex items-center gap-3 rounded-lg px-3 py-2 text-gray-500 transition-all hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-50"
                href="#"
              >
                <SettingsIcon className="h-4 w-4" />
                Settings
              </Link>
            </nav>
          </div>
        </div>
      </div>
      <div className="flex flex-col">
        <header className="flex h-14 lg:h-[60px] items-center gap-4 border-b bg-gray-100/40 px-6 dark:bg-gray-800/40">
          <Link className="lg:hidden" href="#">
            <Package2Icon className="w-6 h-6" />
            <span className="sr-only">Home</span>
          </Link>
          <div className="w-full flex-1">
            <form>
              <div className="relative">
                <SearchIcon className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-500 dark:text-gray-400" />
                <Input
                  className="w-full bg-white shadow-none appearance-none pl-8 md:w-2/3 lg:w-1/3 dark:bg-gray-950"
                  placeholder="Search projects..."
                  type="search"
                />
              </div>
            </form>
          </div>
          <UserButton user={userData.user} />
        </header>
        {children}
      </div>
    </div>
  );
}

```

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