main d7cd1fd2a4b6 cached
69 files
104.2 KB
30.8k tokens
43 symbols
1 requests
Download .txt
Repository: Jaaneek/t3-supabase-app-router
Branch: main
Commit: d7cd1fd2a4b6
Files: 69
Total size: 104.2 KB

Directory structure:
gitextract_wqkuwtlm/

├── .eslintrc.cjs
├── .github/
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── ci.yml
│       ├── production.yaml
│       └── staging.yaml
├── .gitignore
├── README.md
├── components.json
├── next.config.mjs
├── package.json
├── postcss.config.cjs
├── prettier.config.mjs
├── prisma/
│   └── schema.prisma
├── src/
│   ├── app/
│   │   ├── (auth)/
│   │   │   ├── _components/
│   │   │   │   └── DevLoginButtons.tsx
│   │   │   ├── layout.tsx
│   │   │   └── login/
│   │   │       └── page.tsx
│   │   ├── (authenticatedRoutes)/
│   │   │   ├── authenticated/
│   │   │   │   └── page.tsx
│   │   │   └── layout.tsx
│   │   ├── _components/
│   │   │   └── ServerDataStreaming.tsx
│   │   ├── api/
│   │   │   └── trpc/
│   │   │       └── [trpc]/
│   │   │           └── route.ts
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── FormInput/
│   │   │   └── FormInput.tsx
│   │   ├── Icons/
│   │   │   ├── Icons.tsx
│   │   │   └── index.ts
│   │   ├── Loading/
│   │   │   ├── Loading.tsx
│   │   │   └── index.ts
│   │   ├── PrefetchTRPCQuery/
│   │   │   └── PrefetchTRPCQuery.tsx
│   │   ├── PrivateRoute/
│   │   │   ├── PrivateRoute.tsx
│   │   │   └── PrivateRouteBase.tsx
│   │   ├── PublicRoute/
│   │   │   └── PublicRoute.tsx
│   │   ├── TailwindIndicator/
│   │   │   ├── TailwindIndicator.tsx
│   │   │   └── index.ts
│   │   └── ui/
│   │       ├── button.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── form.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── toast.tsx
│   │       ├── toaster.tsx
│   │       └── use-toast.ts
│   ├── env.mjs
│   ├── providers/
│   │   ├── AnalyticsProvider/
│   │   │   └── AnalyticsProvider.tsx
│   │   ├── AuthProvider/
│   │   │   ├── AuthProvider.tsx
│   │   │   ├── withPrivateRoute.tsx
│   │   │   └── withPublicRoute.tsx
│   │   └── index.tsx
│   ├── server/
│   │   ├── api/
│   │   │   ├── root.ts
│   │   │   ├── routers/
│   │   │   │   ├── auth.ts
│   │   │   │   └── example.ts
│   │   │   └── trpc.ts
│   │   ├── db.ts
│   │   └── supabase/
│   │       ├── supabaseClient.ts
│   │       └── supabaseTypes.ts
│   ├── styles/
│   │   └── globals.css
│   ├── trpc/
│   │   ├── react.tsx
│   │   ├── server.ts
│   │   └── shared.ts
│   └── utils/
│       ├── auth.ts
│       ├── cn.ts
│       └── getQueryClient.ts
├── supabase/
│   ├── .gitignore
│   ├── config.toml
│   ├── functions/
│   │   └── .vscode/
│   │       ├── extensions.json
│   │       └── settings.json
│   ├── migrations/
│   │   └── 20231004185846_initial_profiles.sql
│   └── seed.sql
├── tailwind.config.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintrc.cjs
================================================
/** @type {import("eslint").Linter.Config} */
const config = {
  root: true,
  extends: [
    "next",
    "next/core-web-vitals",
    "plugin:@typescript-eslint/recommended",
  ],
  plugins: ["react", "@typescript-eslint", "unused-imports"],
  overrides: [
    {
      files: ["*.ts", "*.tsx", "*.js"],
      parser: "@typescript-eslint/parser",
    },
  ],
  rules: {
    "@typescript-eslint/no-misused-promises": "off",
    "@typescript-eslint/consistent-type-imports": [
      "warn",
      {
        prefer: "type-imports",
        fixStyle: "inline-type-imports",
      },
    ],
    "import/prefer-default-export": "off",
    "react/jsx-filename-extension": ["warn", { extensions: [".tsx"] }],
    "react/jsx-uses-react": "off",
    "react/react-in-jsx-scope": "off",

    "react/jsx-curly-brace-presence": [
      "warn",
      { props: "never", children: "never" },
    ],
    "@typescript-eslint/no-shadow": ["error"],
    "@typescript-eslint/no-unused-vars": "off",
    "no-param-reassign": ["error"],
    "unused-imports/no-unused-imports": "error",
    "unused-imports/no-unused-vars": [
      "warn",
      {
        vars: "all",
        varsIgnorePattern: "^_",
        args: "after-used",
        argsIgnorePattern: "^_",
      },
    ],
    "padding-line-between-statements": [
      "error",
      {
        blankLine: "always",
        prev: ["const", "let", "var", "directive"],
        next: "*",
      },
      {
        blankLine: "any",
        prev: ["const", "let", "var"],
        next: ["const", "let", "var"],
      },
      { blankLine: "always", prev: "*", next: "return" },
      { blankLine: "always", prev: ["block", "block-like"], next: "*" },
      { blankLine: "always", prev: "*", next: ["function"] },
      {
        blankLine: "always",
        prev: "import",
        next: ["const", "let", "var", "function", "export", "block-like"],
      },
    ],
    "no-nested-ternary": "error",
  },
};

module.exports = config;


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Motivation and context

<!--- Why is this change required? What problem does it solve? -->
<!--- Add here link to Jira, but let this not be the only content in this section -->

## Description

<!--- Describe what you have done and why have you done it this way -->

## Steps to reproduce the behavior

## Screenshots (if appropriate)


================================================
FILE: .github/workflows/ci.yml
================================================
defaults:
  run:
    working-directory: ./

name: CI on pull request

on: [pull_request]

jobs:
  prettier-eslint:
    name: prettier-eslint
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18]
    env:
      SKIP_ENV_VALIDATION: true
    steps:
      - uses: actions/checkout@v3
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: "pnpm"
      - name: Install pnpm
        run: pnpm install
      - name: Run Lint
        run: pnpm lint


================================================
FILE: .github/workflows/production.yaml
================================================
name: Deploy Migrations to Production

on:
  push:
    branches:
      - prod
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-22.04

    env:
      SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
      SUPABASE_DB_PASSWORD: ${{ secrets.PRODUCTION_DB_PASSWORD }}
      PRODUCTION_PROJECT_ID: <YOUR_PROJECT_ID>

    steps:
      - uses: actions/checkout@v3

      - uses: supabase/setup-cli@v1

      - run: |
          supabase link --project-ref $PRODUCTION_PROJECT_ID
          supabase db push


================================================
FILE: .github/workflows/staging.yaml
================================================
name: Deploy Migrations to Staging

on:
  push:
    branches:
      - staging
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-22.04

    env:
      SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
      SUPABASE_DB_PASSWORD: ${{ secrets.STAGING_DB_PASSWORD }}
      STAGING_PROJECT_ID: <YOUR_PROJECT_ID>

    steps:
      - uses: actions/checkout@v3

      - uses: supabase/setup-cli@v1

      - run: |
          supabase link --project-ref $STAGING_PROJECT_ID
          supabase db push


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# database
/prisma/db.sqlite
/prisma/db.sqlite-journal

# next.js
/.next/
/out/
next-env.d.ts

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
.env
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo


================================================
FILE: README.md
================================================
# T3 stack + Supabase + App directory

This project is Edge ready (Vercel Edge runtime)

This is a starter project/boilerplate to start out with:

- TRPC
- App directory/router
- Prisma
- Supabase (Auth, Storage, Serverless Queries)
- Tailwind
- Edge Ready
- Umami analytics

It allows us to call database in server components through supabase-js, for client component we are using trpc+prisma due to the superior DX

## Projects using this starter

