From 61124e55c2339163e15fc597adf8a04fc8f26a73 Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 14:49:43 -0400 Subject: [PATCH 01/27] chore: Update npm dependency for @radix-ui/react-hover-card to version 1.1.1 --- package-lock.json | 31 +++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 32 insertions(+) diff --git a/package-lock.json b/package-lock.json index ab57e6ef..7d91b40f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-hover-card": "^1.1.1", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-menubar": "^1.0.4", @@ -2948,6 +2949,36 @@ } } }, + "node_modules/@radix-ui/react-hover-card": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.1.tgz", + "integrity": "sha512-IwzAOP97hQpDADYVKrEEHUH/b2LA+9MgB0LgdmnbFO2u/3M5hmEofjjr2M6CyzUblaAqJdFm6B7oFtU72DPXrA==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-icons": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", diff --git a/package.json b/package.json index 91040819..a27311ba 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-hover-card": "^1.1.1", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-menubar": "^1.0.4", From a3f1fbbdc123c1c0d7928c5d5c91db73a2c7f35f Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 15:10:03 -0400 Subject: [PATCH 02/27] feat: Add chat functionality for students This commit adds new functions `studentCreateNewChat` and `studentSubmitMessage` to handle chat functionality for students. The `studentCreateNewChat` function creates a new chat entry in the database with the provided chat type and title. The `studentSubmitMessage` function allows students to submit messages to existing chats. These new functions enhance the communication capabilities of the application for students. --- actions/dashboard/chatActions.ts | 61 ++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 actions/dashboard/chatActions.ts diff --git a/actions/dashboard/chatActions.ts b/actions/dashboard/chatActions.ts new file mode 100644 index 00000000..b4c9b2b6 --- /dev/null +++ b/actions/dashboard/chatActions.ts @@ -0,0 +1,61 @@ +'use server' + +import { createResponse } from '@/utils/functions' +import { createClient } from '@/utils/supabase/server' +import { Tables } from '@/utils/supabase/supabase' + +export async function studentCreateNewChat (state: { + chatType: Tables<'chats'>['chat_type'] + title: string +}) { + const supabase = createClient() + const userData = await supabase.auth.getUser() + + if (userData.error) { + return createResponse('error', 'Error no user found', null, 'Error no user found') + } + + const studentId = userData.data.user?.id + + const chatInsert = await supabase.from('chats').insert({ + user_id: studentId, + chat_type: state.chatType, + created_at: new Date().toISOString(), + title: state.title + }).select('chat_id').single() + + if (chatInsert.error) { + return createResponse('error', 'Error creating chat', null, 'Error creating chat') + } + + // return chat id + // revalidatePath('/dashboard/student/chat/', 'layout') + return createResponse('success', 'Chat created successfully', chatInsert.data, null) +} + +export async function studentSubmitMessage (state: { + chatId: number + message: string +}) { + const supabase = createClient() + const userData = await supabase.auth.getUser() + + if (userData.error) { + return createResponse('error', 'Error no user found', null, 'Error no user found') + } + + const studentId = userData.data.user?.id + + const messageInsert = await supabase.from('messages').insert({ + user_id: studentId, + chat_id: state.chatId, + content: state.message, + created_at: new Date().toISOString() + }) + + if (messageInsert.error) { + return createResponse('error', 'Error creating message', null, 'Error creating message') + } + + return createResponse('success', 'Message sent successfully', null, null) +} From 16dfe69777e5e868b2518fc47577e718d561a153 Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 15:10:26 -0400 Subject: [PATCH 03/27] refactor: Add HoverCard component for UI hover functionality --- components/ui/hover-card.tsx | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 components/ui/hover-card.tsx diff --git a/components/ui/hover-card.tsx b/components/ui/hover-card.tsx new file mode 100644 index 00000000..81cbfce2 --- /dev/null +++ b/components/ui/hover-card.tsx @@ -0,0 +1,29 @@ +'use client' + +import * as HoverCardPrimitive from '@radix-ui/react-hover-card' +import * as React from 'react' + +import { cn } from '@/utils' + +const HoverCard = HoverCardPrimitive.Root + +const HoverCardTrigger = HoverCardPrimitive.Trigger + +const HoverCardContent = React.forwardRef< +React.ElementRef, +React.ComponentPropsWithoutRef +>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( + +)) +HoverCardContent.displayName = HoverCardPrimitive.Content.displayName + +export { HoverCard, HoverCardContent, HoverCardTrigger } From d963f1d1133b520f885a6100a1dbaba78fc630dd Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 15:16:07 -0400 Subject: [PATCH 04/27] feat: Add StudentChatSidebar component for student chat functionality --- .../student/chat/StudentChatSidebar.tsx | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 components/dashboards/student/chat/StudentChatSidebar.tsx diff --git a/components/dashboards/student/chat/StudentChatSidebar.tsx b/components/dashboards/student/chat/StudentChatSidebar.tsx new file mode 100644 index 00000000..4e4584e5 --- /dev/null +++ b/components/dashboards/student/chat/StudentChatSidebar.tsx @@ -0,0 +1,157 @@ +import Link from 'next/link' + +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger +} from '@/components/ui/accordion' +import { createClient } from '@/utils/supabase/server' + +export default async function StudentChatSidebar ({ + userRole +}: { + userRole: 'student' +}) { + const supabase = createClient() + const user = await supabase.auth.getUser() + + const chats = await supabase + .from('chats') + .select('*') + .eq('user_id', user.data.user.id) + .order('created_at', { ascending: false }) + .limit(5) + + if (chats.error) { + console.log(chats.error) + throw new Error('Error fetching chats') + } + + const freeChatTypes = chats.data.filter(chat => chat.chat_type === 'free_chat') + const qnaTypes = chats.data.filter(chat => chat.chat_type === 'q&a') + const examPrepTypes = chats.data.filter(chat => chat.chat_type === 'exam_prep') + const courseChatTypes = chats.data.filter(chat => chat.chat_type === 'course_convo') + + return ( + + ) +} From d184d46990ba75285ef04f54b74cb72ae6c4a499 Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 15:16:13 -0400 Subject: [PATCH 05/27] feat: Add FreeChat component for student chat functionality --- .../dashboards/student/chat/FreeChat.tsx | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 components/dashboards/student/chat/FreeChat.tsx diff --git a/components/dashboards/student/chat/FreeChat.tsx b/components/dashboards/student/chat/FreeChat.tsx new file mode 100644 index 00000000..8b854e06 --- /dev/null +++ b/components/dashboards/student/chat/FreeChat.tsx @@ -0,0 +1,103 @@ +'use client' +import { generateId, Message } from 'ai' +import { useChat } from 'ai/react' +import { useState } from 'react' + +import { studentCreateNewChat } from '@/actions/dashboard/chatActions' +import { ChatInput, ChatWindow } from '@/components/dashboards/Common/chat/chat' +import SuggestionsContainer from '@/components/dashboards/Common/chat/SuggestionsContainer' + +export default function FreeChat ({ + children, + chatId, + initialMessages +}: { + children?: React.ReactNode + chatId?: number + initialMessages?: Message [] +}) { + const [chatIdState, setChatIdState] = useState(chatId) + const { messages, isLoading, stop, append } = + useChat({ + maxToolRoundtrips: 5, + initialMessages: initialMessages ?? [ + { + role: 'system', + content: 'You are a helpful assistant. Ask me anything!', // initial message + id: generateId() + } + ], + body: { + chatId: chatIdState + } + }) + + console.log(chatIdState) + + return ( + <> + {messages.length <= 1 && ( +
+

+ Ask me anything, I'm here to help! +

+ + {children} +
+ + { + append({ + content: suggestion, + role: 'user' + }) + }} + /> +
+
+ )} + + { + console.log('message', message) + if (!chatIdState) { + // create a new chat + const chat = await studentCreateNewChat({ + chatType: 'free_chat', + title: message.content + }) + + if (chat.error) { + console.error(chat.error) + return + } + + console.log('chat', chat.data) + + setChatIdState(chat.data.chat_id) + } + append(message) + }} + /> + + ) +} From eaec949205cdecdd4b0e5106155cf78e899bb8fc Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 15:16:20 -0400 Subject: [PATCH 06/27] feat: Add SuggestionsContainer component for chat suggestions This commit adds the SuggestionsContainer component to handle chat suggestions in the chat feature. The component renders a list of suggestion buttons with titles and descriptions, allowing users to click on a suggestion to perform an action. This enhancement improves the user experience by providing helpful suggestions for chat interactions. --- .../Common/chat/SuggestionsContainer.tsx | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 components/dashboards/Common/chat/SuggestionsContainer.tsx diff --git a/components/dashboards/Common/chat/SuggestionsContainer.tsx b/components/dashboards/Common/chat/SuggestionsContainer.tsx new file mode 100644 index 00000000..6f19b977 --- /dev/null +++ b/components/dashboards/Common/chat/SuggestionsContainer.tsx @@ -0,0 +1,73 @@ +import { cn } from '@/utils' // Assuming the utility function is located here based on your workspace + +const SuggestionButton = ({ title, description, onSuggestionClick }: { + title: string + description: string + onSuggestionClick: (title: string) => void +}) => ( +
+ +
+) + +const SuggestionsContainer = ({ suggestions, onSuggestionClick }: { + suggestions: Array<{ title: string, description: string }> + onSuggestionClick: (title: string) => void +}) => ( +
+ {suggestions.map((suggestion, index) => ( + + ))} +
+) + +export default SuggestionsContainer From 8b750292139799ef7bdc2ff2decc90938fef8149 Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 15:16:32 -0400 Subject: [PATCH 07/27] feat: Add Error component for displaying chat loading errors This commit adds the Error component to handle and display errors that occur while loading the chat. The Error component logs the error to an error reporting service and renders a GenericError component with a retry button. This enhancement improves the error handling and user experience when encountering loading errors in the chat feature. --- .../student/chat/[chatId]/free-chat/error.tsx | 26 ++++++++++++++ .../student/chat/[chatId]/loading.tsx | 34 +++++++++++++++++++ app/dashboard/student/chat/loading.tsx | 34 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 app/dashboard/student/chat/[chatId]/free-chat/error.tsx create mode 100644 app/dashboard/student/chat/[chatId]/loading.tsx create mode 100644 app/dashboard/student/chat/loading.tsx diff --git a/app/dashboard/student/chat/[chatId]/free-chat/error.tsx b/app/dashboard/student/chat/[chatId]/free-chat/error.tsx new file mode 100644 index 00000000..d62aca1d --- /dev/null +++ b/app/dashboard/student/chat/[chatId]/free-chat/error.tsx @@ -0,0 +1,26 @@ +'use client' // Error components must be Client Components + +import { useEffect } from 'react' + +import GenericError from '@/components/GenericError' + +export default function Error ({ + error, + reset +}: { + error: Error & { digest?: string } + reset: () => void +}) { + useEffect(() => { + // Log the error to an error reporting service + console.error(error) + }, [error]) + + return ( + + ) +} diff --git a/app/dashboard/student/chat/[chatId]/loading.tsx b/app/dashboard/student/chat/[chatId]/loading.tsx new file mode 100644 index 00000000..5ffb6701 --- /dev/null +++ b/app/dashboard/student/chat/[chatId]/loading.tsx @@ -0,0 +1,34 @@ +/** + * v0 by Vercel. + * @see https://v0.dev/t/vkBhDMemo6s + * Documentation: https://v0.dev/docs#integrating-generated-code-into-your-nextjs-app + */ +import { Skeleton } from '@/components/ui/skeleton' + +export default function Component () { + return ( +
+ +
+
+
+ + +
+
+ +
+
+ +
+
+
+
+ ) +} diff --git a/app/dashboard/student/chat/loading.tsx b/app/dashboard/student/chat/loading.tsx new file mode 100644 index 00000000..5ffb6701 --- /dev/null +++ b/app/dashboard/student/chat/loading.tsx @@ -0,0 +1,34 @@ +/** + * v0 by Vercel. + * @see https://v0.dev/t/vkBhDMemo6s + * Documentation: https://v0.dev/docs#integrating-generated-code-into-your-nextjs-app + */ +import { Skeleton } from '@/components/ui/skeleton' + +export default function Component () { + return ( +
+ +
+
+
+ + +
+
+ +
+
+ +
+
+
+
+ ) +} From 8012af322798a9bfffe9c010250eae809ca8d44f Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 15:16:37 -0400 Subject: [PATCH 08/27] refactor: Improve error handling and course authorization in CoursesLayout component --- app/dashboard/student/chat/layout.tsx | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 app/dashboard/student/chat/layout.tsx diff --git a/app/dashboard/student/chat/layout.tsx b/app/dashboard/student/chat/layout.tsx new file mode 100644 index 00000000..92973a6a --- /dev/null +++ b/app/dashboard/student/chat/layout.tsx @@ -0,0 +1,47 @@ +import StudentChatSidebar from '@/components/dashboards/student/chat/StudentChatSidebar' +import { createClient } from '@/utils/supabase/server' + +export default async function CoursesLayout ({ + children +}: { + children: React.ReactNode +}) { + const supabase = createClient() + const user = await supabase.auth.getUser() + + if (user.error != null) { + throw new Error(user.error.message) + } + + const userCourses = await supabase + .from('enrollments') + .select('enrollment_id') + .eq('user_id', user.data.user.id) + + const userSubscriptions = await supabase + .from('subscriptions') + .select('subscription_id') + .eq('user_id', user.data.user.id) + .eq('subscription_status', 'active') + + if (userSubscriptions.error != null && userCourses.error != null) { + throw new Error( + 'Something went wrong while fetching your courses and subscriptions.' + ) + } + + if (userSubscriptions.data.length === 0 && userCourses.data.length === 0) { + throw new Error('You are not authorized to view this page.') + } + + return ( +
+ +
+ {children} +
+
+ ) +} From 195d7134ea1452040896603dc9138c3e72294410 Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 15:16:42 -0400 Subject: [PATCH 09/27] feat: Add FreeChatPage component for student chat functionality --- .../student/chat/[chatId]/free-chat/page.tsx | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 app/dashboard/student/chat/[chatId]/free-chat/page.tsx diff --git a/app/dashboard/student/chat/[chatId]/free-chat/page.tsx b/app/dashboard/student/chat/[chatId]/free-chat/page.tsx new file mode 100644 index 00000000..c242855a --- /dev/null +++ b/app/dashboard/student/chat/[chatId]/free-chat/page.tsx @@ -0,0 +1,48 @@ +import FreeChat from '@/components/dashboards/student/chat/FreeChat' +import { createClient } from '@/utils/supabase/server' + +export const dynamic = 'force-dynamic' +export const maxDuration = 30 + +export default async function FreeChatPage ({ + params +}: { + params: { + chatId: string + } +}) { + const supabase = createClient() + + const messagesData = await supabase + .from('messages') + .select('*') + .eq('chat_id', Number(params.chatId)) + .order('created_at', { ascending: true }) + + if (messagesData.error) { + console.log(messagesData.error) + throw new Error('Error fetching messages') + } + + return ( + + <> +

+ Free Chat +

+
+ + ({ + id: message.id.toString(), + content: message.message, + role: message.sender + })) + } + /> +
+ + ) +} From cba3e7a9571a7b15ff7fbeec9f8d3a62f76ae7c5 Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 15:16:46 -0400 Subject: [PATCH 10/27] feat: Add ChatPage component for student chat page This commit adds the ChatPage component, which displays a selection of chat options for students. It includes badges for different chat types and a HoverCard component for additional information. This enhancement improves the user experience by providing a user-friendly interface for students to interact with the chat feature. --- app/dashboard/student/chat/page.tsx | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 app/dashboard/student/chat/page.tsx diff --git a/app/dashboard/student/chat/page.tsx b/app/dashboard/student/chat/page.tsx new file mode 100644 index 00000000..5d40b4ef --- /dev/null +++ b/app/dashboard/student/chat/page.tsx @@ -0,0 +1,55 @@ +// Force the page to be dynamic and allow streaming responses up to 30 seconds + +import FreeChat from '@/components/dashboards/student/chat/FreeChat' +import { Badge } from '@/components/ui/badge' +import { + HoverCard, + HoverCardContent, + HoverCardTrigger +} from '@/components/ui/hover-card' + +export const dynamic = 'force-dynamic' +export const maxDuration = 30 + +export default async function ChatPage () { + return ( + <> +
+

