From d5863e78fc6b58f4f6b1688fb07d320d45684b3c Mon Sep 17 00:00:00 2001 From: jeremyphilemon Date: Thu, 10 Oct 2024 14:50:36 +0530 Subject: [PATCH 01/15] Add demo thumbnail --- public/images/demo-thumbnail.png | Bin 0 -> 23198 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/images/demo-thumbnail.png diff --git a/public/images/demo-thumbnail.png b/public/images/demo-thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..8c6f98ab2eae4d993ef01a06117d52d2bf63ea35 GIT binary patch literal 23198 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 literal 0 HcmV?d00001 From 45856beddc5dd0ea9930706cd6f0d815f02fc937 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 10 Oct 2024 15:01:23 +0530 Subject: [PATCH 02/15] Update README.md --- README.md | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 79a670107..f14b4ceb2 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,47 @@ - Next.js 14 and App Router-ready AI chatbot. + Next.js 14 and App Router-ready AI chatbot.

Next.js AI Chatbot

- An open-source AI chatbot app template built with Next.js, the Vercel AI SDK, OpenAI, and Vercel KV. + An Open-Source AI Chatbot Template Built With Next.js and the AI SDK by Vercel.

Features · Model Providers · Deploy Your Own · - Running locally · - Authors + Running locally