- PortfolioQuiz [Website](https://www.portfolio-quiz.com/)
- FreeLogo.dev [Website](https://www.freelogo.dev/)
- FeastQR [Repo](https://github.com/jakubczarnowski/FeastQR) [Website](https://www.feastqr.com)
- Instagram Clone [Repo](https://github.com/jakubczarnowski/instagram-clone) [Website](https://instagram-clone-eight-mu.vercel.app/)

## What's next? How do I make an app with this?

- Clone this project
- Run

```
pnpm install
```

- Copy the .env.example into .env and fill out the envs

### Initial Setup

## If you want to develop on local supabase instance, follow the steps below:

Then go to supabase/config.toml file and change your service name.

Start the database:

- supabase start
- pnpm prepare:local

## If you want to develop on remote supabase instance, follow the steps below:

Connect supabase to remote instance:

- supabase link --project-ref <_your_project_id_>
- pnpm prepare:remote

## Common steps

- Fill out environment variables
- Create Secrets on Github

#### If you want to create migrations by hand, go ahead and use this command:

- supabase migration new <_migration_name_>

Then go to supabase/migrations folder and add your SQL there.

#### If you want to make changes with studio, use

- pnpm db:diff <_migration_name_>

## Run these initial commands

Every time you change something on local instance:

```
pnpm prepare:local
```

- If you develop on cloud supabase run:

```
pnpm prepare:remote
```

- Run the project

```
pnpm dev
```

If you are not familiar with the different technologies used in this project, please refer to the respective docs.

- [Next.js app router](https://nextjs.org/docs)
- [Prisma](https://prisma.io)
- [Tailwind CSS](https://tailwindcss.com)
- [tRPC](https://trpc.io)
- [Supabase](https://supabase.com/docs)

## Authors

👤 **Milosz Jankiewicz**

- Twitter: [@twitter.com/jaaneek/](https://twitter.com/jaaneek)
- Github: [@Jaaneek](https://github.com/Jaaneek)
- LinkedIn: [@https://www.linkedin.com/in/jaaneek](https://www.linkedin.com/in/mi%C5%82osz-jankiewicz-554562168/)

👤 **Jakub Czarnowski**

- Twitter: [@twitter.com/charnowsky/](https://twitter.com/charnowsky)
- Github: [@jakubczarnowski](https://github.com/jakubczarnowski)
- LinkedIn: [@https://www.linkedin.com/in/czarnowskijakub/](https://www.linkedin.com/in/czarnowskijakub/)

## Learn More

To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources:

- [Documentation](https://create.t3.gg/)
- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials

You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome!

## How do I deploy this?

Follow deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information.


================================================
FILE: components.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "default",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "src/styles/globals.css",
    "baseColor": "slate",
    "cssVariables": true
  },
  "aliases": {
    "components": "~/components",
    "utils": "~/utils/cn"
  }
}


================================================
FILE: next.config.mjs
================================================
/**
 * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
 * for Docker builds.
 */
await import("./src/env.mjs");

/** @type {import("next").NextConfig} */
const config = {
  reactStrictMode: true,

  /**
   * If you are using `appDir` then you must comment the below `i18n` config out.
   *
   * @see https://github.com/vercel/next.js/issues/41980
   */
  i18n: {
    locales: ["en"],
    defaultLocale: "en",
  },
};

export default config;


================================================
FILE: package.json
================================================
{
  "name": "t3-supabase-app-router-enhanced",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "build": "next build",
    "db:push": "prisma db push",
    "dev": "next dev",
    "postinstall": "prisma generate",
    "start": "next start",
    "lint": "next lint && pnpm check-types",
    "check-types": "tsc --noEmit",
    "update-types-local": "npx supabase gen types typescript --local > src/server/supabase/supabaseTypes.ts",
    "update-types-remote": "npx supabase gen types typescript --project-id \"<YOUR_PROJECT_ID>\" --schema public > src/server/supabase/supabaseTypes.ts",
    "prepare:local": "pnpm update-types-local && pnpm prisma db pull && pnpm prisma generate && pnpm prisma-case-format --file prisma/schema.prisma  && pnpm prisma generate",
    "prepare:remote": "pnpm update-types-remote && pnpm prisma db pull && pnpm prisma generate && pnpm prisma-case-format --file prisma/schema.prisma  && pnpm prisma generate",
    "db:reset": "npx supabase db reset",
    "db:start": "npx supabase start",
    "db:stop": "npx supabase stop",
    "db:diff": "npx supabase db diff -f"
  },
  "dependencies": {
    "@prisma/client": "^5.6.0",
    "@radix-ui/react-dropdown-menu": "^2.0.6",
    "@radix-ui/react-label": "^2.0.2",
    "@radix-ui/react-slot": "^1.0.2",
    "@radix-ui/react-toast": "^1.1.5",
    "@supabase/auth-helpers-nextjs": "^0.8.7",
    "@supabase/supabase-js": "^2.39.0",
    "@t3-oss/env-nextjs": "^0.6.1",
    "@tanstack/react-query": "^4.36.1",
    "@tanstack/react-query-devtools": "^5.12.1",
    "@trpc/client": "^10.44.1",
    "@trpc/next": "^10.44.1",
    "@trpc/react-query": "^10.44.1",
    "@trpc/server": "^10.44.1",
    "@vercel/analytics": "^1.1.1",
    "accept-language": "^3.0.18",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.0.0",
    "lucide-react": "^0.284.0",
    "next": "^14.0.3",
    "next-themes": "^0.2.1",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-hook-form": "^7.49.2",
    "superjson": "^1.13.3",
    "tailwind-merge": "^1.14.0",
    "tailwindcss-animate": "^1.0.7",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@types/eslint": "^8.44.8",
    "@types/node": "^18.19.0",
    "@types/react": "^18.2.39",
    "@types/react-dom": "^18.2.17",
    "@typescript-eslint/eslint-plugin": "^6.13.1",
    "@typescript-eslint/parser": "^6.13.1",
    "autoprefixer": "^10.4.16",
    "eslint": "^8.54.0",
    "eslint-config-next": "^13.5.6",
    "eslint-plugin-unused-imports": "^3.0.0",
    "postcss": "^8.4.31",
    "prettier": "^3.1.0",
    "prettier-plugin-tailwindcss": "^0.5.7",
    "prisma": "^5.6.0",
    "prisma-case-format": "^1.7.3",
    "tailwindcss": "^3.3.5",
    "typescript": "^5.3.2"
  },
  "ct3aMetadata": {
    "initVersion": "7.20.2"
  },
  "packageManager": "pnpm@8.8.0"
}

================================================
FILE: postcss.config.cjs
================================================
const config = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

module.exports = config;


================================================
FILE: prettier.config.mjs
================================================
/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').options} */
const config = {
  plugins: ["prettier-plugin-tailwindcss"],
};

export default config;


================================================
FILE: prisma/schema.prisma
================================================
generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["multiSchema"]
}

datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")
  directUrl = env("DIRECT_URL")
  schemas   = ["auth", "public"]
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model AuditLogEntries {
  instanceId String?   @map("instance_id") @db.Uuid
  id         String    @id @db.Uuid
  payload    Json?     @db.Json
  createdAt  DateTime? @map("created_at") @db.Timestamptz(6)
  ipAddress  String    @default("") @map("ip_address") @db.VarChar(64)

  @@index([instanceId], map: "audit_logs_instance_id_idx")
  @@map("audit_log_entries")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model FlowState {
  id                   String              @id @db.Uuid
  userId               String?             @map("user_id") @db.Uuid
  authCode             String              @map("auth_code")
  codeChallengeMethod  CodeChallengeMethod @map("code_challenge_method")
  codeChallenge        String              @map("code_challenge")
  providerType         String              @map("provider_type")
  providerAccessToken  String?             @map("provider_access_token")
  providerRefreshToken String?             @map("provider_refresh_token")
  createdAt            DateTime?           @map("created_at") @db.Timestamptz(6)
  updatedAt            DateTime?           @map("updated_at") @db.Timestamptz(6)
  authenticationMethod String              @map("authentication_method")

  @@index([authCode], map: "idx_auth_code")
  @@index([userId, authenticationMethod], map: "idx_user_id_auth_method")
  @@map("flow_state")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model Identities {
  id           String
  userId       String    @map("user_id") @db.Uuid
  identityData Json      @map("identity_data")
  provider     String
  lastSignInAt DateTime? @map("last_sign_in_at") @db.Timestamptz(6)
  createdAt    DateTime? @map("created_at") @db.Timestamptz(6)
  updatedAt    DateTime? @map("updated_at") @db.Timestamptz(6)
  email        String?   @default(dbgenerated("lower((identity_data ->> 'email'::text))"))
  users        Users     @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@id([provider, id])
  @@index([email])
  @@index([userId])
  @@map("identities")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model Instances {
  id            String    @id @db.Uuid
  uuid          String?   @db.Uuid
  rawBaseConfig String?   @map("raw_base_config")
  createdAt     DateTime? @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime? @map("updated_at") @db.Timestamptz(6)

  @@map("instances")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model MfaAmrClaims {
  sessionId            String   @map("session_id") @db.Uuid
  createdAt            DateTime @map("created_at") @db.Timestamptz(6)
  updatedAt            DateTime @map("updated_at") @db.Timestamptz(6)
  authenticationMethod String   @map("authentication_method")
  id                   String   @id(map: "amr_id_pk") @db.Uuid
  sessions             Sessions @relation(fields: [sessionId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@unique([sessionId, authenticationMethod], map: "mfa_amr_claims_session_id_authentication_method_pkey")
  @@map("mfa_amr_claims")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model MfaChallenges {
  id         String     @id @db.Uuid
  factorId   String     @map("factor_id") @db.Uuid
  createdAt  DateTime   @map("created_at") @db.Timestamptz(6)
  verifiedAt DateTime?  @map("verified_at") @db.Timestamptz(6)
  ipAddress  String     @map("ip_address") @db.Inet
  mfaFactors MfaFactors @relation(fields: [factorId], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "mfa_challenges_auth_factor_id_fkey")

  @@map("mfa_challenges")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model MfaFactors {
  id            String          @id @db.Uuid
  userId        String          @map("user_id") @db.Uuid
  friendlyName  String?         @map("friendly_name")
  factorType    FactorType      @map("factor_type")
  status        FactorStatus    @map("status")
  createdAt     DateTime        @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime        @map("updated_at") @db.Timestamptz(6)
  secret        String?
  mfaChallenges MfaChallenges[]
  users         Users           @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([userId, createdAt], map: "factor_id_created_at_idx")
  @@map("mfa_factors")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model RefreshTokens {
  instanceId String?   @map("instance_id") @db.Uuid
  id         BigInt    @id @default(autoincrement())
  token      String?   @unique(map: "refresh_tokens_token_unique") @db.VarChar(255)
  userId     String?   @map("user_id") @db.VarChar(255)
  revoked    Boolean?
  createdAt  DateTime? @map("created_at") @db.Timestamptz(6)
  updatedAt  DateTime? @map("updated_at") @db.Timestamptz(6)
  parent     String?   @db.VarChar(255)
  sessionId  String?   @map("session_id") @db.Uuid
  sessions   Sessions? @relation(fields: [sessionId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([instanceId])
  @@index([instanceId, userId])
  @@index([parent])
  @@index([sessionId, revoked])
  @@map("refresh_tokens")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model SamlProviders {
  id               String       @id @db.Uuid
  ssoProviderId    String       @map("sso_provider_id") @db.Uuid
  entityId         String       @unique @map("entity_id")
  metadataXml      String       @map("metadata_xml")
  metadataUrl      String?      @map("metadata_url")
  attributeMapping Json?        @map("attribute_mapping")
  createdAt        DateTime?    @map("created_at") @db.Timestamptz(6)
  updatedAt        DateTime?    @map("updated_at") @db.Timestamptz(6)
  ssoProviders     SsoProviders @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([ssoProviderId])
  @@map("saml_providers")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model SamlRelayStates {
  id            String       @id @db.Uuid
  ssoProviderId String       @map("sso_provider_id") @db.Uuid
  requestId     String       @map("request_id")
  forEmail      String?      @map("for_email")
  redirectTo    String?      @map("redirect_to")
  fromIpAddress String?      @map("from_ip_address") @db.Inet
  createdAt     DateTime?    @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime?    @map("updated_at") @db.Timestamptz(6)
  ssoProviders  SsoProviders @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([forEmail])
  @@index([ssoProviderId])
  @@map("saml_relay_states")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model SchemaMigrations {
  version String @id @db.VarChar(255)

  @@map("schema_migrations")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model Sessions {
  id            String          @id @db.Uuid
  userId        String          @map("user_id") @db.Uuid
  createdAt     DateTime?       @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime?       @map("updated_at") @db.Timestamptz(6)
  factorId      String?         @map("factor_id") @db.Uuid
  aal           AalLevel?       @map("aal")
  notAfter      DateTime?       @map("not_after") @db.Timestamptz(6)
  mfaAmrClaims  MfaAmrClaims[]
  refreshTokens RefreshTokens[]
  users         Users           @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([userId])
  @@index([userId, createdAt], map: "user_id_created_at_idx")
  @@map("sessions")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info.
model SsoDomains {
  id            String       @id @db.Uuid
  ssoProviderId String       @map("sso_provider_id") @db.Uuid
  domain        String
  createdAt     DateTime?    @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime?    @map("updated_at") @db.Timestamptz(6)
  ssoProviders  SsoProviders @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([ssoProviderId])
  @@map("sso_domains")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info.
model SsoProviders {
  id              String            @id @db.Uuid
  resourceId      String?           @map("resource_id")
  createdAt       DateTime?         @map("created_at") @db.Timestamptz(6)
  updatedAt       DateTime?         @map("updated_at") @db.Timestamptz(6)
  samlProviders   SamlProviders[]
  samlRelayStates SamlRelayStates[]
  ssoDomains      SsoDomains[]

  @@map("sso_providers")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info.
model Users {
  instanceId               String?      @map("instance_id") @db.Uuid
  id                       String       @id @db.Uuid
  aud                      String?      @db.VarChar(255)
  role                     String?      @db.VarChar(255)
  email                    String?      @db.VarChar(255)
  encryptedPassword        String?      @map("encrypted_password") @db.VarChar(255)
  emailConfirmedAt         DateTime?    @map("email_confirmed_at") @db.Timestamptz(6)
  invitedAt                DateTime?    @map("invited_at") @db.Timestamptz(6)
  confirmationToken        String?      @map("confirmation_token") @db.VarChar(255)
  confirmationSentAt       DateTime?    @map("confirmation_sent_at") @db.Timestamptz(6)
  recoveryToken            String?      @map("recovery_token") @db.VarChar(255)
  recoverySentAt           DateTime?    @map("recovery_sent_at") @db.Timestamptz(6)
  emailChangeTokenNew      String?      @map("email_change_token_new") @db.VarChar(255)
  emailChange              String?      @map("email_change") @db.VarChar(255)
  emailChangeSentAt        DateTime?    @map("email_change_sent_at") @db.Timestamptz(6)
  lastSignInAt             DateTime?    @map("last_sign_in_at") @db.Timestamptz(6)
  rawAppMetaData           Json?        @map("raw_app_meta_data")
  rawUserMetaData          Json?        @map("raw_user_meta_data")
  isSuperAdmin             Boolean?     @map("is_super_admin")
  createdAt                DateTime?    @map("created_at") @db.Timestamptz(6)
  updatedAt                DateTime?    @map("updated_at") @db.Timestamptz(6)
  phone                    String?      @unique
  phoneConfirmedAt         DateTime?    @map("phone_confirmed_at") @db.Timestamptz(6)
  phoneChange              String?      @default("") @map("phone_change")
  phoneChangeToken         String?      @default("") @map("phone_change_token") @db.VarChar(255)
  phoneChangeSentAt        DateTime?    @map("phone_change_sent_at") @db.Timestamptz(6)
  confirmedAt              DateTime?    @default(dbgenerated("LEAST(email_confirmed_at, phone_confirmed_at)")) @map("confirmed_at") @db.Timestamptz(6)
  emailChangeTokenCurrent  String?      @default("") @map("email_change_token_current") @db.VarChar(255)
  emailChangeConfirmStatus Int?         @default(0) @map("email_change_confirm_status") @db.SmallInt
  bannedUntil              DateTime?    @map("banned_until") @db.Timestamptz(6)
  reauthenticationToken    String?      @default("") @map("reauthentication_token") @db.VarChar(255)
  reauthenticationSentAt   DateTime?    @map("reauthentication_sent_at") @db.Timestamptz(6)
  isSsoUser                Boolean      @default(false) @map("is_sso_user")
  deletedAt                DateTime?    @map("deleted_at") @db.Timestamptz(6)
  identities               Identities[]
  mfaFactors               MfaFactors[]
  sessions                 Sessions[]
  profiles                 Profiles?

  @@index([instanceId])
  @@map("users")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model Profiles {
  id        String    @id @db.Uuid
  updatedAt DateTime? @map("updated_at") @db.Timestamptz(6)
  email     String?   @unique
  fullName  String?   @map("full_name")
  avatarUrl String?   @map("avatar_url")
  users     Users     @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@map("profiles")
  @@schema("public")
}

enum AalLevel {
  aal1
  aal2
  aal3

  @@map("aal_level")
  @@schema("auth")
}

enum CodeChallengeMethod {
  s256
  plain

  @@map("code_challenge_method")
  @@schema("auth")
}

enum FactorStatus {
  unverified
  verified

  @@map("factor_status")
  @@schema("auth")
}

enum FactorType {
  totp
  webauthn

  @@map("factor_type")
  @@schema("auth")
}


================================================
FILE: src/app/(auth)/_components/DevLoginButtons.tsx
================================================
"use client";

import { supabase } from "~/server/supabase/supabaseClient";

const testAccounts = [
  { email: "random@gmail.com", password: "testPassword" },
  { email: "random2@gmail.com", password: "testPassword2" },
];

export const DevLoginButtons = () => {
  return (
    <div className="flex flex-col gap-2">
      {testAccounts.map((account, index) => (
        <div key={index} className="flex flex-col items-start text-white">
          <button
            onClick={() => {
              void supabase().auth.signInWithPassword(account);
            }}
          >
            Login {account.email}
          </button>
          <button
            onClick={() => {
              void supabase().auth.signUp(account);
            }}
          >
            Register {account.email}
          </button>
        </div>
      ))}
    </div>
  );
};


================================================
FILE: src/app/(auth)/layout.tsx
================================================
import { type PropsWithChildren } from "react";
import { PublicRoute } from "~/components/PublicRoute/PublicRoute";

const Layout = async ({ children }: PropsWithChildren) => {
  return <PublicRoute>{children}</PublicRoute>;
};

export default Layout;


================================================
FILE: src/app/(auth)/login/page.tsx
================================================
"use client";

import { type Provider } from "@supabase/supabase-js";
import { Icons } from "~/components/Icons";
import { Button } from "~/components/ui/button";
import { supabase } from "~/server/supabase/supabaseClient";
import { DevLoginButtons } from "../_components/DevLoginButtons";

const Page = () => {
  const signInWithOauth = (provider: Provider) => {
    void supabase().auth.signInWithOAuth({
      provider: provider,
    });
  };

  return (
    <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c]">
      <div className="container flex flex-col items-center justify-center gap-12 px-4 py-16 ">
        <div className="flex flex-col items-center gap-6">
          <h1 className="text-3xl font-extrabold tracking-tight text-white">
            Login
          </h1>

          <div className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 text-white hover:bg-white/20">
            <Button
              variant="outline"
              className="flex flex-row gap-2"
              onClick={() => {
                signInWithOauth("google");
              }}
            >
              <Icons.google width={16} />
              Google
            </Button>
            <Button
              variant="outline"
              className="flex flex-row gap-2"
              onClick={() => {
                signInWithOauth("github");
              }}
            >
              <Icons.gitHub width={16} />
              Github
            </Button>
          </div>
        </div>
      </div>
      {process.env.NEXT_PUBLIC_VERCEL_ENV !== "production" && (
        <DevLoginButtons />
      )}
    </main>
  );
};

export default Page;


================================================
FILE: src/app/(authenticatedRoutes)/authenticated/page.tsx
================================================
"use client";

import { api } from "~/trpc/react";

const AuthenticatedExample = () => {
  const { data } = api.auth.getProfile.useQuery();

  return (
    <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c]">
      <div className="container flex flex-col items-center justify-center gap-12">
        <h1 className="text-5xl font-extrabold tracking-tight text-white sm:text-[5rem]">
          Authenticated Route Example
        </h1>
        <div className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 text-white hover:bg-white/20">
          <h3 className="text-2xl font-bold">User Information</h3>
          <div className="text-lg">Email: {data?.email}</div>
          <div className="text-lg">Name: {data?.fullName}</div>
        </div>
      </div>
    </main>
  );
};

export default AuthenticatedExample;


================================================
FILE: src/app/(authenticatedRoutes)/layout.tsx
================================================
import { type PropsWithChildren } from "react";
import { PrefetchTRPCQuery } from "~/components/PrefetchTRPCQuery/PrefetchTRPCQuery";
import { PrivateRoute } from "~/components/PrivateRoute/PrivateRoute";

export default function Layout({ children }: PropsWithChildren) {
  return (
    <PrivateRoute>
      <PrefetchTRPCQuery queryName="auth.getProfile">
        {children}
      </PrefetchTRPCQuery>
    </PrivateRoute>
  );
}


================================================
FILE: src/app/_components/ServerDataStreaming.tsx
================================================
import { api } from "~/trpc/server";

export async function ServerDataStreaming() {
  const data = await api.example.hello.query({ text: "from tRPC" });

  await new Promise((resolve) => setTimeout(resolve, 1000));

  return <p className="text-2xl text-white">{data.greeting}</p>;
}


================================================
FILE: src/app/api/trpc/[trpc]/route.ts
================================================
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { type NextRequest } from "next/server";
import { env } from "~/env.mjs";

import { appRouter } from "~/server/api/root";
import { createTRPCContext } from "~/server/api/trpc";

/**
 * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
 * handling a HTTP request (e.g. when you make requests from Client Components).
 */
const createContext = async (req: NextRequest) => {
  return createTRPCContext({
    headers: req.headers,
  });
};

const handler = (req: NextRequest) =>
  fetchRequestHandler({
    endpoint: "/api/trpc",
    req,
    router: appRouter,
    createContext: () => createContext(req),
    onError:
      env.NODE_ENV === "development"
        ? ({ path, error }) => {
            console.error(
              `❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`,
            );
          }
        : undefined,
  });

export { handler as GET, handler as POST };


================================================
FILE: src/app/layout.tsx
================================================
import { TailwindIndicator } from "~/components/TailwindIndicator";
import { Providers } from "~/providers";
import "~/styles/globals.css";
import { cn } from "~/utils/cn";
import { Roboto } from "next/font/google";
import { Toaster } from "~/components/ui/toaster";
import { getServerUser } from "~/utils/auth";
import { AuthProvider } from "~/providers/AuthProvider/AuthProvider";
import { TRPCReactProvider } from "~/trpc/react";
import { headers } from "next/headers";

export const metadata = {
  title: "t3-app-dir-supabase",
  description: "Boilerplate for t3-app-dir-supabase.",
};

const font = Roboto({
  weight: ["100", "300", "400", "500", "700", "900"],
  subsets: ["latin"],
});

async function RootLayout({ children }: { children: React.ReactNode }) {
  const user = await getServerUser();

  return (
    <>
      <html lang="en">
        <head />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <body
          className={cn(
            "min-h-screen bg-background font-sans antialiased",
            font.className,
          )}
        >
          <TRPCReactProvider headers={headers()}>
            <AuthProvider {...user}>
              <Providers>
                {children}
                <Toaster />
              </Providers>
            </AuthProvider>
          </TRPCReactProvider>
          <TailwindIndicator />
        </body>
      </html>
    </>
  );
}

export default RootLayout;


================================================
FILE: src/app/page.tsx
================================================
import Link from "next/link";
import { Suspense } from "react";
import { ServerDataStreaming } from "./_components/ServerDataStreaming";

const Homepage = async () => {
  return (
    <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c]">
      <div className="container flex flex-col items-center justify-center gap-12 px-4 py-16 ">
        <div className="flex flex-col items-center gap-6">
          <h1 className="text-5xl font-extrabold tracking-tight text-white sm:text-[5rem]">
            Nextjs 14 App router starter
          </h1>
          <p className="font-extrabold tracking-tight text-white sm:text-[2rem]">
            Based On
          </p>
          <p className="text-xl font-extrabold tracking-tight text-white sm:text-[3rem]">
            Create <span className="text-[hsl(280,100%,70%)]">T3</span> App
          </p>
        </div>
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-8">
          <Link
            className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 text-white hover:bg-white/20"
            href="https://create.t3.gg/en/usage/first-steps"
            target="_blank"
          >
            <h3 className="text-2xl font-bold">First Steps →</h3>
            <div className="text-lg">
              Just the basics - Everything you need to know to set up your
              database and authentication.
            </div>
          </Link>
          <Link
            className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 text-white hover:bg-white/20"
            href="https://create.t3.gg/en/introduction"
            target="_blank"
          >
            <h3 className="text-2xl font-bold">Documentation →</h3>
            <div className="text-lg">
              Learn more about Create T3 App, the libraries it uses, and how to
              deploy it.
            </div>
          </Link>
        </div>
        <div className="flex w-full flex-1 flex-col items-center gap-4">
          <Link href="/login" className="text-lg text-white">
            Login
          </Link>
          <Link href="/authenticated" className="text-lg text-white">
            Authenticated Route Example
          </Link>
        </div>
        <Suspense
          fallback={
            <p className="text-2xl text-white">Streaming TRPC Query...</p>
          }
        >
          <ServerDataStreaming />
        </Suspense>
      </div>
    </main>
  );
};

export default Homepage;


================================================
FILE: src/components/FormInput/FormInput.tsx
================================================
import {
  FormControl,
  FormDescription,
  FormItem,
  FormLabel,
  FormMessage,
} from "../ui/form";

export type FormInputProps = {
  label?: string;
  description?: string;
  children: React.ReactNode;
};

export const FormInput = ({ label, description, children }: FormInputProps) => (
  <FormItem>
    {label && <FormLabel>{label}</FormLabel>}
    <FormControl>{children}</FormControl>
    {description && <FormDescription>{description}</FormDescription>}

    <FormMessage />
  </FormItem>
);


================================================
FILE: src/components/Icons/Icons.tsx
================================================
import {
  AlertTriangle,
  ArrowRight,
  Check,
  ChevronLeft,
  ChevronRight,
  Command,
  CreditCard,
  File,
  FileText,
  HelpCircle,
  Image,
  Laptop,
  Loader2,
  type LucideProps,
  Moon,
  MoreVertical,
  Pizza,
  Plus,
  Settings,
  SunMedium,
  Trash,
  Twitter,
  User,
  X,
  Zap,
  Palette,
  Truck,
  AlignHorizontalDistributeCenter,
  Type,
  Construction,
  type LucideIcon,
  Settings2,
  Edit,
  Star,
  Sparkle,
  MonitorSmartphone,
  Scaling,
  Sparkles,
  Download,
  Menu,
  Facebook,
  Instagram,
  Linkedin,
  Youtube,
  Lock,
  Flag,
  Languages,
} from "lucide-react";

export type Icon = LucideIcon;

export const Icons = {
  languages: Languages,
  logo: Command,
  close: X,
  spinner: Loader2,
  chevronLeft: ChevronLeft,
  chevronRight: ChevronRight,
  trash: Trash,
  post: FileText,
  page: File,
  media: Image,
  settings: Settings,
  billing: CreditCard,
  ellipsis: MoreVertical,
  add: Plus,
  warning: AlertTriangle,
  user: User,
  arrowRight: ArrowRight,
  help: HelpCircle,
  pizza: Pizza,
  sun: SunMedium,
  moon: Moon,
  laptop: Laptop,
  zap: Zap,
  palette: Palette,
  truck: Truck,
  type: Type,
  customize: AlignHorizontalDistributeCenter,
  construction: Construction,
  settings2: Settings2,
  edit: Edit,
  star: Star,
  sparkle: Sparkle,
  devices: MonitorSmartphone,
  scaling: Scaling,
  sparkles: Sparkles,
  download: Download,
  menu: Menu,
  facebook: Facebook,
  instagram: Instagram,
  linkedin: Linkedin,
  youtube: Youtube,
  lock: Lock,
  flag: Flag,
  gitHub: ({ ...props }) => (
    <svg
      aria-hidden="true"
      focusable="false"
      data-prefix="fab"
      data-icon="github"
      role="img"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 496 512"
      {...props}
    >
      <path
        fill="currentColor"
        d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"
      ></path>
    </svg>
  ),
  google: ({ ...props }: LucideProps) => (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 512 512"
      id="google"
      {...props}
    >
      <path
        fill="#fbbb00"
        d="M113.47 309.408 95.648 375.94l-65.139 1.378C11.042 341.211 0 299.9 0 256c0-42.451 10.324-82.483 28.624-117.732h.014L86.63 148.9l25.404 57.644c-5.317 15.501-8.215 32.141-8.215 49.456.002 18.792 3.406 36.797 9.651 53.408z"
      ></path>
      <path
        fill="#518ef8"
        d="M507.527 208.176C510.467 223.662 512 239.655 512 256c0 18.328-1.927 36.206-5.598 53.451-12.462 58.683-45.025 109.925-90.134 146.187l-.014-.014-73.044-3.727-10.338-64.535c29.932-17.554 53.324-45.025 65.646-77.911h-136.89V208.176h245.899z"
      ></path>
      <path
        fill="#28b446"
        d="m416.253 455.624.014.014C372.396 490.901 316.666 512 256 512c-97.491 0-182.252-54.491-225.491-134.681l82.961-67.91c21.619 57.698 77.278 98.771 142.53 98.771 28.047 0 54.323-7.582 76.87-20.818l83.383 68.262z"
      ></path>
      <path
        fill="#f14336"
        d="m419.404 58.936-82.933 67.896C313.136 112.246 285.552 103.82 256 103.82c-66.729 0-123.429 42.957-143.965 102.724l-83.397-68.276h-.014C71.23 56.123 157.06 0 256 0c62.115 0 119.068 22.126 163.404 58.936z"
      ></path>
    </svg>
  ),
  discord: ({ ...props }: LucideProps) => (
    <svg
      width="256px"
      height="199px"
      viewBox="0 0 256 199"
      preserveAspectRatio="xMidYMid"
      fill="#6169ff"
      {...props}
    >
      <g>
        <path
          d="M216.856339,16.5966031 C200.285002,8.84328665 182.566144,3.2084988 164.041564,0 C161.766523,4.11318106 159.108624,9.64549908 157.276099,14.0464379 C137.583995,11.0849896 118.072967,11.0849896 98.7430163,14.0464379 C96.9108417,9.64549908 94.1925838,4.11318106 91.8971895,0 C73.3526068,3.2084988 55.6133949,8.86399117 39.0420583,16.6376612 C5.61752293,67.146514 -3.4433191,116.400813 1.08711069,164.955721 C23.2560196,181.510915 44.7403634,191.567697 65.8621325,198.148576 C71.0772151,190.971126 75.7283628,183.341335 79.7352139,175.300261 C72.104019,172.400575 64.7949724,168.822202 57.8887866,164.667963 C59.7209612,163.310589 61.5131304,161.891452 63.2445898,160.431257 C105.36741,180.133187 151.134928,180.133187 192.754523,160.431257 C194.506336,161.891452 196.298154,163.310589 198.110326,164.667963 C191.183787,168.842556 183.854737,172.420929 176.223542,175.320965 C180.230393,183.341335 184.861538,190.991831 190.096624,198.16893 C211.238746,191.588051 232.743023,181.531619 254.911949,164.955721 C260.227747,108.668201 245.831087,59.8662432 216.856339,16.5966031 Z M85.4738752,135.09489 C72.8290281,135.09489 62.4592217,123.290155 62.4592217,108.914901 C62.4592217,94.5396472 72.607595,82.7145587 85.4738752,82.7145587 C98.3405064,82.7145587 108.709962,94.5189427 108.488529,108.914901 C108.508531,123.290155 98.3405064,135.09489 85.4738752,135.09489 Z M170.525237,135.09489 C157.88039,135.09489 147.510584,123.290155 147.510584,108.914901 C147.510584,94.5396472 157.658606,82.7145587 170.525237,82.7145587 C183.391518,82.7145587 193.761324,94.5189427 193.539891,108.914901 C193.539891,123.290155 183.391518,135.09489 170.525237,135.09489 Z"
          fillRule="nonzero"
        ></path>
      </g>
    </svg>
  ),
  twitter: Twitter,
  check: Check,
};


================================================
FILE: src/components/Icons/index.ts
================================================
export * from "./Icons";


================================================
FILE: src/components/Loading/Loading.tsx
================================================
import React from "react";
import { Icons } from "../Icons/Icons";
import { type LucideProps } from "lucide-react";
import { cn } from "~/utils/cn";

export const Spinner = ({ className, ...props }: LucideProps) => (
  <Icons.spinner
    className={cn("h-5 w-5 animate-spin text-gray-500", className)}
    {...props}
  />
);

export const LoadingScreen = () => {
  return (
    <div className="absolute left-0 top-0 flex h-full w-full flex-col items-center justify-center">
      <Spinner className="h-10 w-10 " />
    </div>
  );
};


================================================
FILE: src/components/Loading/index.ts
================================================
export * from "./Loading";


================================================
FILE: src/components/PrefetchTRPCQuery/PrefetchTRPCQuery.tsx
================================================
import { Hydrate, dehydrate } from "@tanstack/react-query";
import { api as serverApi } from "~/trpc/server";
import { getServerQueryClient } from "~/utils/getQueryClient";
import { type RouterInputs } from "~/trpc/shared";

type AccessPaths<T> = {
  [K in keyof T]: {
    [L in keyof T[K]]: `${string & K}.${string & L}`;
  }[keyof T[K]];
}[keyof T];

type ValueTypeAt<T, P extends string> = P extends `${infer K}.${infer L}`
  ? K extends keyof T
    ? L extends keyof T[K]
      ? T[K][L]
      : never
    : never
  : never;

type ParamsType<T extends AccessPaths<RouterInputs>> = ValueTypeAt<
  RouterInputs,
  T
> extends void | undefined
  ? { params?: undefined }
  : { params: ValueTypeAt<RouterInputs, T> };

export const PrefetchTRPCQuery = async <T extends AccessPaths<RouterInputs>>({
  children,
  queryName,
  params = undefined,
}: {
  children: React.ReactNode;
  queryName: T;
} & ParamsType<T>) => {
  const queryClient = getServerQueryClient();
  const [router, procedure] = queryName.split(".");

  try {
    // Just let the frontend handle it if it fails
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    const data = await serverApi[router][procedure].query(params);

    await queryClient.prefetchQuery(
      [[router, procedure], { input: params, type: "query" }],
      () => data,
    );
    const dehydratedState = dehydrate(queryClient);

    return <Hydrate state={dehydratedState}>{children}</Hydrate>;
  } catch (e) {
    console.error(e, "PrefetchTRPCQuery failed");

    return <>{children}</>;
  }
};


================================================
FILE: src/components/PrivateRoute/PrivateRoute.tsx
================================================
import { type PropsWithChildren } from "react";
import { getServerUser } from "~/utils/auth";
import { PrivateRouteBase } from "./PrivateRouteBase";
import { redirect } from "next/navigation";

export const PrivateRoute = async ({ children }: PropsWithChildren) => {
  const user = await getServerUser();

  if (!user) redirect("/login");

  return <PrivateRouteBase>{children}</PrivateRouteBase>;
};


================================================
FILE: src/components/PrivateRoute/PrivateRouteBase.tsx
================================================
"use client";

import { type PropsWithChildren } from "react";
import { withPrivateRoute } from "~/providers/AuthProvider/withPrivateRoute";

const PrivateRouteBaseComponent = ({ children }: PropsWithChildren) => children;

export const PrivateRouteBase = withPrivateRoute(PrivateRouteBaseComponent);


================================================
FILE: src/components/PublicRoute/PublicRoute.tsx
================================================
"use client";

import { withPublicRoute } from "~/providers/AuthProvider/withPublicRoute";

const PublicRouteComponent = ({ children }: { children: React.ReactNode }) =>
  children;

export const PublicRoute = withPublicRoute(PublicRouteComponent);


================================================
FILE: src/components/TailwindIndicator/TailwindIndicator.tsx
================================================
export function TailwindIndicator() {
  if (process.env.NODE_ENV === "production") return null;

  return (
    <div className="fixed bottom-1 left-1 z-50 flex h-6 w-6 items-center justify-center rounded-full bg-gray-800 p-3 font-mono text-xs text-white">
      <div className="block sm:hidden">xs</div>
      <div className="hidden sm:block md:hidden lg:hidden xl:hidden 2xl:hidden">
        sm
      </div>
      <div className="hidden md:block lg:hidden xl:hidden 2xl:hidden">md</div>
      <div className="hidden lg:block xl:hidden 2xl:hidden">lg</div>
      <div className="hidden xl:block 2xl:hidden">xl</div>
      <div className="hidden 2xl:block">2xl</div>
    </div>
  );
}


================================================
FILE: src/components/TailwindIndicator/index.ts
================================================
export * from "./TailwindIndicator";


================================================
FILE: src/components/ui/button.tsx
================================================
import * as React from "react";
import { type VariantProps, cva } from "class-variance-authority";
import { cn } from "~/utils/cn";
import { Spinner } from "../Loading";

const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive:
          "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline:
          "border border-input hover:bg-accent hover:text-accent-foreground",
        secondary:
          "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "underline-offset-4 hover:underline text-primary",
        premium:
          "font-bold bg-clip-padding border-[2px] before:-m-[2px] rounded-md before:rounded-md border-transparent bg-white dark:bg-secondary text-primary hover:before:bg-gradient-to-r hover:before:from-pink-500 hover:before:to-purple-500 before:bg-gradient-to-r before:from-purple-500 before:to-pink-500 hover:bg-white/90 dark:hover:bg-secondary/90 relative before:content before:absolute before:z-[-1] before:inset-0 before:bg-primary before:transition-opacity before:duration-500 before:delay-100 hover:before:opacity-100",
      },
      size: {
        default: "h-10 py-2 px-4",
        sm: "h-9 px-3 rounded-md",
        lg: "h-11 px-8 rounded-md",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  },
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  loading?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, children, loading, ...props }, ref) => {
    return (
      <button
        className={cn(
          "relative flex select-none items-center justify-center",
          buttonVariants({ variant, size, className }),
        )}
        data-loading={loading}
        ref={ref}
        {...props}
      >
        {loading && <Spinner className="absolute text-primary" />}
        {loading ? <span className="opacity-0"> {children}</span> : children}
      </button>
    );
  },
);

Button.displayName = "Button";

export { Button, buttonVariants };


================================================
FILE: src/components/ui/dropdown-menu.tsx
================================================
"use client";

import * as React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { Check, ChevronRight, Circle } from "lucide-react";

import { cn } from "~/utils/cn";

const DropdownMenu = DropdownMenuPrimitive.Root;

const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;

const DropdownMenuGroup = DropdownMenuPrimitive.Group;

const DropdownMenuPortal = DropdownMenuPrimitive.Portal;

const DropdownMenuSub = DropdownMenuPrimitive.Sub;

const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;

const DropdownMenuSubTrigger = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
    inset?: boolean;
  }
>(({ className, inset, children, ...props }, ref) => (
  <DropdownMenuPrimitive.SubTrigger
    ref={ref}
    className={cn(
      "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
      inset && "pl-8",
      className,
    )}
    {...props}
  >
    {children}
    <ChevronRight className="ml-auto h-4 w-4" />
  </DropdownMenuPrimitive.SubTrigger>
));

DropdownMenuSubTrigger.displayName =
  DropdownMenuPrimitive.SubTrigger.displayName;

const DropdownMenuSubContent = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
  <DropdownMenuPrimitive.SubContent
    ref={ref}
    className={cn(
      "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
      className,
    )}
    {...props}
  />
));

DropdownMenuSubContent.displayName =
  DropdownMenuPrimitive.SubContent.displayName;

const DropdownMenuContent = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
  <DropdownMenuPrimitive.Portal>
    <DropdownMenuPrimitive.Content
      ref={ref}
      sideOffset={sideOffset}
      className={cn(
        "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
        className,
      )}
      {...props}
    />
  </DropdownMenuPrimitive.Portal>
));

DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;

const DropdownMenuItem = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
    inset?: boolean;
  }
>(({ className, inset, ...props }, ref) => (
  <DropdownMenuPrimitive.Item
    ref={ref}
    className={cn(
      "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
      inset && "pl-8",
      className,
    )}
    {...props}
  />
));

DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;

const DropdownMenuCheckboxItem = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
  <DropdownMenuPrimitive.CheckboxItem
    ref={ref}
    className={cn(
      "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
      className,
    )}
    checked={checked}
    {...props}
  >
    <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
      <DropdownMenuPrimitive.ItemIndicator>
        <Check className="h-4 w-4" />
      </DropdownMenuPrimitive.ItemIndicator>
    </span>
    {children}
  </DropdownMenuPrimitive.CheckboxItem>
));

DropdownMenuCheckboxItem.displayName =
  DropdownMenuPrimitive.CheckboxItem.displayName;

const DropdownMenuRadioItem = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
  <DropdownMenuPrimitive.RadioItem
    ref={ref}
    className={cn(
      "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
      className,
    )}
    {...props}
  >
    <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
      <DropdownMenuPrimitive.ItemIndicator>
        <Circle className="h-2 w-2 fill-current" />
      </DropdownMenuPrimitive.ItemIndicator>
    </span>
    {children}
  </DropdownMenuPrimitive.RadioItem>
));

DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;

const DropdownMenuLabel = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Label>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
    inset?: boolean;
  }
>(({ className, inset, ...props }, ref) => (
  <DropdownMenuPrimitive.Label
    ref={ref}
    className={cn(
      "px-2 py-1.5 text-sm font-semibold",
      inset && "pl-8",
      className,
    )}
    {...props}
  />
));

DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;

const DropdownMenuSeparator = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
  <DropdownMenuPrimitive.Separator
    ref={ref}
    className={cn("-mx-1 my-1 h-px bg-muted", className)}
    {...props}
  />
));

DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;

const DropdownMenuShortcut = ({
  className,
  ...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
  return (
    <span
      className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
      {...props}
    />
  );
};

DropdownMenuShortcut.displayName = "DropdownMenuShortcut";

export {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuCheckboxItem,
  DropdownMenuRadioItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuGroup,
  DropdownMenuPortal,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuRadioGroup,
};


================================================
FILE: src/components/ui/form.tsx
================================================
import * as React from "react";
import type * as LabelPrimitive from "@radix-ui/react-label";
import { Slot } from "@radix-ui/react-slot";
import {
  Controller,
  type ControllerProps,
  type FieldPath,
  type FieldValues,
  FormProvider,
  useFormContext,
} from "react-hook-form";

import { cn } from "~/utils/cn";
import { Label } from "~/components/ui/label";

const Form = FormProvider;

type FormFieldContextValue<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
  name: TName;
};

const FormFieldContext = React.createContext<FormFieldContextValue>(
  {} as FormFieldContextValue,
);

const FormField = <
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
  ...props
}: ControllerProps<TFieldValues, TName>) => {
  return (
    <FormFieldContext.Provider value={{ name: props.name }}>
      <Controller {...props} />
    </FormFieldContext.Provider>
  );
};

const useFormField = () => {
  const fieldContext = React.useContext(FormFieldContext);
  const itemContext = React.useContext(FormItemContext);
  const { getFieldState, formState } = useFormContext();

  const fieldState = getFieldState(fieldContext.name, formState);

  if (!fieldContext) {
    throw new Error("useFormField should be used within <FormField>");
  }

  const { id } = itemContext;

  return {
    id,
    name: fieldContext.name,
    formItemId: `${id}-form-item`,
    formDescriptionId: `${id}-form-item-description`,
    formMessageId: `${id}-form-item-message`,
    ...fieldState,
  };
};

type FormItemContextValue = {
  id: string;
};

const FormItemContext = React.createContext<FormItemContextValue>(
  {} as FormItemContextValue,
);

const FormItem = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
  const id = React.useId();

  return (
    <FormItemContext.Provider value={{ id }}>
      <div ref={ref} className={cn("space-y-2", className)} {...props} />
    </FormItemContext.Provider>
  );
});

FormItem.displayName = "FormItem";

const FormLabel = React.forwardRef<
  React.ElementRef<typeof LabelPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
  const { error, formItemId } = useFormField();

  return (
    <Label
      ref={ref}
      className={cn(error && "text-destructive", className)}
      htmlFor={formItemId}
      {...props}
    />
  );
});

FormLabel.displayName = "FormLabel";

const FormControl = React.forwardRef<
  React.ElementRef<typeof Slot>,
  React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
  const { error, formItemId, formDescriptionId, formMessageId } =
    useFormField();

  return (
    <Slot
      ref={ref}
      id={formItemId}
      aria-describedby={
        !error
          ? `${formDescriptionId}`
          : `${formDescriptionId} ${formMessageId}`
      }
      aria-invalid={!!error}
      {...props}
    />
  );
});

FormControl.displayName = "FormControl";

const FormDescription = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
  const { formDescriptionId } = useFormField();

  return (
    <p
      ref={ref}
      id={formDescriptionId}
      className={cn("text-sm text-muted-foreground", className)}
      {...props}
    />
  );
});

FormDescription.displayName = "FormDescription";

const FormMessage = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
  const { error, formMessageId } = useFormField();
  const body = error ? String(error?.message) : children;

  if (!body) {
    return null;
  }

  return (
    <p
      ref={ref}
      id={formMessageId}
      className={cn("text-sm font-medium text-destructive", className)}
      {...props}
    >
      {body}
    </p>
  );
});