+ Select a chat or start a new one +

+
+ + + Free Chat + + Chat with the AI about anything + + + + Q&A + + Ask the AI any question + + + + Exam Preparation + + Prepare for your exams + + + + Course Chat + + + Chat with the AI about your course + + +
+
+ + + + + ) +} From 227bf72695eac74c10e8a42d042c07ef32da83ba Mon Sep 17 00:00:00 2001 From: guillermoscript Date: Sun, 23 Jun 2024 15:16:53 -0400 Subject: [PATCH 11/27] feat: Add ChatPage component for student chat page --- components/dashboards/Sidebar.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/components/dashboards/Sidebar.tsx b/components/dashboards/Sidebar.tsx index 2a2ed953..c190c562 100644 --- a/components/dashboards/Sidebar.tsx +++ b/components/dashboards/Sidebar.tsx @@ -1,5 +1,5 @@ 'use server' -import { HomeIcon, Settings, User2Icon } from 'lucide-react' +import { HomeIcon, MessageCircle, Settings, User2Icon } from 'lucide-react' import Link from 'next/link' import { getServerUserRole } from '@/utils/supabase/getUserRole' @@ -14,12 +14,17 @@ async function Sidebar () { } - label="Acme Inc" + label="Home" /> } - label="Acme Inc" + label="Account" + /> + } + label="Chat" />