## Features - [Next.js](https://nextjs.org) App Router -- React Server Components (RSCs), Suspense, and Server Actions -- [Vercel AI SDK](https://sdk.vercel.ai/docs) for streaming chat UI -- Support for OpenAI (default), Anthropic, Cohere, Hugging Face, or custom AI chat models and/or LangChain + - Advanced routing for seamless navigation and performance + - React Server Components (RSCs) and Server Actions for server-side rendering and increased performance +- [AI SDK](https://sdk.vercel.ai/docs) + - Unified API for generating text, structured objects, and tool calls with LLMs + - Hooks for building dynamic chat and generative user interfaces + - Supports OpenAI (default), Anthropic, Cohere, and other model providers - [shadcn/ui](https://ui.shadcn.com) - Styling with [Tailwind CSS](https://tailwindcss.com) - - [Radix UI](https://radix-ui.com) for headless component primitives - - Icons from [Phosphor Icons](https://phosphoricons.com) -- Chat History, rate limiting, and session storage with [Vercel KV](https://vercel.com/storage/kv) -- [NextAuth.js](https://github.com/nextauthjs/next-auth) for authentication + - Component primitives from [Radix UI](https://radix-ui.com) for accessibility and flexibility +- Data Persistence + - Managed through [Vercel Postgres](https://vercel.com/storage/kv) for structured data + - [Vercel Blob](https://vercel.com/storage/blob) for efficient object storage +- [NextAuth.js](https://github.com/nextauthjs/next-auth) + - Simple and secure authentication ## Model Providers -This template ships with OpenAI `gpt-3.5-turbo` as the default. However, thanks to the [Vercel AI SDK](https://sdk.vercel.ai/docs), you can switch LLM providers to [Anthropic](https://anthropic.com), [Cohere](https://cohere.com/), [Hugging Face](https://huggingface.co), or using [LangChain](https://js.langchain.com) with just a few lines of code. +This template ships with OpenAI `gpt-4o` as the default. However, thanks to the [AI SDK](https://sdk.vercel.ai/docs) by Vercel, you can switch LLM providers to [Anthropic](https://anthropic.com), [Cohere](https://cohere.com/), [Hugging Face](https://huggingface.co), or using [LangChain](https://js.langchain.com) with just a few lines of code. ## Deploy Your Own You can deploy your own version of the Next.js AI Chatbot to Vercel with one click: -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-title=Next.js+Chat&demo-description=A+full-featured%2C+hackable+Next.js+AI+chatbot+built+by+Vercel+Labs&demo-url=https%3A%2F%2Fchat.vercel.ai%2F&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2F4aVPvWuTmBvzM5cEdRdqeW%2F4234f9baf160f68ffb385a43c3527645%2FCleanShot_2023-06-16_at_17.09.21.png&project-name=Next.js+Chat&repository-name=nextjs-chat&repository-url=https%3A%2F%2Fgithub.com%2Fvercel-labs%2Fai-chatbot&from=templates&skippable-integrations=1&env=OPENAI_API_KEY%2CAUTH_SECRET&envDescription=How+to+get+these+env+vars&envLink=https%3A%2F%2Fgithub.com%2Fvercel-labs%2Fai-chatbot%2Fblob%2Fmain%2F.env.example&teamCreateStatus=hidden&stores=[{"type":"kv"}]) - -## Creating a KV Database Instance - -Follow the steps outlined in the [quick start guide](https://vercel.com/docs/storage/vercel-kv/quickstart#create-a-kv-database) provided by Vercel. This guide will assist you in creating and configuring your KV database instance on Vercel, enabling your application to interact with it. - -Remember to update your environment variables (`KV_URL`, `KV_REST_API_URL`, `KV_REST_API_TOKEN`, `KV_REST_API_READ_ONLY_TOKEN`) in the `.env` file with the appropriate credentials provided during the KV database setup. +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot&env=AUTH_SECRET,OPENAI_API_KEY,BLOB_READ_WRITE_TOKEN,POSTGRES_URL&envDescription=Learn%20more%20about%20how%20to%20get%20the%20API%20Keys%20for%20the%20application&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot%2Fblob%2Fmain%2F.env.example&demo-title=AI%20Chatbot&demo-description=Next.js%20AI%20Chatbot&demo-url=https%3A%2F%2Fchat.vercel.ai&stores=%5B%7B%22type%22%3A%22integration%22%2C%22integrationSlug%22%3A%22d%22%2C%22productSlug%22%3A%22%22%7D%5D) ## Running locally @@ -61,11 +59,3 @@ pnpm dev ``` Your app template should now be running on [localhost:3000](http://localhost:3000/). - -## Authors - -This library is created by [Vercel](https://vercel.com) and [Next.js](https://nextjs.org) team members, with contributions from: - -- Jared Palmer ([@jaredpalmer](https://twitter.com/jaredpalmer)) - [Vercel](https://vercel.com) -- Shu Ding ([@shuding\_](https://twitter.com/shuding_)) - [Vercel](https://vercel.com) -- shadcn ([@shadcn](https://twitter.com/shadcn)) - [Vercel](https://vercel.com) From 8218d83de1644d4a109f4b41c420f2d9f4a8ae14 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 10 Oct 2024 16:31:00 +0530 Subject: [PATCH 03/15] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f14b4ceb2..f74e4413a 100644 --- a/README.md +++ b/README.md @@ -35,13 +35,13 @@ ## Model Providers -This template ships with OpenAI `gpt-4o` as the default. However, thanks to the [AI SDK](https://sdk.vercel.ai/docs) by Vercel, you can switch LLM providers to [Anthropic](https://anthropic.com), [Cohere](https://cohere.com/), [Hugging Face](https://huggingface.co), or using [LangChain](https://js.langchain.com) with just a few lines of code. +This template ships with OpenAI `gpt-4o` as the default. However, with the [AI SDK](https://sdk.vercel.ai/docs), you can switch LLM providers to [OpenAI](https://openai.com), [Anthropic](https://anthropic.com), [Cohere](https://cohere.com/), and many more with just a few lines of code. ## Deploy Your Own You can deploy your own version of the Next.js AI Chatbot to Vercel with one click: -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot&env=AUTH_SECRET,OPENAI_API_KEY,BLOB_READ_WRITE_TOKEN,POSTGRES_URL&envDescription=Learn%20more%20about%20how%20to%20get%20the%20API%20Keys%20for%20the%20application&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot%2Fblob%2Fmain%2F.env.example&demo-title=AI%20Chatbot&demo-description=Next.js%20AI%20Chatbot&demo-url=https%3A%2F%2Fchat.vercel.ai&stores=%5B%7B%22type%22%3A%22integration%22%2C%22integrationSlug%22%3A%22d%22%2C%22productSlug%22%3A%22%22%7D%5D) +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot&env=AUTH_SECRET,OPENAI_API_KEY&envDescription=Learn%20more%20about%20how%20to%20get%20the%20API%20Keys%20for%20the%20application&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot%2Fblob%2Fmain%2F.env.example&demo-title=AI%20Chatbot&demo-description=An%20Open-Source%20AI%20Chatbot%20Template%20Built%20With%20Next.js%20and%20the%20AI%20SDK%20by%20Vercel.&demo-url=https%3A%2F%2Fchat.vercel.ai&stores=[{%22type%22:%22postgres%22},{%22type%22:%22blob%22}]) ## Running locally From 2c871671e791aa3b1cba72419d88c8f95db1bb41 Mon Sep 17 00:00:00 2001 From: jeremyphilemon Date: Thu, 10 Oct 2024 19:16:50 +0530 Subject: [PATCH 04/15] Update readme and prune unused packages --- README.md | 8 ++++---- package.json | 5 +---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f74e4413a..de58daeeb 100644 --- a/README.md +++ b/README.md @@ -28,14 +28,14 @@ - Styling with [Tailwind CSS](https://tailwindcss.com) - Component primitives from [Radix UI](https://radix-ui.com) for accessibility and flexibility - Data Persistence - - Managed through [Vercel Postgres](https://vercel.com/storage/kv) for structured data - - [Vercel Blob](https://vercel.com/storage/blob) for efficient object storage -- [NextAuth.js](https://github.com/nextauthjs/next-auth) + - [Vercel Postgres](https://vercel.com/storage/postgres) for chat history and user data + - [Vercel Blob](https://vercel.com/storage/blob) for efficient file storage +- [NextAuth.js](https://github.com/nextauthjs/next-auth) - Simple and secure authentication ## Model Providers -This template ships with OpenAI `gpt-4o` as the default. However, with the [AI SDK](https://sdk.vercel.ai/docs), you can switch LLM providers to [OpenAI](https://openai.com), [Anthropic](https://anthropic.com), [Cohere](https://cohere.com/), and many more with just a few lines of code. +This template ships with OpenAI `gpt-4o` as the default. However, with the [AI SDK](https://sdk.vercel.ai/docs), you can switch LLM providers to [OpenAI](https://openai.com), [Anthropic](https://anthropic.com), [Cohere](https://cohere.com/), and [many more](https://sdk.vercel.ai/providers/ai-sdk-providers) with just a few lines of code. ## Deploy Your Own diff --git a/package.json b/package.json index e309f6004..169b02fd1 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,12 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev --turbo", "build": "tsx db/migrate && next build", "start": "next start", "lint": "next lint" }, "dependencies": { - "@ai-sdk/google": "^0.0.51", "@ai-sdk/openai": "^0.0.60", "@radix-ui/react-alert-dialog": "^1.1.2", "@radix-ui/react-dialog": "^1.1.2", @@ -18,8 +17,6 @@ "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", "@radix-ui/react-visually-hidden": "^1.1.0", - "@supabase/ssr": "^0.5.1", - "@supabase/supabase-js": "^2.45.4", "@vercel/analytics": "^1.3.1", "@vercel/blob": "^0.24.1", "@vercel/postgres": "^0.10.0", From 43bceec426a257027b078b8d73b09602261da548 Mon Sep 17 00:00:00 2001 From: jeremyphilemon Date: Thu, 10 Oct 2024 19:26:04 +0530 Subject: [PATCH 05/15] Fix lint issues and tweak eslint config --- .eslintrc.json | 8 ++- components/chat.tsx | 2 +- components/message.tsx | 2 +- db/queries.ts | 5 +- package.json | 3 + pnpm-lock.yaml | 147 ++++++++++------------------------------- 6 files changed, 49 insertions(+), 118 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index ed92fcc14..1c856be33 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,10 +2,14 @@ "extends": [ "next/core-web-vitals", "plugin:import/recommended", - "plugin:import/typescript" + "plugin:import/typescript", + "prettier", + "plugin:tailwindcss/recommended" ], - "plugins": ["import"], + "plugins": ["import", "tailwindcss"], "rules": { + "tailwindcss/no-custom-classname": "off", + "tailwindcss/classnames-order": "off", "import/order": [ "error", { diff --git a/components/chat.tsx b/components/chat.tsx index 51ce4eda0..596dd1882 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -53,7 +53,7 @@ export function Chat({ ))}
diff --git a/components/message.tsx b/components/message.tsx index 264450b64..ae1765902 100644 --- a/components/message.tsx +++ b/components/message.tsx @@ -26,7 +26,7 @@ export const Message = ({ initial={{ y: 5, opacity: 0 }} animate={{ y: 0, opacity: 1 }} > -
+
{role === "assistant" ? : }
diff --git a/db/queries.ts b/db/queries.ts index c064733d9..2ecb8ad3a 100644 --- a/db/queries.ts +++ b/db/queries.ts @@ -1,7 +1,8 @@ -import { drizzle } from "drizzle-orm/postgres-js"; +import { genSaltSync, hashSync } from "bcrypt-ts"; import { desc, eq } from "drizzle-orm"; +import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; -import { genSaltSync, hashSync } from "bcrypt-ts"; + import { user, chat, User } from "./schema"; // Optionally, if not using email/pass login, you can diff --git a/package.json b/package.json index 169b02fd1..138ee24ee 100644 --- a/package.json +++ b/package.json @@ -55,9 +55,12 @@ "drizzle-kit": "^0.25.0", "eslint": "^8.57.0", "eslint-config-next": "14.2.5", + "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-import": "^2.31.0", + "eslint-plugin-tailwindcss": "^3.17.5", "postcss": "^8", + "prettier": "^3.3.3", "tailwindcss": "^3.4.1", "tsx": "^4.19.1", "typescript": "^5" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b5d1d8cc..bae18b145 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,9 +5,6 @@ settings: excludeLinksFromLockfile: false dependencies: - '@ai-sdk/google': - specifier: ^0.0.51 - version: 0.0.51(zod@3.23.8) '@ai-sdk/openai': specifier: ^0.0.60 version: 0.0.60(zod@3.23.8) @@ -32,12 +29,6 @@ dependencies: '@radix-ui/react-visually-hidden': specifier: ^1.1.0 version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@19.0.0-rc-7771d3a7-20240827)(react@19.0.0-rc-7771d3a7-20240827) - '@supabase/ssr': - specifier: ^0.5.1 - version: 0.5.1(@supabase/supabase-js@2.45.4) - '@supabase/supabase-js': - specifier: ^2.45.4 - version: 2.45.4 '@vercel/analytics': specifier: ^1.3.1 version: 1.3.1(next@15.0.0-canary.152)(react@19.0.0-rc-7771d3a7-20240827) @@ -148,15 +139,24 @@ devDependencies: eslint-config-next: specifier: 14.2.5 version: 14.2.5(eslint@8.57.0)(typescript@5.5.4) + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@8.57.0) eslint-import-resolver-typescript: specifier: ^3.6.3 version: 3.6.3(eslint-plugin-import@2.31.0)(eslint@8.57.0) eslint-plugin-import: specifier: ^2.31.0 version: 2.31.0(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-plugin-tailwindcss: + specifier: ^3.17.5 + version: 3.17.5(tailwindcss@3.4.7) postcss: specifier: ^8 version: 8.4.40 + prettier: + specifier: ^3.3.3 + version: 3.3.3 tailwindcss: specifier: ^3.4.1 version: 3.4.7 @@ -169,18 +169,6 @@ devDependencies: packages: - /@ai-sdk/google@0.0.51(zod@3.23.8): - resolution: {integrity: sha512-MUwN3tFH0grkL5zZdTUFDCDubQdMjykFw0o9bR3dMFtKP8Z2tc/iEqU1fb79+jbz2HzUBbnNGjqMQiUwG3/OrA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.0.0 - dependencies: - '@ai-sdk/provider': 0.0.24 - '@ai-sdk/provider-utils': 1.0.20(zod@3.23.8) - json-schema: 0.4.0 - zod: 3.23.8 - dev: false - /@ai-sdk/openai@0.0.60(zod@3.23.8): resolution: {integrity: sha512-NEdDdv3o76jT6UeWHxP6I/lMYcjFQhQGQi/U2gVqW1PEU4Pjaud7tAVSy27IPbiRakg6GOzWrltI2JhZgAI1wg==} engines: {node: '>=18'} @@ -2147,72 +2135,6 @@ packages: resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} dev: true - /@supabase/auth-js@2.65.0: - resolution: {integrity: sha512-+wboHfZufAE2Y612OsKeVP4rVOeGZzzMLD/Ac3HrTQkkY4qXNjI6Af9gtmxwccE5nFvTiF114FEbIQ1hRq5uUw==} - dependencies: - '@supabase/node-fetch': 2.6.15 - dev: false - - /@supabase/functions-js@2.4.1: - resolution: {integrity: sha512-8sZ2ibwHlf+WkHDUZJUXqqmPvWQ3UHN0W30behOJngVh/qHHekhJLCFbh0AjkE9/FqqXtf9eoVvmYgfCLk5tNA==} - dependencies: - '@supabase/node-fetch': 2.6.15 - dev: false - - /@supabase/node-fetch@2.6.15: - resolution: {integrity: sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==} - engines: {node: 4.x || >=6.0.0} - dependencies: - whatwg-url: 5.0.0 - dev: false - - /@supabase/postgrest-js@1.16.1: - resolution: {integrity: sha512-EOSEZFm5pPuCPGCmLF1VOCS78DfkSz600PBuvBND/IZmMciJ1pmsS3ss6TkB6UkuvTybYiBh7gKOYyxoEO3USA==} - dependencies: - '@supabase/node-fetch': 2.6.15 - dev: false - - /@supabase/realtime-js@2.10.2: - resolution: {integrity: sha512-qyCQaNg90HmJstsvr2aJNxK2zgoKh9ZZA8oqb7UT2LCh3mj9zpa3Iwu167AuyNxsxrUE8eEJ2yH6wLCij4EApA==} - dependencies: - '@supabase/node-fetch': 2.6.15 - '@types/phoenix': 1.6.5 - '@types/ws': 8.5.12 - ws: 8.18.0(bufferutil@4.0.8) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: false - - /@supabase/ssr@0.5.1(@supabase/supabase-js@2.45.4): - resolution: {integrity: sha512-+G94H/GZG0nErZ3FQV9yJmsC5Rj7dmcfCAwOt37hxeR1La+QTl8cE9whzYwPUrTJjMLGNXoO+1BMvVxwBAbz4g==} - peerDependencies: - '@supabase/supabase-js': ^2.43.4 - dependencies: - '@supabase/supabase-js': 2.45.4 - cookie: 0.6.0 - dev: false - - /@supabase/storage-js@2.7.0: - resolution: {integrity: sha512-iZenEdO6Mx9iTR6T7wC7sk6KKsoDPLq8rdu5VRy7+JiT1i8fnqfcOr6mfF2Eaqky9VQzhP8zZKQYjzozB65Rig==} - dependencies: - '@supabase/node-fetch': 2.6.15 - dev: false - - /@supabase/supabase-js@2.45.4: - resolution: {integrity: sha512-E5p8/zOLaQ3a462MZnmnz03CrduA5ySH9hZyL03Y+QZLIOO4/Gs8Rdy4ZCKDHsN7x0xdanVEWWFN3pJFQr9/hg==} - dependencies: - '@supabase/auth-js': 2.65.0 - '@supabase/functions-js': 2.4.1 - '@supabase/node-fetch': 2.6.15 - '@supabase/postgrest-js': 1.16.1 - '@supabase/realtime-js': 2.10.2 - '@supabase/storage-js': 2.7.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: false - /@swc/counter@0.1.3: resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} dev: false @@ -2294,10 +2216,6 @@ packages: pg-types: 4.0.2 dev: false - /@types/phoenix@1.6.5: - resolution: {integrity: sha512-xegpDuR+z0UqG9fwHqNoy3rI7JDlvaPh2TY47Fl80oq6g+hXT+c/LEuE43X48clZ6lOfANl5WrPur9fYO1RJ/w==} - dev: false - /@types/prop-types@15.7.12: resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} @@ -2320,12 +2238,6 @@ packages: resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} dev: false - /@types/ws@8.5.12: - resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} - dependencies: - '@types/node': 20.14.13 - dev: false - /@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4): resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} engines: {node: ^16.0.0 || >=18.0.0} @@ -3547,6 +3459,15 @@ packages: - supports-color dev: true + /eslint-config-prettier@9.1.0(eslint@8.57.0): + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.57.0 + dev: true + /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: @@ -3868,6 +3789,17 @@ packages: string.prototype.repeat: 1.0.0 dev: true + /eslint-plugin-tailwindcss@3.17.5(tailwindcss@3.4.7): + resolution: {integrity: sha512-8Mi7p7dm+mO1dHgRHHFdPu4RDTBk69Cn4P0B40vRQR+MrguUpwmKwhZy1kqYe3Km8/4nb+cyrCF+5SodOEmaow==} + engines: {node: '>=18.12.0'} + peerDependencies: + tailwindcss: ^3.4.0 + dependencies: + fast-glob: 3.3.2 + postcss: 8.4.40 + tailwindcss: 3.4.7 + dev: true + /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5694,6 +5626,12 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} + hasBin: true + dev: true + /pretty-format@3.8.0: resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} dev: false @@ -6401,10 +6339,6 @@ packages: dependencies: is-number: 7.0.0 - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: false - /trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} dev: false @@ -6670,17 +6604,6 @@ packages: typescript: 5.5.4 dev: false - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: false - - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - dev: false - /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: From efe1ab353bc9396e27f2674e625bd51460f92fd9 Mon Sep 17 00:00:00 2001 From: jeremyphilemon Date: Thu, 10 Oct 2024 19:29:33 +0530 Subject: [PATCH 06/15] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de58daeeb..d55369c6a 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ - Styling with [Tailwind CSS](https://tailwindcss.com) - Component primitives from [Radix UI](https://radix-ui.com) for accessibility and flexibility - Data Persistence - - [Vercel Postgres](https://vercel.com/storage/postgres) for chat history and user data + - [Vercel Postgres powered by Neon](https://vercel.com/storage/postgres) for saving chat history and user data - [Vercel Blob](https://vercel.com/storage/blob) for efficient file storage - [NextAuth.js](https://github.com/nextauthjs/next-auth) - Simple and secure authentication From 9b265236344b180be3bb280d0885b334ccd68c80 Mon Sep 17 00:00:00 2001 From: jeremyphilemon Date: Thu, 10 Oct 2024 20:09:23 +0530 Subject: [PATCH 07/15] Preserve email field on auth errors --- app/(auth)/login/page.tsx | 16 +++++++++++----- app/(auth)/register/page.tsx | 15 +++++++++++---- components/{form.tsx => auth-form.tsx} | 5 ++++- 3 files changed, 26 insertions(+), 10 deletions(-) rename components/{form.tsx => auth-form.tsx} (90%) diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 234846e19..44f1a719a 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -2,18 +2,19 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; -import { useActionState, useEffect } from "react"; +import { useActionState, useEffect, useState } from "react"; import { toast } from "sonner"; -import { Form } from "@/components/form"; +import { AuthForm } from "@/components/auth-form"; import { SubmitButton } from "@/components/submit-button"; import { login, LoginActionState } from "../actions"; - export default function Page() { const router = useRouter(); + const [email, setEmail] = useState(""); + const [state, formAction] = useActionState( login, { @@ -29,6 +30,11 @@ export default function Page() { } }, [state.status, router]); + const handleSubmit = (formData: FormData) => { + setEmail(formData.get("email") as string); + formAction(formData); + }; + return (
@@ -38,7 +44,7 @@ export default function Page() { Use your email and password to sign in

-
+ Sign in

{"Don't have an account? "} @@ -50,7 +56,7 @@ export default function Page() { {" for free."}

- +
); diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx index 64b14352f..25820654e 100644 --- a/app/(auth)/register/page.tsx +++ b/app/(auth)/register/page.tsx @@ -2,16 +2,18 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; -import { useActionState, useEffect } from "react"; +import { useActionState, useEffect, useState } from "react"; import { toast } from "sonner"; -import { Form } from "@/components/form"; +import { AuthForm } from "@/components/auth-form"; import { SubmitButton } from "@/components/submit-button"; import { register, RegisterActionState } from "../actions"; export default function Page() { const router = useRouter(); + + const [email, setEmail] = useState(""); const [state, formAction] = useActionState( register, { @@ -30,6 +32,11 @@ export default function Page() { } }, [state, router]); + const handleSubmit = (formData: FormData) => { + setEmail(formData.get("email") as string); + formAction(formData); + }; + return (
@@ -39,7 +46,7 @@ export default function Page() { Create an account with your email and password

-
+ Sign Up

{"Already have an account? "} @@ -51,7 +58,7 @@ export default function Page() { {" instead."}

- +
); diff --git a/components/form.tsx b/components/auth-form.tsx similarity index 90% rename from components/form.tsx rename to components/auth-form.tsx index 7f130aa8b..d7e21a818 100644 --- a/components/form.tsx +++ b/components/auth-form.tsx @@ -1,12 +1,14 @@ import { Input } from "./shadcn/input"; import { Label } from "./shadcn/label"; -export function Form({ +export function AuthForm({ action, children, + defaultEmail = "", }: { action: any; children: React.ReactNode; + defaultEmail?: string; }) { return (
@@ -26,6 +28,7 @@ export function Form({ placeholder="user@acme.com" autoComplete="email" required + defaultValue={defaultEmail} />