FormMessage.displayName = "FormMessage";

export {
  useFormField,
  Form,
  FormItem,
  FormLabel,
  FormControl,
  FormDescription,
  FormMessage,
  FormField,
};


================================================
FILE: src/components/ui/input.tsx
================================================
import * as React from "react";

import { cn } from "~/utils/cn";

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
  ({ className, type, ...props }, ref) => {
    return (
      <input
        type={type}
        className={cn(
          "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
          className,
        )}
        ref={ref}
        {...props}
      />
    );
  },
);

Input.displayName = "Input";

export { Input };


================================================
FILE: src/components/ui/label.tsx
================================================
"use client";

import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "~/utils/cn";

const labelVariants = cva(
  "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
);

const Label = React.forwardRef<
  React.ElementRef<typeof LabelPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
    VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
  <LabelPrimitive.Root
    ref={ref}
    className={cn(labelVariants(), className)}
    {...props}
  />
));

Label.displayName = LabelPrimitive.Root.displayName;

export { Label };


================================================
FILE: src/components/ui/toast.tsx
================================================
import * as React from "react";
import * as ToastPrimitives from "@radix-ui/react-toast";
import { cva, type VariantProps } from "class-variance-authority";
import { X } from "lucide-react";

import { cn } from "~/utils/cn";

