diff --git a/src/components/TopicDetail/PredefinedMessageGroup.tsx b/src/components/TopicDetail/PredefinedMessageGroup.tsx new file mode 100644 index 000000000..c65e06f8d --- /dev/null +++ b/src/components/TopicDetail/PredefinedMessageGroup.tsx @@ -0,0 +1,54 @@ +import type { LucideIcon } from 'lucide-react'; +import { useState, useRef } from 'react'; +import { useOutsideClick } from '../../hooks/use-outside-click'; +import { + type PredefinedMessage, + PredefinedMessageButton, +} from './PredefinedMessages'; + +type PredefinedMessageGroupProps = { + label: string; + icon: LucideIcon; + messages: Omit[]; + onSelect: (message: Omit) => void; +}; + +export function PredefinedMessageGroup(props: PredefinedMessageGroupProps) { + const { label, icon: Icon, messages, onSelect } = props; + + const [isOpen, setIsOpen] = useState(false); + const containerRef = useRef(null); + + useOutsideClick(containerRef, () => { + setIsOpen(false); + }); + + return ( +
+ setIsOpen(!isOpen)} + isGroup={true} + /> + + {isOpen && ( +
+ {messages.map((m) => { + return ( + { + onSelect(m); + setIsOpen(false); + }} + /> + ); + })} +
+ )} +
+ ); +} diff --git a/src/components/TopicDetail/PredefinedMessages.tsx b/src/components/TopicDetail/PredefinedMessages.tsx new file mode 100644 index 000000000..ba3d5dc44 --- /dev/null +++ b/src/components/TopicDetail/PredefinedMessages.tsx @@ -0,0 +1,120 @@ +import { + BabyIcon, + BookOpenTextIcon, + BrainIcon, + ChevronDownIcon, + NotebookPenIcon, + type LucideIcon, +} from 'lucide-react'; +import { useMemo } from 'react'; +import { cn } from '../../lib/classname'; +import { PredefinedMessageGroup } from './PredefinedMessageGroup'; + +export const testMyKnowledgePrompt = + 'Act as an interviewer and test my understanding of this topic'; +export const explainTopicPrompt = 'Explain this topic in detail'; + +export type PredefinedMessage = { + icon: LucideIcon; + label: string; +} & ( + | { + message?: never; + messages: PredefinedMessage[]; + } + | { + message: string; + messages?: never; + } +); + +type PredefinedMessagesProps = { + onSelect: (message: Omit) => void; +}; + +export function PredefinedMessages(props: PredefinedMessagesProps) { + const { onSelect } = props; + + const predefinedMessages: PredefinedMessage[] = useMemo( + () => [ + { + icon: BookOpenTextIcon, + label: 'Explain', + messages: [ + { + icon: NotebookPenIcon, + label: 'Explain Topic', + message: 'Explain this topic in detail and include examples', + }, + { + icon: BabyIcon, + label: 'Explain like I am five', + message: 'Explain this topic like I am a 5 years old', + }, + ], + }, + { + icon: BrainIcon, + label: 'Test my Knowledge', + message: testMyKnowledgePrompt, + }, + ], + [], + ); + + return ( +
+ {predefinedMessages.map((m) => { + const isGroup = 'messages' in m && Array.isArray(m?.messages); + if (!isGroup) { + return ( + { + onSelect(m); + }} + /> + ); + } + + return ( + + ); + })} +
+ ); +} + +type PredefinedMessageButtonProps = { + label: string; + icon?: LucideIcon; + onClick: () => void; + isGroup?: boolean; + className?: string; +}; + +export function PredefinedMessageButton(props: PredefinedMessageButtonProps) { + const { label, icon: Icon, onClick, isGroup = false, className } = props; + + return ( + + ); +} diff --git a/src/components/TopicDetail/TopicDetailAI.tsx b/src/components/TopicDetail/TopicDetailAI.tsx index f2c6d74e7..79e340163 100644 --- a/src/components/TopicDetail/TopicDetailAI.tsx +++ b/src/components/TopicDetail/TopicDetailAI.tsx @@ -37,6 +37,11 @@ import { getPercentage } from '../../lib/number'; import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree'; import { defaultChatHistory } from './TopicDetail'; import { AILimitsPopup } from '../GenerateCourse/AILimitsPopup'; +import { + explainTopicPrompt, + PredefinedMessages, + testMyKnowledgePrompt, +} from './PredefinedMessages'; type TopicDetailAIProps = { resourceId: string; @@ -229,27 +234,6 @@ export function TopicDetailAI(props: TopicDetailAIProps) { roadmapTreeMapping?.subjects && roadmapTreeMapping?.subjects?.length > 0; const hasChatHistory = aiChatHistory.length > 1; - const testMyKnowledgePrompt = - 'Act as an interviewer and test my understanding of this topic'; - const explainTopicPrompt = 'Explain this topic in detail'; - const predefinedMessages = useMemo( - () => [ - { - label: 'Explain like I am five', - message: 'Explain this topic like I am a 5 years old', - }, - { - label: 'Test my Knowledge', - message: testMyKnowledgePrompt, - }, - { - label: 'Explain Topic', - message: explainTopicPrompt, - }, - ], - [], - ); - return (
{isDataLoading && ( @@ -333,7 +317,7 @@ export function TopicDetailAI(props: TopicDetailAIProps) { {!isPaidUser && ( <>
-
- {predefinedMessages.map((m) => ( - { - setMessage(m.message); - handleChatSubmit(m.message); - }} - /> - ))} -
+ { + if (!m?.message) { + toast.error('Something went wrong'); + return; + } + + setMessage(m.message); + handleChatSubmit(m.message); + }} + />
); } - -type PredefinedMessageButtonProps = { - label: string; - message: string; - onClick: () => void; -}; - -function PredefinedMessageButton(props: PredefinedMessageButtonProps) { - const { label, message, onClick } = props; - - return ( - - ); -}