diff --git a/src/components/ExploreAIRoadmap/ExploreAIRoadmap.tsx b/src/components/ExploreAIRoadmap/ExploreAIRoadmap.tsx index 616b58709..7a905dfd4 100644 --- a/src/components/ExploreAIRoadmap/ExploreAIRoadmap.tsx +++ b/src/components/ExploreAIRoadmap/ExploreAIRoadmap.tsx @@ -1,19 +1,14 @@ -import { useEffect, useState, useCallback } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useToast } from '../../hooks/use-toast'; import { httpGet } from '../../lib/http'; import { getRelativeTimeString } from '../../lib/date'; -import { - BadgeCheck, - CalendarCheck, - Eye, - Loader2, - RefreshCcw, - Sparkles, -} from 'lucide-react'; +import { Eye, Loader2, RefreshCcw } from 'lucide-react'; +import { AIRoadmapAlert } from '../GenerateRoadmap/AIRoadmapAlert.tsx'; export interface AIRoadmapDocument { _id?: string; - topic: string; + term: string; + title: string; data: string; viewCount: number; createdAt: Date; @@ -37,7 +32,7 @@ export function ExploreAIRoadmap() { const [currPage, setCurrPage] = useState(1); const [totalPages, setTotalPages] = useState(1); - const loadAIRoadamps = useCallback( + const loadAIRoadmaps = useCallback( async (currPage: number) => { const { response, error } = await httpGet( `${import.meta.env.PUBLIC_API_URL}/v1-list-ai-roadmaps`, @@ -67,7 +62,7 @@ export function ExploreAIRoadmap() { ); useEffect(() => { - loadAIRoadamps(currPage).finally(() => { + loadAIRoadmaps(currPage).finally(() => { setIsLoading(false); }); }, []); @@ -75,99 +70,64 @@ export function ExploreAIRoadmap() { const hasMorePages = currPage < totalPages; return ( -
-
-

- Explore AI Generated Roadmaps -

-

- These roadmaps are generated by AI based on the data and the topic - community has provided. You can also create your own roadmap and share - it with the community. -

-
- - - Visit Official Roadmaps - - - · - - - - Generate Your Own Roadmap - -
+
+
+
+ {isLoading ? ( - + )}
)} diff --git a/src/components/GenerateRoadmap/AIRoadmapAlert.tsx b/src/components/GenerateRoadmap/AIRoadmapAlert.tsx new file mode 100644 index 000000000..4a76c9d7d --- /dev/null +++ b/src/components/GenerateRoadmap/AIRoadmapAlert.tsx @@ -0,0 +1,53 @@ +import { BadgeCheck, Telescope, Wand } from 'lucide-react'; + +type AIRoadmapAlertProps = { + isListing?: boolean; +}; + +export function AIRoadmapAlert(props: AIRoadmapAlertProps) { + const { isListing = false } = props; + + return ( +
+

+ AI Generated Roadmap{isListing ? 's' : ''}{' '} + + Beta + +

+

+ {isListing + ? 'These are AI generated roadmaps and are not verified by' + : 'This is an AI generated roadmap and is not verified by'}{' '} + roadmap.sh. We are currently in + beta and working hard to improve the quality of the generated roadmaps. +

+

+ {isListing ? ( + + + Create your own Roadmap with AI + + ) : ( + + + Explore other AI Roadmaps + + )} + + + Visit Official Roadmaps + +

+
+ ); +} diff --git a/src/components/GenerateRoadmap/GenerateRoadmap.tsx b/src/components/GenerateRoadmap/GenerateRoadmap.tsx index 35f034973..4923135d9 100644 --- a/src/components/GenerateRoadmap/GenerateRoadmap.tsx +++ b/src/components/GenerateRoadmap/GenerateRoadmap.tsx @@ -15,15 +15,7 @@ import { readAIRoadmapStream } from '../../helper/read-stream'; import { isLoggedIn, removeAuthToken, visitAIRoadmap } from '../../lib/jwt'; import { RoadmapSearch } from './RoadmapSearch.tsx'; import { Spinner } from '../ReactIcons/Spinner.tsx'; -import { - BadgeCheck, - Ban, - Download, - PenSquare, - Save, - Telescope, - Wand, -} from 'lucide-react'; +import { Ban, Download, PenSquare, Save, Wand } from 'lucide-react'; import { ShareRoadmapButton } from '../ShareRoadmapButton.tsx'; import { httpGet, httpPost } from '../../lib/http.ts'; import { pageProgressMessage } from '../../stores/page.ts'; @@ -36,6 +28,7 @@ import { downloadGeneratedRoadmapImage } from '../../helper/download-image.ts'; import { showLoginPopup } from '../../lib/popup.ts'; import { cn } from '../../lib/classname.ts'; import { RoadmapTopicDetail } from './RoadmapTopicDetail.tsx'; +import { AIRoadmapAlert } from './AIRoadmapAlert.tsx'; export type GetAIRoadmapLimitResponse = { used: number; @@ -77,7 +70,8 @@ export const allowedClickableNodeTypes = [ type GetAIRoadmapResponse = { id: string; - topic: string; + term: string; + title: string; data: string; }; @@ -89,7 +83,7 @@ export function GenerateRoadmap() { const [hasSubmitted, setHasSubmitted] = useState(false); const [isLoading, setIsLoading] = useState(false); - const [roadmapTopic, setRoadmapTopic] = useState(''); + const [roadmapTerm, setRoadmapTerm] = useState(''); const [generatedRoadmapContent, setGeneratedRoadmapContent] = useState(''); const [currentRoadmap, setCurrentRoadmap] = useState(null); @@ -110,7 +104,7 @@ export function GenerateRoadmap() { } }; - const loadTopic = async (topic: string) => { + const loadTermRoadmap = async (term: string) => { setIsLoading(true); setHasSubmitted(true); @@ -131,7 +125,7 @@ export function GenerateRoadmap() { 'Content-Type': 'application/json', }, credentials: 'include', - body: JSON.stringify({ topic: topic }), + body: JSON.stringify({ term }), }, ); @@ -167,7 +161,8 @@ export function GenerateRoadmap() { result = result.replace(ROADMAP_ID_REGEX, ''); setCurrentRoadmap({ id: roadmapId, - topic: roadmapTopic, + term: roadmapTerm, + title: term, data: result, }); } @@ -186,15 +181,15 @@ export function GenerateRoadmap() { const handleSubmit = async (e: FormEvent) => { e.preventDefault(); - if (!roadmapTopic) { + if (!roadmapTerm) { return; } - if (roadmapTopic === currentRoadmap?.topic) { + if (roadmapTerm === currentRoadmap?.topic) { return; } - loadTopic(roadmapTopic); + loadTermRoadmap(roadmapTerm).finally(() => null); }; const saveAIRoadmap = async () => { @@ -212,7 +207,7 @@ export function GenerateRoadmap() { }>( `${import.meta.env.PUBLIC_API_URL}/v1-save-ai-roadmap/${currentRoadmap?.id}`, { - title: roadmapTopic, + title: roadmapTerm, nodes: nodes.map((node) => ({ ...node, @@ -252,7 +247,7 @@ export function GenerateRoadmap() { } try { - await downloadGeneratedRoadmapImage(roadmapTopic, node); + await downloadGeneratedRoadmapImage(roadmapTerm, node); pageProgressMessage.set(''); } catch (error) { console.error(error); @@ -291,15 +286,17 @@ export function GenerateRoadmap() { return; } - const { topic, data } = response; + const { term, title, data } = response; await renderRoadmap(data); setCurrentRoadmap({ id: roadmapId, - topic, + title: title, + term: term, data, }); - setRoadmapTopic(topic); + + setRoadmapTerm(title); setGeneratedRoadmapContent(data); visitAIRoadmap(roadmapId); }; @@ -360,14 +357,14 @@ export function GenerateRoadmap() { if (!hasSubmitted) { return ( { - setRoadmapTopic(topic); - loadTopic(topic).finally(() => {}); + onLoadTerm={(term: string) => { + setRoadmapTerm(term); + loadTermRoadmap(term).finally(() => {}); }} /> ); @@ -406,37 +403,8 @@ export function GenerateRoadmap() { )} {!isLoading && ( -
-
-

- AI Generated Roadmap{' '} - - Beta - -

-

- This is an AI generated roadmap and is not verified by{' '} - roadmap.sh. We are - currently in beta and working hard to improve the quality of - the generated roadmaps. -

-

- - - Explore other AI Roadmaps - - - - Visit Official Roadmaps - -

-
+
+
- setRoadmapTopic((e.target as HTMLInputElement).value) + setRoadmapTerm((e.target as HTMLInputElement).value) } /> {roadmapId && ( )} diff --git a/src/components/GenerateRoadmap/RoadmapSearch.tsx b/src/components/GenerateRoadmap/RoadmapSearch.tsx index 6667a023c..bdc84fdf7 100644 --- a/src/components/GenerateRoadmap/RoadmapSearch.tsx +++ b/src/components/GenerateRoadmap/RoadmapSearch.tsx @@ -11,27 +11,27 @@ import { showLoginPopup } from '../../lib/popup'; import { cn } from '../../lib/classname.ts'; type RoadmapSearchProps = { - roadmapTopic: string; - setRoadmapTopic: (topic: string) => void; + roadmapTerm: string; + setRoadmapTerm: (topic: string) => void; handleSubmit: (e: FormEvent) => void; - onLoadTopic: (topic: string) => void; + onLoadTerm: (topic: string) => void; limit: number; limitUsed: number; }; export function RoadmapSearch(props: RoadmapSearchProps) { const { - roadmapTopic, - setRoadmapTopic, + roadmapTerm, + setRoadmapTerm, handleSubmit, limit = 0, limitUsed = 0, - onLoadTopic, + onLoadTerm, } = props; const canGenerateMore = limitUsed < limit; - const randomTopics = ['Linux', 'Prometheus', 'gRPC']; + const randomTerms = ['OAuth', 'APIs', 'UX Design', 'gRPC']; return (
@@ -65,9 +65,9 @@ export function RoadmapSearch(props: RoadmapSearchProps) { type="text" placeholder="Enter a topic to generate a roadmap for" className="w-full rounded-md border border-gray-400 px-3 py-2.5 transition-colors focus:border-black focus:outline-none" - value={roadmapTopic} + value={roadmapTerm} onInput={(e) => - setRoadmapTopic((e.target as HTMLInputElement).value) + setRoadmapTerm((e.target as HTMLInputElement).value) } />
- {randomTopics.map((topic) => ( + {randomTerms.map((term) => ( ))}