Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add onboarding ai agent #86

Merged
merged 11 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/api/app/v1/[[...route]]/health.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Hono } from "hono";
const app = new Hono().get("/", (c) => {
const app = new Hono()
.get("/", (c) => {
return c.json({
message: "i am alive",
status: 200,
Expand Down
5 changes: 5 additions & 0 deletions apps/api/app/v1/[[...route]]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const runtime = "edge";

const app = new Hono().basePath("/v1");

<<<<<<< HEAD:apps/api/app/api/[[...route]]/route.ts
=======
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Unresolved merge conflict markers detected

The file contains unresolved Git merge conflict markers. This will cause syntax errors and break the application. Please resolve the merge conflict before proceeding.

Remove the following lines:

-<<<<<<< HEAD:apps/api/app/api/[[...route]]/route.ts
-=======
🧰 Tools
🪛 Biome (1.9.4)

[error] 17-19: Expected a statement but instead found '<<<<<<< HEAD:apps/api/app/api/[[...route]]/route.ts
======='.

Expected a statement here.

(parse)

const allowedOrigins = [
"http://localhost:3002",
"http://localhost:3003",
Expand All @@ -34,6 +36,7 @@ app.use(
credentials: true,
}),
);
>>>>>>> main:apps/api/app/v1/[[...route]]/route.ts

app.route("/health", health);
app.route("/session", session);
Expand All @@ -52,3 +55,5 @@ const DELETE = handle(app);
const OPTIONS = handle(app);

export { GET, PATCH, POST, DELETE, OPTIONS };


2 changes: 1 addition & 1 deletion apps/api/app/v1/[[...route]]/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { auth } from "@repo/auth";
const app = new Hono()
.get("/", async (c) => {
const session = await auth.api.getSession({ headers: c.req.raw.headers });

if (!session) return c.json({ message: "no session found" }, 401);

return c.json({
Expand Down
3 changes: 2 additions & 1 deletion apps/api/app/v1/[[...route]]/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { prisma } from "@repo/db";
import { auth } from "@repo/auth";

const app = new Hono()
.get("/self", async (c) => {
.get("/self", async (c) => {
const currentUser = await auth.api.getSession({
headers: c.req.raw.headers,
});
Expand All @@ -23,6 +23,7 @@ const app = new Hono()
id: currentUser.user.id,
},
});

return c.json(
{
user,
Expand Down
4 changes: 2 additions & 2 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"dev": "next dev -p 3001 --turbopack",
"prebuild": "turbo --filter=@repo/db db:generate",
"build": "next build",
"start": "next start -p 5555",
"preview": "next build && next start -p 5555",
"start": "next start -p 3001",
"preview": "next build && next start -p 3001",
Comment on lines +7 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Documentation update needed for port 3001

The port change to 3001 in the API service requires documentation updates. Our verification found that:

  • No port references exist in any README files
  • No environment templates are present
  • No port configurations in Docker files or config files
  • No explicit port documentation in any of the existing documentation

Please update the following:

  • Add port information to apps/api/README.md
  • Create an .env.example file in the API service directory
  • Update the main README.md with the development ports for all services
  • Document port configurations in Docker setup if containerization is used
🔗 Analysis chain

Documentation update needed for port change.

Since this is a significant infrastructure change that affects local development and deployment, documentation should be updated to reflect the new port.

Please ensure the following are updated:

  • README files
  • Development setup guides
  • Deployment documentation
  • Environment variable templates

Let's check for documentation files that might need updating:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for documentation files
fd -e md -e mdx

# Look for environment templates
fd -g '.env.example' -g '.env.template'

Length of output: 294


Script:

#!/bin/bash
# Check README files for port mentions
echo "=== Checking main README.md ==="
rg -i "port|3001|3000" README.md || echo "No port mentions found"

echo -e "\n=== Checking API README ==="
rg -i "port|3001|3000" apps/api/README.md || echo "No port mentions found"

echo -e "\n=== Checking other documentation for port references ==="
rg -i "port|3001|3000" CONTRIBUTING.md || echo "No port mentions found"

echo -e "\n=== Checking for environment files ==="
fd ".env" --hidden --no-ignore

Length of output: 7840


Script:

#!/bin/bash
# Let's check for any docker or docker-compose files that might reference the port
echo "=== Checking Docker files for port references ==="
fd -e dockerfile -e yml -e yaml | xargs rg -i "port.*3001|port.*3000" || echo "No port mentions found in Docker files"

# Check for any configuration files that might contain port references
echo -e "\n=== Checking common config files ==="
fd -e json -e js -e ts -g "config*" | xargs rg -i "port.*3001|port.*3000" || echo "No port mentions found in config files"

# Check specifically for Next.js config files
echo -e "\n=== Checking Next.js config files ==="
fd "next.config" | xargs rg -i "port" || echo "No port mentions found in Next.js config"

Length of output: 1115

"lint": "next lint",
"lint:fix": "next lint --fix",
"test": "vitest --run",
Expand Down
156 changes: 156 additions & 0 deletions apps/app/actions/action.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
"use server";
import { ReactNode } from "react";
import { createStreamableValue, getMutableAIState, streamUI } from "ai/rsc";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove unused imports to clean up the code

The following imports are not used and can be removed:

  • createStreamableValue at line 3
  • Button at line 10
  • CoreMessage, generateId at line 14

Apply this diff to remove unused imports:

 import { ReactNode } from "react";
-import { createStreamableValue, getMutableAIState, streamUI } from "ai/rsc";
+ import { getMutableAIState, streamUI } from "ai/rsc";
 
 /* ... */

-import { Button } from "@/components/ui/button";
-import { CoreMessage, generateId, ToolInvocation } from "ai";
+import { ToolInvocation } from "ai";

Also applies to: 10-10, 14-14

🧰 Tools
🪛 eslint

[error] 3-3: 'createStreamableValue' is defined but never used.

(@typescript-eslint/no-unused-vars)

import { togetherai } from "@ai-sdk/togetherai";
import { AI } from "@/lib/ai";
import { BotMessage } from "@/components/custom/onboarding/message";
import BeatLoader from "@/components/custom/onboarding/BeatLoader";
import { getSession } from "./session";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import Proceed from "@/components/custom/onboarding/proceed";
import WorkspaceForm from "@/components/custom/onboarding/workspace-form";
import {sleep } from "@/lib/utils";
import { CoreMessage, generateId,ToolInvocation } from "ai";
export type ServerMessage = {
id?: number;
name?: "proceed" | "workspace" ;
role: "user" | "assistant";
content: string;
};

export type ClientMessage = {
id: number;
role: "user" | "assistant";
display: ReactNode;
toolInvocations?: ToolInvocation[]
};

export const sendMessage = async (
message: string
): Promise<ClientMessage> => {
const history = getMutableAIState<typeof AI>();
console.log(history.get().length)
console.log("ai",history.get())

history.update([...history.get(), { role: "user", content: message }]);

const response = await streamUI({
model: togetherai("meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo"),
system: `
You are an onboarding assitand and you are helping users to onboard them to Plura AI.
-any question not related to the onbaording should not be answered by you
-if someone asks any message that is not related to the onboarding then you should respond with the exact same text "wlecome to Plura"
-if the message comes as "should we continue" then call proceed tool
The workflow is as follows:
-User sends "yes" or "no" to proceed
-If the user sends "yes" then the workflow is as follows:
-then you call the workspace tool
- If the user sends "no", respond with exactly: "please create a workspace to continue your onboarding" Do not call any tools for "no"
- Only trigger the proceed tool when asking: "should we continue?"
-If the user sends any message after workspace tool is called then you should respond with the same text:"Please create a workspace to continue"
-dont call any tools if the user doesnt creates a workspace
-If the message comes as workspace {workspaceName} created then respond with the exact same text "your first workspace has been created with name: {workspaceName} created" and dont call any tools
`,
messages: [{ role: "user", content: message }, ...history.get()],
temperature: 0,
initial: (
<BotMessage>
<BeatLoader />
</BotMessage>
),
text: async function ({ content, done }) {
await sleep(1000);
if (done) {
history.done([...history.get(), { role: "assistant", content }]);
}
return <BotMessage>{content}</BotMessage>;
},
tools: {
workspace: {
description:
"when the user responds with yes then render the workspace form",
parameters: z.object({}),
generate: async function* ({}) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid using empty object patterns in Zod parameters

Using an empty object pattern {} in Zod's parameters is unnecessary and may cause linting errors.

If no parameters are needed, you can omit the parameters field or define it as z.unknown().

Apply this diff:

At line 74:

- parameters: z.object({}),
+ // You can omit parameters or define as z.unknown()

At line 98:

- parameters: z.object({}),
+ // You can omit parameters or define as z.unknown()

Also applies to: 98-98

🧰 Tools
🪛 Biome (1.9.4)

[error] 74-74: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

yield (
<BotMessage>
<BeatLoader />
</BotMessage>
);
console.log("before");
history.done([
...history.get(),
{ role: "assistant", content: "workspace form rendered" },
]);
console.log("history", history.get());
console.log("after");

return (
<BotMessage>
<WorkspaceForm />
</BotMessage>
);
},
},
proceed: {
description: `should we continue option that contains yes and no options`,
parameters: z.object({}),
generate: async function* ({}) {
yield (
<BotMessage>
<BeatLoader />
</BotMessage>
);

history.done([
...history.get(),
{ role: "assistant", content: "should be continue rendered" },
]);

return (
<BotMessage>
<Proceed />
</BotMessage>
);
},
},
},
});

return {
id: Date.now(),
role: "assistant" as const,
display: response.value,
};
};
export const sendAiGreeting = async ():Promise<ClientMessage[]> => {
const session= await getSession()
const {name,email} = session!.user
const contentString = `Hi ${name}, welcome to Plura AI!.Your email is ${email}.I am going to help you with oboarding your acccount`
Comment on lines +128 to +129
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security: PII exposure in AI prompt

The greeting message exposes user's email address to the AI model. This is unnecessary and could lead to PII leakage.

- const contentString = `Hi ${name}, welcome to Plura AI!.Your email is ${email}.I am going to help you with oboarding your acccount`
+ const contentString = `Hi ${name}, welcome to Plura AI! I am going to help you with onboarding your account.`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const {name,email} = session!.user
const contentString = `Hi ${name}, welcome to Plura AI!.Your email is ${email}.I am going to help you with oboarding your acccount`
const {name,email} = session!.user
const contentString = `Hi ${name}, welcome to Plura AI! I am going to help you with onboarding your account.`

const history = getMutableAIState<typeof AI>();
console.log("greeting history", history.get())
const value = await streamUI({
model: togetherai("meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo"),
system: ` always reply the exact same text exactly as it is: ${contentString}
`,
messages: history.get(),
initial: (
<BotMessage>
<BeatLoader />
</BotMessage>
),
text: async function ({ content, done }) {
await sleep(1000);
if (done) {
history.done([...history.get(), { role: "assistant", content }]);
}
return <BotMessage>{content}</BotMessage>;
},
});

return [{
id: Date.now(),
role: "assistant" as const,
display: value.value,
}]
}
17 changes: 17 additions & 0 deletions apps/app/actions/session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use server"
import { betterFetch } from "@better-fetch/fetch";
import { Session } from "@repo/auth";
import { headers } from "next/headers";

export const getSession = async () => {

const response = await betterFetch<Session>("http://localhost:3001/api/auth/get-session",
{
baseURL: "http://localhost:3002",
headers: {
cookie: (await headers()).get("cookie") || "",
},
});
Comment on lines +8 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use environment variables for API endpoints

Hardcoded URLs make the code less maintainable and harder to configure across different environments.

Replace hardcoded URLs with environment variables:

-  const response = await betterFetch<Session>("http://localhost:3001/api/auth/get-session", 
-    {
-      baseURL: "http://localhost:3002",
+  const response = await betterFetch<Session>(
+    `${process.env.NEXT_PUBLIC_API_URL}/api/auth/get-session`, 
+    {
+      baseURL: process.env.NEXT_PUBLIC_APP_URL,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await betterFetch<Session>("http://localhost:3001/api/auth/get-session",
{
baseURL: "http://localhost:3002",
headers: {
cookie: (await headers()).get("cookie") || "",
},
});
const response = await betterFetch<Session>(
`${process.env.NEXT_PUBLIC_API_URL}/api/auth/get-session`,
{
baseURL: process.env.NEXT_PUBLIC_APP_URL,
headers: {
cookie: (await headers()).get("cookie") || "",
},
});

return response.data

}
Comment on lines +6 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling and improve type safety

The function lacks error handling for failed requests and type validation.

Consider implementing error handling:

 export const getSession = async () => {
+  try {
     const response = await betterFetch<Session>(
       // ... configuration ...
     );
+    if (!response.data) {
+      throw new Error('Invalid session response');
+    }
     return response.data;
+  } catch (error) {
+    console.error('Failed to fetch session:', error);
+    return null;
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const getSession = async () => {
const response = await betterFetch<Session>("http://localhost:3001/api/auth/get-session",
{
baseURL: "http://localhost:3002",
headers: {
cookie: (await headers()).get("cookie") || "",
},
});
return response.data
}
export const getSession = async () => {
try {
const response = await betterFetch<Session>("http://localhost:3001/api/auth/get-session",
{
baseURL: "http://localhost:3002",
headers: {
cookie: (await headers()).get("cookie") || "",
},
});
if (!response.data) {
throw new Error('Invalid session response');
}
return response.data;
} catch (error) {
console.error('Failed to fetch session:', error);
return null;
}
}

5 changes: 5 additions & 0 deletions apps/app/app/(onboarding)/onboarding/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AI } from "@/lib/ai";

export default function OnboardingLayout({children}: {children: React.ReactNode}) {
return <AI>{children}</AI>;
}
6 changes: 6 additions & 0 deletions apps/app/app/(onboarding)/onboarding/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Chatbox from "@/components/custom/onboarding/chatbox";

export default function Onboarding() {

return <Chatbox />
}
20 changes: 20 additions & 0 deletions apps/app/app/(routes)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Infobar from "@/components/custom/infobar/infobar";
import ProgressBar from "@/components/custom/progress.bar";
import { AppSidebar } from "@/components/custom/sidebar/sidebar";
import { SidebarProvider } from "@/components/ui/sidebar";
import { cookies } from "next/headers";
async function RouteLayout({children}: {children: React.ReactNode}) {
const cookieStore = await cookies();
const defaultOpen = cookieStore.get("plura-sidebar:state")?.value === "true";
Comment on lines +7 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for cookie access

The cookie access should handle potential errors and provide fallback behavior.

-  const cookieStore = await cookies();
-  const defaultOpen = cookieStore.get("plura-sidebar:state")?.value === "true";
+  const defaultOpen = await (async () => {
+    try {
+      const cookieStore = await cookies();
+      return cookieStore.get("plura-sidebar:state")?.value === "true";
+    } catch (error) {
+      console.error('Failed to access cookies:', error);
+      return false;
+    }
+  })();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const cookieStore = await cookies();
const defaultOpen = cookieStore.get("plura-sidebar:state")?.value === "true";
const defaultOpen = await (async () => {
try {
const cookieStore = await cookies();
return cookieStore.get("plura-sidebar:state")?.value === "true";
} catch (error) {
console.error('Failed to access cookies:', error);
return false;
}
})();

return (
<SidebarProvider defaultOpen={defaultOpen}>
<AppSidebar />
<div className="p-2">
<ProgressBar />
<Infobar />
{children}
</div>
</SidebarProvider>
);
}
export default RouteLayout;
20 changes: 4 additions & 16 deletions apps/app/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ import type { Metadata } from "next";
import { GeistSans } from "geist/font/sans";
import "./globals.css";
import { ThemeProvider } from "@/hooks/theme-provider";
import { SidebarProvider } from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/custom/sidebar/sidebar";
import { cookies } from "next/headers";
import Infobar from "@/components/custom/infobar/infobar";
import ProgressBar from "@/components/custom/progress.bar";
import { PosthogProvider } from "@/hooks/posthog";

import { AI } from "@/lib/ai";
import { Toaster } from "sonner";
export const metadata: Metadata = {
title: "Plura",
description: "Generated by create next app",
Expand All @@ -19,8 +15,6 @@ async function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = await cookies();
const defaultOpen = cookieStore.get("plura-sidebar:state")?.value === "true";

return (
<html lang="en" suppressHydrationWarning>
Expand All @@ -34,14 +28,8 @@ async function RootLayout({
enableSystem
disableTransitionOnChange
>
<SidebarProvider defaultOpen={defaultOpen}>
<AppSidebar />
<div className="p-2">
<ProgressBar />
<Infobar />
{children}
</div>
</SidebarProvider>
<Toaster />
{children}
Comment on lines +31 to +32
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Warning: Inconsistent Toaster implementations across layouts

The Toaster components in the two layout files are using different implementations:

  • apps/app/app/layout.tsx uses import { Toaster } from "sonner"
  • apps/www/app/layout.tsx uses import { Toaster } from "@/components/ui/toaster"

This inconsistency could lead to different toast notification behaviors across the applications.

🔗 Analysis chain

LGTM! Clean layout structure

The simplified layout with the Toaster component is well-structured. The removal of the sidebar-related complexity makes the code more maintainable.

Let's verify if the Toaster component is consistently used across other layouts:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent Toaster usage across layouts
# Expect: Other layout files should also use Toaster for consistency

# Search for other layout files and check Toaster usage
fd "layout.tsx" apps/ --exec rg -l "Toaster"

Length of output: 90


Script:

#!/bin/bash
# Let's check the actual implementation of Toaster in both layout files
rg -A 5 -B 5 "Toaster" apps/app/app/layout.tsx apps/www/app/layout.tsx

Length of output: 2372

</ThemeProvider>
</body>
</PosthogProvider>
Expand Down
27 changes: 27 additions & 0 deletions apps/app/components/custom/onboarding/BeatLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client";

import { motion } from "motion/react";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect import path

The import from 'motion/react' is incorrect. It should be from 'framer-motion'.

-import { motion } from "motion/react";
+import { motion } from "framer-motion";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { motion } from "motion/react";
import { motion } from "framer-motion";


export default function BeatLoader() {
return (
<div className="flex backdrop:space-x-1">
{[0, 1, 2].map((index) => (
<motion.div
key={index}
className="w-3 h-3 bg-muted rounded-full"
animate={{
scale: [1, 0.5, 1],
opacity: [1, 0.5, 1],
}}
transition={{
duration: 0.6,
repeat: Infinity,
repeatType: "loop",
ease: "easeInOut",
delay: index * 0.2,
}}
/>
))}
</div>
);
}
Comment on lines +5 to +27
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance component accessibility and customization

The loader component could benefit from several improvements:

  1. Add ARIA attributes for accessibility
  2. Allow customization through props
  3. Extract animation constants
-export default function BeatLoader() {
+interface BeatLoaderProps {
+  color?: string;
+  size?: number;
+  speed?: number;
+}
+
+export default function BeatLoader({ 
+  color = 'bg-muted',
+  size = 3,
+  speed = 0.6 
+}: BeatLoaderProps) {
   return (
-    <div className="flex backdrop:space-x-1">
+    <div 
+      role="status"
+      aria-label="Loading"
+      className="flex backdrop:space-x-1"
+    >
       {[0, 1, 2].map((index) => (
         <motion.div
           key={index}
-          className="w-3 h-3 bg-muted rounded-full"
+          className={`w-${size} h-${size} ${color} rounded-full`}
           animate={{
             scale: [1, 0.5, 1],
             opacity: [1, 0.5, 1],
           }}
           transition={{
-            duration: 0.6,
+            duration: speed,
             repeat: Infinity,
             repeatType: "loop",
             ease: "easeInOut",
-            delay: index * 0.2,
+            delay: index * (speed / 3),
           }}
         />
       ))}
+      <span className="sr-only">Loading...</span>
     </div>
   );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default function BeatLoader() {
return (
<div className="flex backdrop:space-x-1">
{[0, 1, 2].map((index) => (
<motion.div
key={index}
className="w-3 h-3 bg-muted rounded-full"
animate={{
scale: [1, 0.5, 1],
opacity: [1, 0.5, 1],
}}
transition={{
duration: 0.6,
repeat: Infinity,
repeatType: "loop",
ease: "easeInOut",
delay: index * 0.2,
}}
/>
))}
</div>
);
}
interface BeatLoaderProps {
color?: string;
size?: number;
speed?: number;
}
export default function BeatLoader({
color = 'bg-muted',
size = 3,
speed = 0.6
}: BeatLoaderProps) {
return (
<div
role="status"
aria-label="Loading"
className="flex backdrop:space-x-1"
>
{[0, 1, 2].map((index) => (
<motion.div
key={index}
className={`w-${size} h-${size} ${color} rounded-full`}
animate={{
scale: [1, 0.5, 1],
opacity: [1, 0.5, 1],
}}
transition={{
duration: speed,
repeat: Infinity,
repeatType: "loop",
ease: "easeInOut",
delay: index * (speed / 3),
}}
/>
))}
<span className="sr-only">Loading...</span>
</div>
);
}

34 changes: 34 additions & 0 deletions apps/app/components/custom/onboarding/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Textarea } from "@/components/ui/textarea";
import {ArrowUp} from "lucide-react";

export function ChatInput() {
return (
<div className="fixed inset-x-0 w-full bottom-4 ">
<div className="max-w-2xl mx-auto ">
<form>
<div className="grow border border-card flex flex-col justify-center items-center rounded-2xl bg-card p-1 shadow-md ">
<Textarea
id="textarea-17"
className="resize-none placeholder:text-neutral-600"
placeholder="Leave a comment"
/>
Comment on lines +8 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add form submission handling and state management.

The form lacks essential functionality:

  • Missing onSubmit handler
  • Textarea is uncontrolled
  • No validation or error handling
-        <form>
+        <form onSubmit={handleSubmit}>
           <div className="grow border border-card flex flex-col justify-center items-center rounded-2xl bg-card p-1 shadow-md ">
             <Textarea
               id="textarea-17"
+              value={message}
+              onChange={(e) => setMessage(e.target.value)}
               className="resize-none placeholder:text-neutral-600"
               placeholder="Leave a comment"
+              aria-label="Chat message input"
             />

Committable suggestion skipped: line range outside the PR's diff.


<div className=" px-2 self-end group">
<div className="p-1 rounded-md bg-woodsmoke-700 border border-neutral-700">
<ArrowUp
size={20}
strokeWidth={2}
aria-hidden="true"
className="text-neutral-500 group-hover:text-neutral-200 "
/>
</div>
</div>
</div>
</form>
</div>
</div>
);
}



Loading
Loading