const ToastProvider = ToastPrimitives.Provider;

const ToastViewport = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Viewport>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Viewport
    ref={ref}
    className={cn(
      "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
      className,
    )}
    {...props}
  />
));

ToastViewport.displayName = ToastPrimitives.Viewport.displayName;

const toastVariants = cva(
  "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
  {
    variants: {
      variant: {
        default: "border bg-background text-foreground",
        destructive:
          "destructive group border-destructive bg-destructive text-destructive-foreground",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  },
);

const Toast = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Root>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
    VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
  return (
    <ToastPrimitives.Root
      ref={ref}
      className={cn(toastVariants({ variant }), className)}
      {...props}
    />
  );
});

Toast.displayName = ToastPrimitives.Root.displayName;

const ToastAction = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Action>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Action
    ref={ref}
    className={cn(
      "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
      className,
    )}
    {...props}
  />
));

ToastAction.displayName = ToastPrimitives.Action.displayName;

const ToastClose = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Close>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Close
    ref={ref}
    className={cn(
      "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
      className,
    )}
    toast-close=""
    {...props}
  >
    <X className="h-4 w-4" />
  </ToastPrimitives.Close>
));

ToastClose.displayName = ToastPrimitives.Close.displayName;

const ToastTitle = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Title>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Title
    ref={ref}
    className={cn("text-sm font-semibold", className)}
    {...props}
  />
));

ToastTitle.displayName = ToastPrimitives.Title.displayName;

const ToastDescription = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Description>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Description
    ref={ref}
    className={cn("text-sm opacity-90", className)}
    {...props}
  />
));

ToastDescription.displayName = ToastPrimitives.Description.displayName;

type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;

type ToastActionElement = React.ReactElement<typeof ToastAction>;

export {
  type ToastProps,
  type ToastActionElement,
  ToastProvider,
  ToastViewport,
  Toast,
  ToastTitle,
  ToastDescription,
  ToastClose,
  ToastAction,
};


