diff --git a/src/components/TopicDetail/ResourceListSeparator.tsx b/src/components/TopicDetail/ResourceListSeparator.tsx index 1b8d8d60f..ce07255e4 100644 --- a/src/components/TopicDetail/ResourceListSeparator.tsx +++ b/src/components/TopicDetail/ResourceListSeparator.tsx @@ -27,7 +27,7 @@ export function ResourceListSeparator(props: ResourceSeparatorProps) { {Icon && } {text} -
+

); } diff --git a/src/components/TopicDetail/TopicDetailAI.tsx b/src/components/TopicDetail/TopicDetailAI.tsx index 7af6b6b49..18817ee76 100644 --- a/src/components/TopicDetail/TopicDetailAI.tsx +++ b/src/components/TopicDetail/TopicDetailAI.tsx @@ -26,6 +26,7 @@ import { readStream } from '../../lib/ai'; import { markdownToHtmlWithHighlighting } from '../../lib/markdown'; import type { ResourceType } from '../../lib/resource-progress'; import { getPercentage } from '../../lib/number'; +import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree'; type TopicDetailAIProps = { resourceId: string; @@ -51,6 +52,10 @@ export function TopicDetailAI(props: TopicDetailAIProps) { const textareaRef = useRef(null); const scrollareaRef = useRef(null); + const sanitizedTopicId = topicId?.includes('@') + ? topicId?.split('@')?.[1] + : topicId; + const toast = useToast(); const [message, setMessage] = useState(''); const [isStreamingMessage, setIsStreamingMessage] = useState(false); @@ -64,6 +69,20 @@ export function TopicDetailAI(props: TopicDetailAIProps) { const { data: userBillingDetails, isLoading: isBillingDetailsLoading } = useQuery(billingDetailsOptions(), queryClient); + const { data: roadmapTreeMapping, isLoading: isRoadmapTreeMappingLoading } = + useQuery( + { + ...roadmapTreeMappingOptions(resourceId), + select: (data) => { + const node = data.find( + (mapping) => mapping.nodeId === sanitizedTopicId, + ); + return node; + }, + }, + queryClient, + ); + const isLimitExceeded = (tokenUsage?.used || 0) >= (tokenUsage?.limit || 0); const isPaidUser = userBillingDetails?.status === 'active'; @@ -109,10 +128,6 @@ export function TopicDetailAI(props: TopicDetailAIProps) { try { setIsStreamingMessage(true); - const sanitizedTopicId = topicId?.includes('@') - ? topicId?.split('@')?.[1] - : topicId; - const response = await fetch( `${import.meta.env.PUBLIC_API_URL}/v1-topic-detail-chat`, { @@ -194,22 +209,61 @@ export function TopicDetailAI(props: TopicDetailAIProps) { scrollToBottom(); }, []); - const isDataLoading = isLoading || isBillingDetailsLoading; + const isDataLoading = + isLoading || isBillingDetailsLoading || isRoadmapTreeMappingLoading; const usagePercentage = getPercentage( tokenUsage?.used || 0, tokenUsage?.limit || 0, ); + const hasSubjects = + roadmapTreeMapping?.subjects && roadmapTreeMapping?.subjects?.length > 0; return ( -
-
-

- - AI Tutor -

+
+ {hasSubjects && ( +
+

+ Complete the following courses on AI Tutor +

+ +
+ {roadmapTreeMapping?.subjects?.map((subject) => { + return ( + + {subject} + + ); + })} +
+
+ )} + +
+ {hasSubjects && ( + + or start chatting with AI + + )} + + {!hasSubjects && ( +

+ + AI Tutor +

+ )} {!isDataLoading && !isPaidUser && (

diff --git a/src/queries/roadmap-tree.ts b/src/queries/roadmap-tree.ts new file mode 100644 index 000000000..a94259ceb --- /dev/null +++ b/src/queries/roadmap-tree.ts @@ -0,0 +1,26 @@ +import { queryOptions } from '@tanstack/react-query'; +import { httpGet } from '../lib/query-http'; + +export interface RoadmapTreeDocument { + _id?: string; + roadmapId: string; + mapping: { + _id?: string; + nodeId: string; + text: string; + subjects: string[]; + }[]; + createdAt: Date; + updatedAt: Date; +} + +export function roadmapTreeMappingOptions(roadmapId: string) { + return queryOptions({ + queryKey: ['roadmap-tree-mapping', { roadmapId }], + queryFn: () => { + return httpGet( + `${import.meta.env.PUBLIC_API_URL}/v1-roadmap-tree-mapping/${roadmapId}`, + ); + }, + }); +}