================================================
FILE: src/components/ui/toaster.tsx
================================================
"use client";

import {
  Toast,
  ToastClose,
  ToastDescription,
  ToastProvider,
  ToastTitle,
  ToastViewport,
} from "~/components/ui/toast";
import { useToast } from "~/components/ui/use-toast";

export function Toaster() {
  const { toasts } = useToast();

  return (
    <ToastProvider>
      {toasts.map(function ({ id, title, description, action, ...props }) {
        return (
          <Toast key={id} {...props}>
            <div className="grid gap-1">
              {title && <ToastTitle>{title}</ToastTitle>}
              {description && (
                <ToastDescription>{description}</ToastDescription>
              )}
            </div>
            {action}
            <ToastClose />
          </Toast>
        );
      })}
      <ToastViewport />
    </ToastProvider>
  );
}


================================================
FILE: src/components/ui/use-toast.ts
================================================
// Inspired by react-hot-toast library
import * as React from "react";

import type { ToastActionElement, ToastProps } from "~/components/ui/toast";

const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1000000;

type ToasterToast = ToastProps & {
  id: string;
  title?: React.ReactNode;
  description?: React.ReactNode;
  action?: ToastActionElement;
};

const actionTypes = {
  ADD_TOAST: "ADD_TOAST",
  UPDATE_TOAST: "UPDATE_TOAST",
  DISMISS_TOAST: "DISMISS_TOAST",
  REMOVE_TOAST: "REMOVE_TOAST",
} as const;

let count = 0;

function genId() {
  count = (count + 1) % Number.MAX_VALUE;

  return count.toString();
}

type ActionType = typeof actionTypes;

type Action =
  | {
      type: ActionType["ADD_TOAST"];
      toast: ToasterToast;
    }
  | {
      type: ActionType["UPDATE_TOAST"];
      toast: Partial<ToasterToast>;
    }
  | {
      type: ActionType["DISMISS_TOAST"];
      toastId?: ToasterToast["id"];
    }
  | {
      type: ActionType["REMOVE_TOAST"];
      toastId?: ToasterToast["id"];
    };

interface State {
  toasts: ToasterToast[];
}

const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();

const addToRemoveQueue = (toastId: string) => {
  if (toastTimeouts.has(toastId)) {
    return;
  }

  const timeout = setTimeout(() => {
    toastTimeouts.delete(toastId);
    dispatch({
      type: "REMOVE_TOAST",
      toastId: toastId,
    });
  }, TOAST_REMOVE_DELAY);

  toastTimeouts.set(toastId, timeout);
};

export const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case "ADD_TOAST":
      return {
        ...state,
        toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
      };

    case "UPDATE_TOAST":
      return {
        ...state,
        toasts: state.toasts.map((t) =>
          t.id === action.toast.id ? { ...t, ...action.toast } : t,
        ),
      };

    case "DISMISS_TOAST": {
      const { toastId } = action;

      // ! Side effects ! - This could be extracted into a dismissToast() action,
      // but I'll keep it here for simplicity
      if (toastId) {
        addToRemoveQueue(toastId);
      } else {
        state.toasts.forEach((tst) => {
          addToRemoveQueue(tst.id);
        });
      }

      return {
        ...state,
        toasts: state.toasts.map((t) =>
          t.id === toastId || toastId === undefined
            ? {
                ...t,
                open: false,
              }
            : t,
        ),
      };
    }

    case "REMOVE_TOAST":
      if (action.toastId === undefined) {
        return {
          ...state,
          toasts: [],
        };
      }

      return {
        ...state,
        toasts: state.toasts.filter((t) => t.id !== action.toastId),
      };
  }
};

const listeners: Array<(state: State) => void> = [];

let memoryState: State = { toasts: [] };

function dispatch(action: Action) {
  memoryState = reducer(memoryState, action);
  listeners.forEach((listener) => {
    listener(memoryState);
  });
}

type Toast = Omit<ToasterToast, "id">;

function toast({ ...props }: Toast) {
  const id = genId();

  const update = (updateProps: ToasterToast) =>
    dispatch({
      type: "UPDATE_TOAST",
      toast: { ...updateProps, id },
    });
  const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });

  dispatch({
    type: "ADD_TOAST",
    toast: {
      ...props,
      id,
      open: true,
      onOpenChange: (open) => {
        if (!open) dismiss();
      },
    },
  });

  return {
    id: id,
    dismiss,
    update,
  };
}

function useToast() {
  const [state, setState] = React.useState<State>(memoryState);

  React.useEffect(() => {
    listeners.push(setState);

    return () => {
      const index = listeners.indexOf(setState);

      if (index > -1) {
        listeners.splice(index, 1);
      }
    };
  }, [state]);

  return {
    ...state,
    toast,
    dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
  };
}

export { useToast, toast };


================================================
FILE: src/env.mjs
================================================
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";

export const env = createEnv({
  /**
   * Specify your server-side environment variables schema here. This way you can ensure the app
   * isn't built with invalid env vars.
   */
  server: {
    DATABASE_URL: z
      .string()
      .url()
      .refine(
        (str) => !str.includes("YOUR_MYSQL_URL_HERE"),
        "You forgot to change the default URL",
      ),
    NODE_ENV: z
      .enum(["development", "test", "production"])
      .default("development"),
    SUPABASE_SERVICE_KEY: z.string().min(1),
    DIRECT_URL: z.string().min(1),
  },

  /**
   * Specify your client-side environment variables schema here. This way you can ensure the app
   * isn't built with invalid env vars. To expose them to the client, prefix them with
   * `NEXT_PUBLIC_`.
   */
  client: {
    NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
    NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
    NEXT_PUBLIC_UMAMI_WEBSITE_ID: z.string().optional(),
  },

  /**
   * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
   * middlewares) or client-side so we need to destruct manually.
   */
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    NODE_ENV: process.env.NODE_ENV,
    DIRECT_URL: process.env.DIRECT_URL,
    NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
    NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    SUPABASE_SERVICE_KEY: process.env.SUPABASE_SERVICE_KEY,
    NEXT_PUBLIC_UMAMI_WEBSITE_ID: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID,
  },
  /**
   * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
   * useful for Docker builds.
   */
  skipValidation: !!process.env.SKIP_ENV_VALIDATION,
});


================================================
FILE: src/providers/AnalyticsProvider/AnalyticsProvider.tsx
================================================
"use client";

import { Analytics } from "@vercel/analytics/react";

import Script from "next/script";
import { env } from "~/env.mjs";

export const AnalyticsProvider = () => <Analytics />;

export const UmamiAnalyticsProvider = () => {
  return env.NEXT_PUBLIC_UMAMI_WEBSITE_ID ? (
    <Script
      src="https://analytics.umami.is/script.js"
      data-website-id={process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID}
      strategy="lazyOnload"
    />
  ) : null;
};


================================================
FILE: src/providers/AuthProvider/AuthProvider.tsx
================================================
"use client";

import React, {
  type ReactNode,
  createContext,
  useContext,
  useEffect,
  useState,
} from "react";

import { type Session, type User } from "@supabase/supabase-js";
import { supabase } from "~/server/supabase/supabaseClient";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

export const AuthContext = createContext<{
  user: User | null;
  session: Session | null;
  isLoading: boolean;
}>({
  user: null,
  session: null,
  isLoading: false,
});

const setCookies = (session: Session | null) => {
  if (session) {
    const maxAge = 100 * 365 * 24 * 60 * 60; // 100 years, never expires

    document.cookie = `access-token=${session.access_token}; path=/; max-age=${maxAge}; SameSite=Lax; secure`;
    document.cookie = `refresh-token=${session.refresh_token}; path=/; max-age=${maxAge}; SameSite=Lax; secure`;
  } else {
    const expires = new Date(0).toUTCString();

    document.cookie = `access-token=; path=/; expires=${expires}; SameSite=Lax; secure`;
    document.cookie = `refresh-token=; path=/; expires=${expires}; SameSite=Lax; secure`;
  }
};

export const AuthProvider = ({
  user: initialUser,
  session: initialSession,
  children,
}: {
  user: User | null;
  session: Session | null;
  children: ReactNode;
}) => {
  const [userSession, setUserSession] = useState<Session | null>(
    initialSession,
  );
  const [user, setUser] = useState<User | null>(initialUser);
  const [isLoading, setIsLoading] = useState(!initialUser);

  useEffect(() => {
    void supabase()
      .auth.getSession()
      .then(({ data: { session } }) => {
        setUserSession(session);
        setUser(session?.user ?? null);
        setCookies(session);
        setIsLoading(false);
      });

    const { data: authListener } = supabase().auth.onAuthStateChange(
      (event, session) => {
        setUserSession(session);
        setUser(session?.user ?? null);
        setCookies(session);
        setIsLoading(false);
      },
    );

    return () => {
      authListener.subscription.unsubscribe();
    };
  }, []);

  const value = {
    session: userSession,
    user,
    isLoading,
  };

  return (
    <AuthContext.Provider value={value}>
      {children}
      <ReactQueryDevtools />
    </AuthContext.Provider>
  );
};

export const useUser = () => {
  const context = useContext(AuthContext);

  if (context === undefined) {
    throw new Error("useUser must be used within a AuthContextProvider.");
  }

  return context;
};


================================================
FILE: src/providers/AuthProvider/withPrivateRoute.tsx
================================================
"use client";

import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { useUser } from "~/providers/AuthProvider/AuthProvider";

export const withPrivateRoute = <T extends object>(
  WrappedComponent: React.FunctionComponent<T>,
) => {
  const ComponentWithPrivateRoute = (props: T) => {
    const router = useRouter();

    const { user, isLoading } = useUser();

    useEffect(() => {
      if (!user && !isLoading) {
        router.push("/login");
      }
    }, [user, router, isLoading]);

    if (!user) return null;

    return <WrappedComponent {...props} />;
  };

  return ComponentWithPrivateRoute;
};


================================================
FILE: src/providers/AuthProvider/withPublicRoute.tsx
================================================
"use client";

import React, { useEffect } from "react";

import { useRouter } from "next/navigation";
import { useUser } from "~/providers/AuthProvider/AuthProvider";
import { LoadingScreen } from "~/components/Loading";

export const withPublicRoute = <T extends object>(
  WrappedComponent: React.FunctionComponent<T>,
) => {
  const ComponentwithPublicRoute = (props: T) => {
    const router = useRouter();
    const { user, isLoading } = useUser();
    const isUserDataLoaded = !isLoading;

    useEffect(() => {
      if (user && isUserDataLoaded) {
        router.push("/");
      }
    }, [user, isUserDataLoaded, router]);

    if (user ?? !isUserDataLoaded) return <LoadingScreen />;

    return <WrappedComponent {...props} />;
  };

  return ComponentwithPublicRoute;
};


================================================
FILE: src/providers/index.tsx
================================================
"use client";

import { ThemeProvider } from "next-themes";
import {
  AnalyticsProvider,
  UmamiAnalyticsProvider,
} from "./AnalyticsProvider/AnalyticsProvider";
import React, { type PropsWithChildren } from "react";

export function Providers({ children }: PropsWithChildren) {
  return (
    <>
      <ThemeProvider attribute="class" forcedTheme="light">
        {children}
      </ThemeProvider>
      <AnalyticsProvider />
      <UmamiAnalyticsProvider />
    </>
  );
}


================================================
FILE: src/server/api/root.ts
================================================
import { exampleRouter } from "~/server/api/routers/example";
import { createTRPCRouter } from "~/server/api/trpc";
import { authRouter } from "./routers/auth";

/**
 * This is the primary router for your server.
 *
 * All routers added in /api/routers should be manually added here.
 */
export const appRouter = createTRPCRouter({
  example: exampleRouter,
  auth: authRouter,
});

// export type definition of API
export type AppRouter = typeof appRouter;


================================================
FILE: src/server/api/routers/auth.ts
================================================
import { createTRPCRouter, privateProcedure } from "~/server/api/trpc";

export const authRouter = createTRPCRouter({
  getProfile: privateProcedure.query(({ ctx }) => {
    return ctx.db.profiles.findFirstOrThrow({
      where: {
        id: ctx.user.id,
      },
    });
  }),
});


================================================
FILE: src/server/api/routers/example.ts
================================================
import { z } from "zod";

import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";

export const exampleRouter = createTRPCRouter({
  hello: publicProcedure
    .input(z.object({ text: z.string() }))
    .query(({ input }) => {
      return {
        greeting: `Hello ${input.text}`,
      };
    }),
});


================================================
FILE: src/server/api/trpc.ts
================================================
/**
 * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS:
 * 1. You want to modify request context (see Part 1).
 * 2. You want to create a new middleware or type of procedure (see Part 3).
 *
 * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will
 * need to use are documented accordingly near the end.
 */
import { TRPCError, initTRPC } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";

import { db } from "~/server/db";
import { getUserAsAdmin } from "../supabase/supabaseClient";

/**
 * This is the actual context you will use in your router. It will be used to process every request
 * that goes through your tRPC endpoint.
 *
 * @see https://trpc.io/docs/context
 */

export const createTRPCContext = async (opts: { headers: Headers }) => {
  const headers = opts.headers;
  const authToken = headers.get("authorization");

  const { user } = authToken ? await getUserAsAdmin(authToken) : { user: null };

  return {
    ...opts,
    db,
    user,
  };
};
/**
 * 2. INITIALIZATION
 *
 * This is where the tRPC API is initialized, connecting the context and transformer. We also parse
 * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
 * errors on the backend.
 */

const t = initTRPC.context<typeof createTRPCContext>().create({
  transformer: superjson,
  errorFormatter({ shape, error }) {
    return {
      ...shape,
      data: {
        ...shape.data,
        zodError:
          error.cause instanceof ZodError ? error.cause.flatten() : null,
      },
    };
  },
});

/**
 * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
 *
 * These are the pieces you use to build your tRPC API. You should import these a lot in the
 * "/src/server/api/routers" directory.
 */

/**
 * This is how you create new routers and sub-routers in your tRPC API.
 *
 * @see https://trpc.io/docs/router
 */
export const createTRPCRouter = t.router;

/**
 * Public (unauthenticated) procedure
 *
 * This is the base piece you use to build new queries and mutations on your tRPC API. It does not
 * guarantee that a user querying is authorized, but you can still access user session data if they
 * are logged in.
 */
export const publicProcedure = t.procedure;

const enforceUserIsAuthed = t.middleware(async ({ ctx, next }) => {
  if (!ctx.user) {
    throw new TRPCError({
      code: "UNAUTHORIZED",
    });
  }

  return next({
    ctx: {
      user: ctx.user,
    },
  });
});

export const privateProcedure = t.procedure.use(enforceUserIsAuthed);


================================================
FILE: src/server/db.ts
================================================
import { PrismaClient } from "@prisma/client";

import { env } from "~/env.mjs";

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined;
};

export const db =
  globalForPrisma.prisma ??
  new PrismaClient({
    log:
      env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
  });

if (env.NODE_ENV !== "production") globalForPrisma.prisma = db;


================================================
FILE: src/server/supabase/supabaseClient.ts
================================================
import { createClient } from "@supabase/supabase-js";

import { type Database } from "./supabaseTypes";

import { env } from "~/env.mjs";

export const getServiceSupabase = () =>
  createClient<Database>(
    env.NEXT_PUBLIC_SUPABASE_URL,
    env.SUPABASE_SERVICE_KEY,
    {
      auth: {
        autoRefreshToken: false,
        persistSession: false,
      },
    },
  );

export const clientSupabase = createClient<Database>(
  env.NEXT_PUBLIC_SUPABASE_URL,
  env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
);

export const supabase = () =>
  typeof window === "undefined" ? getServiceSupabase() : clientSupabase;

export const getUserAsAdmin = async (token: string) => {
  const { data, error } = await getServiceSupabase().auth.getUser(token);

  if (error) {
    console.error(error);
    throw error;
  }

  return data;
};


================================================
FILE: src/server/supabase/supabaseTypes.ts
================================================
export type Json =
  | string
  | number
  | boolean
  | null
  | { [key: string]: Json | undefined }
  | Json[];

export interface Database {
  graphql_public: {
    Tables: {
      [_ in never]: never;
    };
    Views: {
      [_ in never]: never;
    };
    Functions: {
      graphql: {
        Args: {
          operationName?: string;
          query?: string;
          variables?: Json;
          extensions?: Json;
        };
        Returns: Json;
      };
    };
    Enums: {
      [_ in never]: never;
    };
    CompositeTypes: {
      [_ in never]: never;
    };
  };
  public: {
    Tables: {
      profiles: {
        Row: {
          avatar_url: string | null;
          email: string | null;
          full_name: string | null;
          id: string;
          updated_at: string | null;
        };
        Insert: {
          avatar_url?: string | null;
          email?: string | null;
          full_name?: string | null;
          id: string;
          updated_at?: string | null;
        };
        Update: {
          avatar_url?: string | null;
          email?: string | null;
          full_name?: string | null;
          id?: string;
          updated_at?: string | null;
        };
        Relationships: [
          {
            foreignKeyName: "profiles_id_fkey";
            columns: ["id"];
            referencedRelation: "users";
            referencedColumns: ["id"];
          },
        ];
      };
    };
    Views: {
      [_ in never]: never;
    };
    Functions: {
      [_ in never]: never;
    };
    Enums: {
      [_ in never]: never;
    };
    CompositeTypes: {
      [_ in never]: never;
    };
  };
  storage: {
    Tables: {
      buckets: {
        Row: {
          allowed_mime_types: string[] | null;
          avif_autodetection: boolean | null;
          created_at: string | null;
          file_size_limit: number | null;
          id: string;
          name: string;
          owner: string | null;
          public: boolean | null;
          updated_at: string | null;
        };
        Insert: {
          allowed_mime_types?: string[] | null;
          avif_autodetection?: boolean | null;
          created_at?: string | null;
          file_size_limit?: number | null;
          id: string;
          name: string;
          owner?: string | null;
          public?: boolean | null;
          updated_at?: string | null;
        };
        Update: {
          allowed_mime_types?: string[] | null;
          avif_autodetection?: boolean | null;
          created_at?: string | null;
          file_size_limit?: number | null;
          id?: string;
          name?: string;
          owner?: string | null;
          public?: boolean | null;
          updated_at?: string | null;
        };
        Relationships: [
          {
            foreignKeyName: "buckets_owner_fkey";
            columns: ["owner"];
            referencedRelation: "users";
            referencedColumns: ["id"];
          },
        ];
      };
      migrations: {
        Row: {
          executed_at: string | null;
          hash: string;
          id: number;
          name: string;
        };
        Insert: {
          executed_at?: string | null;
          hash: string;
          id: number;
          name: string;
        };
        Update: {
          executed_at?: string | null;
          hash?: string;
          id?: number;
          name?: string;
        };
        Relationships: [];
      };
      objects: {
        Row: {
          bucket_id: string | null;
          created_at: string | null;
          id: string;
          last_accessed_at: string | null;
          metadata: Json | null;
          name: string | null;
          owner: string | null;
          path_tokens: string[] | null;
          updated_at: string | null;
          version: string | null;
        };
        Insert: {
          bucket_id?: string | null;
          created_at?: string | null;
          id?: string;
          last_accessed_at?: string | null;
          metadata?: Json | null;
          name?: string | null;
          owner?: string | null;
          path_tokens?: string[] | null;
          updated_at?: string | null;
          version?: string | null;
        };
        Update: {
          bucket_id?: string | null;
          created_at?: string | null;
          id?: string;
          last_accessed_at?: string | null;
          metadata?: Json | null;
          name?: string | null;
          owner?: string | null;
          path_tokens?: string[] | null;
          updated_at?: string | null;
          version?: string | null;
        };
        Relationships: [
          {
            foreignKeyName: "objects_bucketId_fkey";
            columns: ["bucket_id"];
            referencedRelation: "buckets";
            referencedColumns: ["id"];
          },
        ];
      };
    };
    Views: {
      [_ in never]: never;
    };
    Functions: {
      can_insert_object: {
        Args: {
          bucketid: string;
          name: string;
          owner: string;
          metadata: Json;
        };
        Returns: undefined;
      };
      extension: {
        Args: {
          name: string;
        };
        Returns: string;
      };
      filename: {
        Args: {
          name: string;
        };
        Returns: string;
      };
      foldername: {
        Args: {
          name: string;
        };
        Returns: unknown;
      };
      get_size_by_bucket: {
        Args: Record<PropertyKey, never>;
        Returns: {
          size: number;
          bucket_id: string;
        }[];
      };
      search: {
        Args: {
          prefix: string;
          bucketname: string;
          limits?: number;
          levels?: number;
          offsets?: number;
          search?: string;
          sortcolumn?: string;
          sortorder?: string;
        };
        Returns: {
          name: string;
          id: string;
          updated_at: string;
          created_at: string;
          last_accessed_at: string;
          metadata: Json;
        }[];
      };
    };
    Enums: {
      [_ in never]: never;
    };
    CompositeTypes: {
      [_ in never]: never;
    };
  };
}


================================================
FILE: src/styles/globals.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --card: 0 0% 100%;
    --card-foreground: 222.2 84% 4.9%;
    --popover: 0 0% 100%;
    --popover-foreground: 222.2 84% 4.9%;
    --primary: 222.2 47.4% 11.2%;
    --primary-foreground: 210 40% 98%;
    --secondary: 210 40% 96.1%;
    --secondary-foreground: 222.2 47.4% 11.2%;
    --muted: 210 40% 96.1%;
    --muted-foreground: 215.4 16.3% 46.9%;
    --accent: 210 40% 96.1%;
    --accent-foreground: 222.2 47.4% 11.2%;
    --destructive: 0 84.2% 60.2%;
    --destructive-foreground: 210 40% 98%;
    --border: 214.3 31.8% 91.4%;
    --input: 214.3 31.8% 91.4%;
    --ring: 222.2 84% 4.9%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    --card: 222.2 84% 4.9%;
    --card-foreground: 210 40% 98%;
    --popover: 222.2 84% 4.9%;
    --popover-foreground: 210 40% 98%;
    --primary: 210 40% 98%;
    --primary-foreground: 222.2 47.4% 11.2%;
    --secondary: 217.2 32.6% 17.5%;
    --secondary-foreground: 210 40% 98%;
    --muted: 217.2 32.6% 17.5%;
    --muted-foreground: 215 20.2% 65.1%;
    --accent: 217.2 32.6% 17.5%;
    --accent-foreground: 210 40% 98%;
    --destructive: 0 62.8% 30.6%;
    --destructive-foreground: 210 40% 98%;
    --border: 217.2 32.6% 17.5%;
    --input: 217.2 32.6% 17.5%;
    --ring: 212.7 26.8% 83.9;
  }
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply bg-background text-foreground;
  }
}


================================================
FILE: src/trpc/react.tsx
================================================
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { loggerLink, unstable_httpBatchStreamLink } from "@trpc/client";
import { createTRPCReact } from "@trpc/react-query";
import { useState } from "react";

import { type AppRouter } from "~/server/api/root";
import { getUrl, transformer } from "./shared";
import { supabase } from "~/server/supabase/supabaseClient";

export const api = createTRPCReact<AppRouter>({
  overrides: {
    useMutation: {
      /**
       * This function is called whenever a `.useMutation` succeeds
       **/
      async onSuccess(opts) {
        /**
         * @note that order here matters:
         * The order here allows route changes in `onSuccess` without
         * having a flash of content change whilst redirecting.
         **/
        // Calls the `onSuccess` defined in the `useQuery()`-options:
        await opts.originalFn();
        // Invalidate all queries in the react-query cache:
        await opts.queryClient.invalidateQueries();
      },
    },
  },
});

export function TRPCReactProvider(props: {
  children: React.ReactNode;
  headers: Headers;
}) {
  const [queryClient] = useState(() => new QueryClient());

  const [trpcClient] = useState(() =>
    api.createClient({
      transformer,

      links: [
        loggerLink({
          enabled: (op) =>
            process.env.NODE_ENV === "development" ||
            (op.direction === "down" && op.result instanceof Error),
        }),
        unstable_httpBatchStreamLink({
          url: getUrl(),
          async headers() {
            const heads = new Map(props.headers);
            const { data } = await supabase().auth.getSession();

            if (data.session) {
              heads.set("authorization", data.session.access_token);
            }

            heads.set("x-trpc-source", "react");

            return Object.fromEntries(heads);
          },
        }),
      ],
    }),
  );

  return (
    <QueryClientProvider client={queryClient}>
      <api.Provider client={trpcClient} queryClient={queryClient}>
        {props.children}
      </api.Provider>
    </QueryClientProvider>
  );
}


================================================
FILE: src/trpc/server.ts
================================================
import {
  createTRPCProxyClient,
  loggerLink,
  TRPCClientError,
} from "@trpc/client";
import { cookies, headers } from "next/headers";

import { appRouter } from "~/server/api/root";
import { transformer } from "./shared";
import { cache } from "react";
import { createTRPCContext } from "~/server/api/trpc";
import { observable } from "@trpc/server/observable";
import { callProcedure } from "@trpc/server";
import { type TRPCErrorResponse } from "@trpc/server/rpc";

/**
 * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
 * handling a tRPC call from a React Server Component.
 */
const createContext = cache(() => {
  const heads = new Headers(headers());
  const ckies = cookies();
  const mappedCookies = new Map(ckies);
  const accessToken = mappedCookies.get("access-token")?.value;

  if (accessToken) {
    heads.set("authorization", accessToken);
  }

  heads.set("x-trpc-source", "rsc");
  heads.set("cookie", ckies.toString());

  return createTRPCContext({
    headers: heads,
  });
});

export const api = createTRPCProxyClient<typeof appRouter>({
  transformer,
  links: [
    loggerLink({
      enabled: (op) =>
        process.env.NODE_ENV === "development" ||
        (op.direction === "down" && op.result instanceof Error),
    }),
    /**
     * Custom RSC link that invokes procedures directly in the server component Don't be too afraid
     * about the complexity here, it's just wrapping `callProcedure` with an observable to make it a
     * valid ending link for tRPC.
     */
    () =>
      ({ op }) =>
        observable((observer) => {
          createContext()
            .then((ctx) => {
              return callProcedure({
                procedures: appRouter._def.procedures,
                path: op.path,
                rawInput: op.input,
                ctx,
                type: op.type,
              });
            })
            .then((data) => {
              observer.next({ result: { data } });
              observer.complete();
            })
            .catch((cause: TRPCErrorResponse) => {
              observer.error(TRPCClientError.from(cause));
            });
        }),
  ],
});


================================================
FILE: src/trpc/shared.ts
================================================
import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server";
import superjson from "superjson";

import { type AppRouter } from "~/server/api/root";

export const transformer = superjson;

function getBaseUrl() {
  if (typeof window !== "undefined") return "";
  if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;

  return `http://localhost:${process.env.PORT ?? 3000}`;
}

export function getUrl() {
  return getBaseUrl() + "/api/trpc";
}

/**
 * Inference helper for inputs.
 *
 * @example type HelloInput = RouterInputs['example']['hello']
 */
export type RouterInputs = inferRouterInputs<AppRouter>;

/**
 * Inference helper for outputs.
 *
 * @example type HelloOutput = RouterOutputs['example']['hello']
 */
export type RouterOutputs = inferRouterOutputs<AppRouter>;


================================================
FILE: src/utils/auth.ts
================================================
import { cache } from "react";
import { cookies } from "next/headers";
import { supabase } from "~/server/supabase/supabaseClient";

export const getServerUser = cache(async () => {
  const ckies = cookies();
  const mappedCookies = new Map(ckies);
  const accessToken = mappedCookies.get("access-token")?.value;
  const refreshToken = mappedCookies.get("refresh-token")?.value;

  if (!accessToken || !refreshToken) {
    return {
      user: null,
      session: null,
    };
  }

  const { error, data } = await supabase().auth.setSession({
    access_token: accessToken,
    refresh_token: refreshToken,
  });

  if (error) {
    return {
      user: null,
      session: null,
    };
  }

  return data;
});


================================================
FILE: src/utils/cn.ts
================================================
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}


================================================
FILE: src/utils/getQueryClient.ts
================================================
import { QueryClient } from "@tanstack/react-query";
import { cache } from "react";

export const getServerQueryClient = cache(() => new QueryClient());


================================================
FILE: supabase/.gitignore
================================================
# Supabase
.branches
.temp


================================================
FILE: supabase/config.toml
================================================
# A string used to distinguish different Supabase projects on the same host. Defaults to the working
# directory name when running `supabase init`.
project_id = "APP_NAME"

[api]
# Port to use for the API URL.
port = 54321
# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API
# endpoints. public and storage are always included.
schemas = ["public", "storage", "graphql_public"]
# Extra schemas to add to the search_path of every request. public is always included.
extra_search_path = ["public", "extensions"]
# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size
# for accidental or malicious requests.
max_rows = 1000

[db]
# Port to use for the local database URL.
port = 54322
# The database major version to use. This has to be the same as your remote database's. Run `SHOW
# server_version;` on the remote database to check.
major_version = 15

[studio]
# Port to use for Supabase Studio.
port = 54323

# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they
# are monitored, and you can view the emails that would have been sent from the web interface.
[inbucket]
# Port to use for the email testing server web interface.
port = 54324
smtp_port = 54325
pop3_port = 54326

[storage]
# The maximum file size allowed (e.g. "5MB", "500KB").
file_size_limit = "50MiB"

[auth]
# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used
# in emails.
site_url = "http://localhost:3000"
# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.
additional_redirect_urls = ["https://localhost:3000"]
# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (one
# week).
jwt_expiry = 3600
# Allow/disallow new user signups to your project.
enable_signup = true

[auth.email]
# Allow/disallow new user signups via email to your project.
enable_signup = true
# If enabled, a user will be required to confirm any email change on both the old, and new email
# addresses. If disabled, only the new email is required to confirm.
double_confirm_changes = true
# If enabled, users need to confirm their email address before signing in.
enable_confirmations = false

# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`,
# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin`, `notion`, `twitch`,
# `twitter`, `slack`, `spotify`, `workos`, `zoom`.
[auth.external.apple]
enabled = false
client_id = ""
secret = ""
# Overrides the default auth redirectUrl.
redirect_uri = ""
# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure,
# or any other third-party OIDC providers.
url = ""

[analytics]
enabled = false
port = 54327
vector_port = 54328
# Setup BigQuery project to enable log viewer on local development stack.
# See: https://supabase.com/docs/guides/getting-started/local-development#enabling-local-logging
gcp_project_id = ""
gcp_project_number = ""
gcp_jwt_path = "supabase/gcloud.json"


================================================
FILE: supabase/functions/.vscode/extensions.json
================================================
{
  "recommendations": ["denoland.vscode-deno"]
}


================================================
FILE: supabase/functions/.vscode/settings.json
================================================
{
  "deno.enable": true,
  "deno.lint": true,
  "editor.defaultFormatter": "denoland.vscode-deno"
}


================================================
FILE: supabase/migrations/20231004185846_initial_profiles.sql
================================================
create table "public"."profiles" (
    "id" uuid not null,
    "updated_at" timestamp with time zone,
    "email" text,
    "full_name" text,
    "avatar_url" text
);


alter table "public"."profiles" enable row level security;

CREATE UNIQUE INDEX profiles_pkey ON public.profiles USING btree (id);

CREATE UNIQUE INDEX profiles_email_key ON public.profiles USING btree (email);

alter table "public"."profiles" add constraint "profiles_pkey" PRIMARY KEY using index "profiles_pkey";

alter table "public"."profiles" add constraint "profiles_id_fkey" FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE not valid;

alter table "public"."profiles" validate constraint "profiles_id_fkey";

alter table "public"."profiles" add constraint "profiles_email_key" UNIQUE using index "profiles_email_key";

set check_function_bodies = off;

CREATE OR REPLACE FUNCTION public.handle_new_user()
 RETURNS trigger
 LANGUAGE plpgsql
 SECURITY DEFINER
AS $function$
begin
  insert into public.profiles (id, full_name, email)
  values (new.id, new.raw_user_meta_data->>'full_name', new.email);
  return new;
end;
$function$
;
create trigger on_auth_user_created
  after insert on auth.users
  for each row execute procedure public.handle_new_user();
  
create policy "Public profiles are viewable by everyone."
on "public"."profiles"
as permissive
for select
to public
using (true);


create policy "Users can insert their own profile."
on "public"."profiles"
as permissive
for insert
to public
with check ((auth.uid() = id));


create policy "Users can update own profile."
on "public"."profiles"
as permissive
for update
to public
using ((auth.uid() = id));





================================================
FILE: supabase/seed.sql
================================================


================================================
FILE: tailwind.config.ts
================================================
import { type Config } from "tailwindcss";

export default {
  darkMode: ["class"],
  content: ["src/**/*.{ts,tsx}"],
  theme: {
    container: {
      center: true,
      padding: "2rem",
      screens: {
        "2xl": "1400px",
      },
    },
    extend: {
      colors: {
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
        secondary: {
          DEFAULT: "hsl(var(--secondary))",
          foreground: "hsl(var(--secondary-foreground))",
        },
        destructive: {
          DEFAULT: "hsl(var(--destructive))",
          foreground: "hsl(var(--destructive-foreground))",
        },
        muted: {
          DEFAULT: "hsl(var(--muted))",
          foreground: "hsl(var(--muted-foreground))",
        },
        accent: {
          DEFAULT: "hsl(var(--accent))",
          foreground: "hsl(var(--accent-foreground))",
        },
        popover: {
          DEFAULT: "hsl(var(--popover))",
          foreground: "hsl(var(--popover-foreground))",
        },
        card: {
          DEFAULT: "hsl(var(--card))",
          foreground: "hsl(var(--card-foreground))",
        },
      },
      borderRadius: {
        lg: "var(--radius)",
        md: "calc(var(--radius) - 2px)",
        sm: "calc(var(--radius) - 4px)",
      },
      keyframes: {
        "accordion-down": {
          from: { height: "0" },
          to: { height: "var(--radix-accordion-content-height)" },
        },
        "accordion-up": {
          from: { height: "var(--radix-accordion-content-height)" },
          to: { height: "0" },
        },
      },
      animation: {
        "accordion-down": "accordion-down 0.2s ease-out",
        "accordion-up": "accordion-up 0.2s ease-out",
      },
    },
  },
  plugins: [require("tailwindcss-animate")],
} satisfies Config;


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "es2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "checkJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "noUncheckedIndexedAccess": true,
    "baseUrl": ".",
    "paths": {
      "~/*": ["./src/*"]
    },
    "plugins": [
      {
        "name": "next"
      }
    ]
  },
  "include": [
    ".eslintrc.cjs",
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    "**/*.cjs",
    "**/*.mjs",
    ".next/types/**/*.ts"
  ],
  "exclude": ["node_modules"]
}
Download .txt
gitextract_wqkuwtlm/

├── .eslintrc.cjs
├── .github/
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── ci.yml
│       ├── production.yaml
│       └── staging.yaml
├── .gitignore
├── README.md
├── components.json
├── next.config.mjs
├── package.json
├── postcss.config.cjs
├── prettier.config.mjs
├── prisma/
│   └── schema.prisma
├── src/
│   ├── app/
│   │   ├── (auth)/
│   │   │   ├── _components/
│   │   │   │   └── DevLoginButtons.tsx
│   │   │   ├── layout.tsx
│   │   │   └── login/
│   │   │       └── page.tsx
│   │   ├── (authenticatedRoutes)/
│   │   │   ├── authenticated/
│   │   │   │   └── page.tsx
│   │   │   └── layout.tsx
│   │   ├── _components/
│   │   │   └── ServerDataStreaming.tsx
│   │   ├── api/
│   │   │   └── trpc/
│   │   │       └── [trpc]/
│   │   │           └── route.ts
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── FormInput/
│   │   │   └── FormInput.tsx
│   │   ├── Icons/
│   │   │   ├── Icons.tsx
│   │   │   └── index.ts
│   │   ├── Loading/
│   │   │   ├── Loading.tsx
│   │   │   └── index.ts
│   │   ├── PrefetchTRPCQuery/
│   │   │   └── PrefetchTRPCQuery.tsx
│   │   ├── PrivateRoute/
│   │   │   ├── PrivateRoute.tsx
│   │   │   └── PrivateRouteBase.tsx
│   │   ├── PublicRoute/
│   │   │   └── PublicRoute.tsx
│   │   ├── TailwindIndicator/
│   │   │   ├── TailwindIndicator.tsx
│   │   │   └── index.ts
│   │   └── ui/
│   │       ├── button.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── form.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── toast.tsx
│   │       ├── toaster.tsx
│   │       └── use-toast.ts
│   ├── env.mjs
│   ├── providers/
│   │   ├── AnalyticsProvider/
│   │   │   └── AnalyticsProvider.tsx
│   │   ├── AuthProvider/
│   │   │   ├── AuthProvider.tsx
│   │   │   ├── withPrivateRoute.tsx
│   │   │   └── withPublicRoute.tsx
│   │   └── index.tsx
│   ├── server/
│   │   ├── api/
│   │   │   ├── root.ts
│   │   │   ├── routers/
│   │   │   │   ├── auth.ts
│   │   │   │   └── example.ts
│   │   │   └── trpc.ts
│   │   ├── db.ts
│   │   └── supabase/
│   │       ├── supabaseClient.ts
│   │       └── supabaseTypes.ts
│   ├── styles/
│   │   └── globals.css
│   ├── trpc/
│   │   ├── react.tsx
│   │   ├── server.ts
│   │   └── shared.ts
│   └── utils/
│       ├── auth.ts
│       ├── cn.ts
│       └── getQueryClient.ts
├── supabase/
│   ├── .gitignore
│   ├── config.toml
│   ├── functions/
│   │   └── .vscode/
│   │       ├── extensions.json
│   │       └── settings.json
│   ├── migrations/
│   │   └── 20231004185846_initial_profiles.sql
│   └── seed.sql
├── tailwind.config.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (43 symbols across 21 files)

FILE: src/app/(authenticatedRoutes)/layout.tsx
  function Layout (line 5) | function Layout({ children }: PropsWithChildren) {

FILE: src/app/_components/ServerDataStreaming.tsx
  function ServerDataStreaming (line 3) | async function ServerDataStreaming() {

FILE: src/app/layout.tsx
  function RootLayout (line 22) | async function RootLayout({ children }: { children: React.ReactNode }) {

FILE: src/components/FormInput/FormInput.tsx
  type FormInputProps (line 9) | type FormInputProps = {

FILE: src/components/Icons/Icons.tsx
  type Icon (line 51) | type Icon = LucideIcon;

FILE: src/components/PrefetchTRPCQuery/PrefetchTRPCQuery.tsx
  type AccessPaths (line 6) | type AccessPaths<T> = {
  type ValueTypeAt (line 12) | type ValueTypeAt<T, P extends string> = P extends `${infer K}.${infer L}`
  type ParamsType (line 20) | type ParamsType<T extends AccessPaths<RouterInputs>> = ValueTypeAt<

FILE: src/components/TailwindIndicator/TailwindIndicator.tsx
  function TailwindIndicator (line 1) | function TailwindIndicator() {

FILE: src/components/ui/button.tsx
  type ButtonProps (line 36) | interface ButtonProps

FILE: src/components/ui/form.tsx
  type FormFieldContextValue (line 18) | type FormFieldContextValue<
  type FormItemContextValue (line 65) | type FormItemContextValue = {

FILE: src/components/ui/input.tsx
  type InputProps (line 5) | interface InputProps

FILE: src/components/ui/toast.tsx
  type ToastProps (line 119) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
  type ToastActionElement (line 121) | type ToastActionElement = React.ReactElement<typeof ToastAction>;

FILE: src/components/ui/toaster.tsx
  function Toaster (line 13) | function Toaster() {

FILE: src/components/ui/use-toast.ts
  constant TOAST_LIMIT (line 6) | const TOAST_LIMIT = 1;
  constant TOAST_REMOVE_DELAY (line 7) | const TOAST_REMOVE_DELAY = 1000000;
  type ToasterToast (line 9) | type ToasterToast = ToastProps & {
  function genId (line 25) | function genId() {
  type ActionType (line 31) | type ActionType = typeof actionTypes;
  type Action (line 33) | type Action =
  type State (line 51) | interface State {
  function dispatch (line 134) | function dispatch(action: Action) {
  type Toast (line 141) | type Toast = Omit<ToasterToast, "id">;
  function toast (line 143) | function toast({ ...props }: Toast) {
  function useToast (line 172) | function useToast() {

FILE: src/providers/index.tsx
  function Providers (line 10) | function Providers({ children }: PropsWithChildren) {

FILE: src/server/api/root.ts
  type AppRouter (line 16) | type AppRouter = typeof appRouter;

FILE: src/server/api/trpc.ts
  method errorFormatter (line 45) | errorFormatter({ shape, error }) {

FILE: src/server/supabase/supabaseTypes.ts
  type Json (line 1) | type Json =
  type Database (line 9) | interface Database {

FILE: src/trpc/react.tsx
  method onSuccess (line 18) | async onSuccess(opts) {
  function TRPCReactProvider (line 33) | function TRPCReactProvider(props: {

FILE: src/trpc/shared.ts
  function getBaseUrl (line 8) | function getBaseUrl() {
  function getUrl (line 15) | function getUrl() {
  type RouterInputs (line 24) | type RouterInputs = inferRouterInputs<AppRouter>;
  type RouterOutputs (line 31) | type RouterOutputs = inferRouterOutputs<AppRouter>;

FILE: src/utils/cn.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: supabase/migrations/20231004185846_initial_profiles.sql
  type "public" (line 1) | create table "public"."profiles" (
  type profiles_pkey (line 12) | CREATE UNIQUE INDEX profiles_pkey ON public.profiles USING btree (id)
  type profiles_email_key (line 14) | CREATE UNIQUE INDEX profiles_email_key ON public.profiles USING btree (e...
  function public (line 26) | CREATE OR REPLACE FUNCTION public.handle_new_user()
Condensed preview — 69 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (116K chars).
[
  {
    "path": ".eslintrc.cjs",
    "chars": 1960,
    "preview": "/** @type {import(\"eslint\").Linter.Config} */\nconst config = {\n  root: true,\n  extends: [\n    \"next\",\n    \"next/core-web"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 338,
    "preview": "## Motivation and context\n\n<!--- Why is this change required? What problem does it solve? -->\n<!--- Add here link to Jir"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 656,
    "preview": "defaults:\n  run:\n    working-directory: ./\n\nname: CI on pull request\n\non: [pull_request]\n\njobs:\n  prettier-eslint:\n    n"
  },
  {
    "path": ".github/workflows/production.yaml",
    "chars": 518,
    "preview": "name: Deploy Migrations to Production\n\non:\n  push:\n    branches:\n      - prod\n  workflow_dispatch:\n\njobs:\n  deploy:\n    "
  },
  {
    "path": ".github/workflows/staging.yaml",
    "chars": 509,
    "preview": "name: Deploy Migrations to Staging\n\non:\n  push:\n    branches:\n      - staging\n  workflow_dispatch:\n\njobs:\n  deploy:\n    "
  },
  {
    "path": ".gitignore",
    "chars": 591,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": "README.md",
    "chars": 3445,
    "preview": "# T3 stack + Supabase + App directory\n\nThis project is Edge ready (Vercel Edge runtime)\n\nThis is a starter project/boile"
  },
  {
    "path": "components.json",
    "chars": 330,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"default\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {\n"
  },
  {
    "path": "next.config.mjs",
    "chars": 491,
    "preview": "/**\n * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful\n * for Docker b"
  },
  {
    "path": "package.json",
    "chars": 2785,
    "preview": "{\n  \"name\": \"t3-supabase-app-router-enhanced\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"build\": \"nex"
  },
  {
    "path": "postcss.config.cjs",
    "chars": 107,
    "preview": "const config = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n};\n\nmodule.exports = config;\n"
  },
  {
    "path": "prettier.config.mjs",
    "chars": 177,
    "preview": "/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').options} */\nconst config = {\n  plugins: [\"p"
  },
  {
    "path": "prisma/schema.prisma",
    "chars": 15999,
    "preview": "generator client {\n  provider        = \"prisma-client-js\"\n  previewFeatures = [\"multiSchema\"]\n}\n\ndatasource db {\n  provi"
  },
  {
    "path": "src/app/(auth)/_components/DevLoginButtons.tsx",
    "chars": 856,
    "preview": "\"use client\";\n\nimport { supabase } from \"~/server/supabase/supabaseClient\";\n\nconst testAccounts = [\n  { email: \"random@g"
  },
  {
    "path": "src/app/(auth)/layout.tsx",
    "chars": 252,
    "preview": "import { type PropsWithChildren } from \"react\";\nimport { PublicRoute } from \"~/components/PublicRoute/PublicRoute\";\n\ncon"
  },
  {
    "path": "src/app/(auth)/login/page.tsx",
    "chars": 1724,
    "preview": "\"use client\";\n\nimport { type Provider } from \"@supabase/supabase-js\";\nimport { Icons } from \"~/components/Icons\";\nimport"
  },
  {
    "path": "src/app/(authenticatedRoutes)/authenticated/page.tsx",
    "chars": 890,
    "preview": "\"use client\";\n\nimport { api } from \"~/trpc/react\";\n\nconst AuthenticatedExample = () => {\n  const { data } = api.auth.get"
  },
  {
    "path": "src/app/(authenticatedRoutes)/layout.tsx",
    "chars": 429,
    "preview": "import { type PropsWithChildren } from \"react\";\nimport { PrefetchTRPCQuery } from \"~/components/PrefetchTRPCQuery/Prefet"
  },
  {
    "path": "src/app/_components/ServerDataStreaming.tsx",
    "chars": 283,
    "preview": "import { api } from \"~/trpc/server\";\n\nexport async function ServerDataStreaming() {\n  const data = await api.example.hel"
  },
  {
    "path": "src/app/api/trpc/[trpc]/route.ts",
    "chars": 1009,
    "preview": "import { fetchRequestHandler } from \"@trpc/server/adapters/fetch\";\nimport { type NextRequest } from \"next/server\";\nimpor"
  },
  {
    "path": "src/app/layout.tsx",
    "chars": 1456,
    "preview": "import { TailwindIndicator } from \"~/components/TailwindIndicator\";\nimport { Providers } from \"~/providers\";\nimport \"~/s"
  },
  {
    "path": "src/app/page.tsx",
    "chars": 2525,
    "preview": "import Link from \"next/link\";\nimport { Suspense } from \"react\";\nimport { ServerDataStreaming } from \"./_components/Serve"
  },
  {
    "path": "src/components/FormInput/FormInput.tsx",
    "chars": 501,
    "preview": "import {\n  FormControl,\n  FormDescription,\n  FormItem,\n  FormLabel,\n  FormMessage,\n} from \"../ui/form\";\n\nexport type For"
  },
  {
    "path": "src/components/Icons/Icons.tsx",
    "chars": 6456,
    "preview": "import {\n  AlertTriangle,\n  ArrowRight,\n  Check,\n  ChevronLeft,\n  ChevronRight,\n  Command,\n  CreditCard,\n  File,\n  FileT"
  },
  {
    "path": "src/components/Icons/index.ts",
    "chars": 25,
    "preview": "export * from \"./Icons\";\n"
  },
  {
    "path": "src/components/Loading/Loading.tsx",
    "chars": 534,
    "preview": "import React from \"react\";\nimport { Icons } from \"../Icons/Icons\";\nimport { type LucideProps } from \"lucide-react\";\nimpo"
  },
  {
    "path": "src/components/Loading/index.ts",
    "chars": 27,
    "preview": "export * from \"./Loading\";\n"
  },
  {
    "path": "src/components/PrefetchTRPCQuery/PrefetchTRPCQuery.tsx",
    "chars": 1575,
    "preview": "import { Hydrate, dehydrate } from \"@tanstack/react-query\";\nimport { api as serverApi } from \"~/trpc/server\";\nimport { g"
  },
  {
    "path": "src/components/PrivateRoute/PrivateRoute.tsx",
    "chars": 401,
    "preview": "import { type PropsWithChildren } from \"react\";\nimport { getServerUser } from \"~/utils/auth\";\nimport { PrivateRouteBase "
  },
  {
    "path": "src/components/PrivateRoute/PrivateRouteBase.tsx",
    "chars": 301,
    "preview": "\"use client\";\n\nimport { type PropsWithChildren } from \"react\";\nimport { withPrivateRoute } from \"~/providers/AuthProvide"
  },
  {
    "path": "src/components/PublicRoute/PublicRoute.tsx",
    "chars": 249,
    "preview": "\"use client\";\n\nimport { withPublicRoute } from \"~/providers/AuthProvider/withPublicRoute\";\n\nconst PublicRouteComponent ="
  },
  {
    "path": "src/components/TailwindIndicator/TailwindIndicator.tsx",
    "chars": 684,
    "preview": "export function TailwindIndicator() {\n  if (process.env.NODE_ENV === \"production\") return null;\n\n  return (\n    <div cla"
  },
  {
    "path": "src/components/TailwindIndicator/index.ts",
    "chars": 37,
    "preview": "export * from \"./TailwindIndicator\";\n"
  },
  {
    "path": "src/components/ui/button.tsx",
    "chars": 2563,
    "preview": "import * as React from \"react\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport { cn } from \"~"
  },
  {
    "path": "src/components/ui/dropdown-menu.tsx",
    "chars": 7358,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\ni"
  },
  {
    "path": "src/components/ui/form.tsx",
    "chars": 4163,
    "preview": "import * as React from \"react\";\nimport type * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { Slot } from \"@rad"
  },
  {
    "path": "src/components/ui/input.tsx",
    "chars": 832,
    "preview": "import * as React from \"react\";\n\nimport { cn } from \"~/utils/cn\";\n\nexport interface InputProps\n  extends React.InputHTML"
  },
  {
    "path": "src/components/ui/label.tsx",
    "chars": 734,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { cva, ty"
  },
  {
    "path": "src/components/ui/toast.tsx",
    "chars": 4877,
    "preview": "import * as React from \"react\";\nimport * as ToastPrimitives from \"@radix-ui/react-toast\";\nimport { cva, type VariantProp"
  },
  {
    "path": "src/components/ui/toaster.tsx",
    "chars": 800,
    "preview": "\"use client\";\n\nimport {\n  Toast,\n  ToastClose,\n  ToastDescription,\n  ToastProvider,\n  ToastTitle,\n  ToastViewport,\n} fro"
  },
  {
    "path": "src/components/ui/use-toast.ts",
    "chars": 3998,
    "preview": "// Inspired by react-hot-toast library\nimport * as React from \"react\";\n\nimport type { ToastActionElement, ToastProps } f"
  },
  {
    "path": "src/env.mjs",
    "chars": 1810,
    "preview": "import { createEnv } from \"@t3-oss/env-nextjs\";\nimport { z } from \"zod\";\n\nexport const env = createEnv({\n  /**\n   * Spec"
  },
  {
    "path": "src/providers/AnalyticsProvider/AnalyticsProvider.tsx",
    "chars": 460,
    "preview": "\"use client\";\n\nimport { Analytics } from \"@vercel/analytics/react\";\n\nimport Script from \"next/script\";\nimport { env } fr"
  },
  {
    "path": "src/providers/AuthProvider/AuthProvider.tsx",
    "chars": 2487,
    "preview": "\"use client\";\n\nimport React, {\n  type ReactNode,\n  createContext,\n  useContext,\n  useEffect,\n  useState,\n} from \"react\";"
  },
  {
    "path": "src/providers/AuthProvider/withPrivateRoute.tsx",
    "chars": 645,
    "preview": "\"use client\";\n\nimport { useRouter } from \"next/navigation\";\nimport { useEffect } from \"react\";\nimport { useUser } from \""
  },
  {
    "path": "src/providers/AuthProvider/withPublicRoute.tsx",
    "chars": 784,
    "preview": "\"use client\";\n\nimport React, { useEffect } from \"react\";\n\nimport { useRouter } from \"next/navigation\";\nimport { useUser "
  },
  {
    "path": "src/providers/index.tsx",
    "chars": 477,
    "preview": "\"use client\";\n\nimport { ThemeProvider } from \"next-themes\";\nimport {\n  AnalyticsProvider,\n  UmamiAnalyticsProvider,\n} fr"
  },
  {
    "path": "src/server/api/root.ts",
    "chars": 458,
    "preview": "import { exampleRouter } from \"~/server/api/routers/example\";\nimport { createTRPCRouter } from \"~/server/api/trpc\";\nimpo"
  },
  {
    "path": "src/server/api/routers/auth.ts",
    "chars": 283,
    "preview": "import { createTRPCRouter, privateProcedure } from \"~/server/api/trpc\";\n\nexport const authRouter = createTRPCRouter({\n  "
  },
  {
    "path": "src/server/api/routers/example.ts",
    "chars": 319,
    "preview": "import { z } from \"zod\";\n\nimport { createTRPCRouter, publicProcedure } from \"~/server/api/trpc\";\n\nexport const exampleRo"
  },
  {
    "path": "src/server/api/trpc.ts",
    "chars": 2567,
    "preview": "/**\n * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS:\n * 1. You want to modify request context (see Part 1).\n * 2. Y"
  },
  {
    "path": "src/server/db.ts",
    "chars": 399,
    "preview": "import { PrismaClient } from \"@prisma/client\";\n\nimport { env } from \"~/env.mjs\";\n\nconst globalForPrisma = globalThis as "
  },
  {
    "path": "src/server/supabase/supabaseClient.ts",
    "chars": 819,
    "preview": "import { createClient } from \"@supabase/supabase-js\";\n\nimport { type Database } from \"./supabaseTypes\";\n\nimport { env } "
  },
  {
    "path": "src/server/supabase/supabaseTypes.ts",
    "chars": 6194,
    "preview": "export type Json =\n  | string\n  | number\n  | boolean\n  | null\n  | { [key: string]: Json | undefined }\n  | Json[];\n\nexpor"
  },
  {
    "path": "src/styles/globals.css",
    "chars": 1559,
    "preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --f"
  },
  {
    "path": "src/trpc/react.tsx",
    "chars": 2165,
    "preview": "\"use client\";\n\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { loggerLink, unstable_h"
  },
  {
    "path": "src/trpc/server.ts",
    "chars": 2197,
    "preview": "import {\n  createTRPCProxyClient,\n  loggerLink,\n  TRPCClientError,\n} from \"@trpc/client\";\nimport { cookies, headers } fr"
  },
  {
    "path": "src/trpc/shared.ts",
    "chars": 814,
    "preview": "import { type inferRouterInputs, type inferRouterOutputs } from \"@trpc/server\";\nimport superjson from \"superjson\";\n\nimpo"
  },
  {
    "path": "src/utils/auth.ts",
    "chars": 713,
    "preview": "import { cache } from \"react\";\nimport { cookies } from \"next/headers\";\nimport { supabase } from \"~/server/supabase/supab"
  },
  {
    "path": "src/utils/cn.ts",
    "chars": 169,
    "preview": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: C"
  },
  {
    "path": "src/utils/getQueryClient.ts",
    "chars": 153,
    "preview": "import { QueryClient } from \"@tanstack/react-query\";\nimport { cache } from \"react\";\n\nexport const getServerQueryClient ="
  },
  {
    "path": "supabase/.gitignore",
    "chars": 27,
    "preview": "# Supabase\n.branches\n.temp\n"
  },
  {
    "path": "supabase/config.toml",
    "chars": 3141,
    "preview": "# A string used to distinguish different Supabase projects on the same host. Defaults to the working\n# directory name wh"
  },
  {
    "path": "supabase/functions/.vscode/extensions.json",
    "chars": 50,
    "preview": "{\n  \"recommendations\": [\"denoland.vscode-deno\"]\n}\n"
  },
  {
    "path": "supabase/functions/.vscode/settings.json",
    "chars": 100,
    "preview": "{\n  \"deno.enable\": true,\n  \"deno.lint\": true,\n  \"editor.defaultFormatter\": \"denoland.vscode-deno\"\n}\n"
  },
  {
    "path": "supabase/migrations/20231004185846_initial_profiles.sql",
    "chars": 1657,
    "preview": "create table \"public\".\"profiles\" (\n    \"id\" uuid not null,\n    \"updated_at\" timestamp with time zone,\n    \"email\" text,\n"
  },
  {
    "path": "supabase/seed.sql",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tailwind.config.ts",
    "chars": 2045,
    "preview": "import { type Config } from \"tailwindcss\";\n\nexport default {\n  darkMode: [\"class\"],\n  content: [\"src/**/*.{ts,tsx}\"],\n  "
  },
  {
    "path": "tsconfig.json",
    "chars": 797,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es2017\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    "
  }
]

About this extraction

This page contains the full source code of the Jaaneek/t3-supabase-app-router GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 69 files (104.2 KB), approximately 30.8k tokens, and a symbol index with 43 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!