|
|
@ -23,6 +23,12 @@ import { cn } from '../../lib/classname.ts'; |
|
|
|
|
|
|
|
|
|
|
|
const ROADMAP_ID_REGEX = new RegExp('@ROADMAPID:(\\w+)@'); |
|
|
|
const ROADMAP_ID_REGEX = new RegExp('@ROADMAPID:(\\w+)@'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type GetAIRoadmapResponse = { |
|
|
|
|
|
|
|
id: string; |
|
|
|
|
|
|
|
topic: string; |
|
|
|
|
|
|
|
data: string; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
export function GenerateRoadmap() { |
|
|
|
export function GenerateRoadmap() { |
|
|
|
const roadmapContainerRef = useRef<HTMLDivElement>(null); |
|
|
|
const roadmapContainerRef = useRef<HTMLDivElement>(null); |
|
|
|
|
|
|
|
|
|
|
@ -33,6 +39,8 @@ export function GenerateRoadmap() { |
|
|
|
const [isLoading, setIsLoading] = useState(false); |
|
|
|
const [isLoading, setIsLoading] = useState(false); |
|
|
|
const [roadmapTopic, setRoadmapTopic] = useState(''); |
|
|
|
const [roadmapTopic, setRoadmapTopic] = useState(''); |
|
|
|
const [generatedRoadmap, setGeneratedRoadmap] = useState(''); |
|
|
|
const [generatedRoadmap, setGeneratedRoadmap] = useState(''); |
|
|
|
|
|
|
|
const [currentRoadmap, setCurrentRoadmap] = |
|
|
|
|
|
|
|
useState<GetAIRoadmapResponse | null>(null); |
|
|
|
|
|
|
|
|
|
|
|
const [roadmapLimit, setRoadmapLimit] = useState(0); |
|
|
|
const [roadmapLimit, setRoadmapLimit] = useState(0); |
|
|
|
const [roadmapLimitUsed, setRoadmapLimitUsed] = useState(0); |
|
|
|
const [roadmapLimitUsed, setRoadmapLimitUsed] = useState(0); |
|
|
@ -51,6 +59,10 @@ export function GenerateRoadmap() { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (roadmapTopic === currentRoadmap?.topic) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
setIsLoading(true); |
|
|
|
setIsLoading(true); |
|
|
|
setHasSubmitted(true); |
|
|
|
setHasSubmitted(true); |
|
|
|
|
|
|
|
|
|
|
@ -104,6 +116,11 @@ export function GenerateRoadmap() { |
|
|
|
const roadmapId = result.match(ROADMAP_ID_REGEX)?.[1] || ''; |
|
|
|
const roadmapId = result.match(ROADMAP_ID_REGEX)?.[1] || ''; |
|
|
|
setUrlParams({ id: roadmapId }); |
|
|
|
setUrlParams({ id: roadmapId }); |
|
|
|
result = result.replace(ROADMAP_ID_REGEX, ''); |
|
|
|
result = result.replace(ROADMAP_ID_REGEX, ''); |
|
|
|
|
|
|
|
setCurrentRoadmap({ |
|
|
|
|
|
|
|
id: roadmapId, |
|
|
|
|
|
|
|
topic: roadmapTopic, |
|
|
|
|
|
|
|
data: result, |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
await renderRoadmap(result); |
|
|
|
await renderRoadmap(result); |
|
|
@ -150,11 +167,17 @@ export function GenerateRoadmap() { |
|
|
|
|
|
|
|
|
|
|
|
if (error || !response) { |
|
|
|
if (error || !response) { |
|
|
|
toast.error(error?.message || 'Something went wrong'); |
|
|
|
toast.error(error?.message || 'Something went wrong'); |
|
|
|
|
|
|
|
pageProgressMessage.set(''); |
|
|
|
setIsLoading(false); |
|
|
|
setIsLoading(false); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
window.location.href = `${import.meta.env.PUBLIC_EDITOR_APP_URL}/${response.roadmapId}`; |
|
|
|
setIsLoading(false); |
|
|
|
|
|
|
|
pageProgressMessage.set(''); |
|
|
|
|
|
|
|
window.open( |
|
|
|
|
|
|
|
`${import.meta.env.PUBLIC_EDITOR_APP_URL}/${response.roadmapId}`, |
|
|
|
|
|
|
|
'_blank', |
|
|
|
|
|
|
|
); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const downloadGeneratedRoadmap = async () => { |
|
|
|
const downloadGeneratedRoadmap = async () => { |
|
|
@ -208,6 +231,11 @@ export function GenerateRoadmap() { |
|
|
|
const { topic, data } = response; |
|
|
|
const { topic, data } = response; |
|
|
|
await renderRoadmap(data); |
|
|
|
await renderRoadmap(data); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setCurrentRoadmap({ |
|
|
|
|
|
|
|
id: roadmapId, |
|
|
|
|
|
|
|
topic, |
|
|
|
|
|
|
|
data, |
|
|
|
|
|
|
|
}); |
|
|
|
setRoadmapTopic(topic); |
|
|
|
setRoadmapTopic(topic); |
|
|
|
setGeneratedRoadmap(data); |
|
|
|
setGeneratedRoadmap(data); |
|
|
|
}; |
|
|
|
}; |
|
|
@ -217,7 +245,7 @@ export function GenerateRoadmap() { |
|
|
|
}, []); |
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
useEffect(() => { |
|
|
|
if (!roadmapId) { |
|
|
|
if (!roadmapId || roadmapId === currentRoadmap?.id) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -225,7 +253,7 @@ export function GenerateRoadmap() { |
|
|
|
loadAIRoadmap(roadmapId).finally(() => { |
|
|
|
loadAIRoadmap(roadmapId).finally(() => { |
|
|
|
pageProgressMessage.set(''); |
|
|
|
pageProgressMessage.set(''); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}, [roadmapId]); |
|
|
|
}, [roadmapId, currentRoadmap]); |
|
|
|
|
|
|
|
|
|
|
|
if (!hasSubmitted) { |
|
|
|
if (!hasSubmitted) { |
|
|
|
return ( |
|
|
|
return ( |
|
|
@ -282,12 +310,12 @@ export function GenerateRoadmap() { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<form |
|
|
|
<form |
|
|
|
onSubmit={handleSubmit} |
|
|
|
onSubmit={handleSubmit} |
|
|
|
className="my-3 flex w-full flex-col sm:flex-row sm:items-center sm:justify-center gap-2" |
|
|
|
className="my-3 flex w-full flex-col gap-2 sm:flex-row sm:items-center sm:justify-center" |
|
|
|
> |
|
|
|
> |
|
|
|
<input |
|
|
|
<input |
|
|
|
type="text" |
|
|
|
type="text" |
|
|
|
autoFocus |
|
|
|
autoFocus |
|
|
|
placeholder="e.g. Ansible" |
|
|
|
placeholder="e.g. Try searching for Ansible or DevOps" |
|
|
|
className="flex-grow rounded-md border border-gray-400 px-3 py-2 transition-colors focus:border-black focus:outline-none" |
|
|
|
className="flex-grow rounded-md border border-gray-400 px-3 py-2 transition-colors focus:border-black focus:outline-none" |
|
|
|
value={roadmapTopic} |
|
|
|
value={roadmapTopic} |
|
|
|
onInput={(e) => |
|
|
|
onInput={(e) => |
|
|
@ -297,14 +325,15 @@ export function GenerateRoadmap() { |
|
|
|
<button |
|
|
|
<button |
|
|
|
type={'submit'} |
|
|
|
type={'submit'} |
|
|
|
className={cn( |
|
|
|
className={cn( |
|
|
|
'flex min-w-[127px] flex-shrink-0 items-center gap-2 rounded-md bg-black px-4 py-2 text-white justify-center', |
|
|
|
'flex min-w-[127px] flex-shrink-0 items-center justify-center gap-2 rounded-md bg-black px-4 py-2 text-white', |
|
|
|
{ |
|
|
|
'disabled:cursor-not-allowed disabled:opacity-50', |
|
|
|
'cursor-not-allowed opacity-50': |
|
|
|
|
|
|
|
!roadmapLimit || |
|
|
|
|
|
|
|
!roadmapTopic || |
|
|
|
|
|
|
|
roadmapLimitUsed >= roadmapLimit, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
)} |
|
|
|
)} |
|
|
|
|
|
|
|
disabled={ |
|
|
|
|
|
|
|
!roadmapLimit || |
|
|
|
|
|
|
|
!roadmapTopic || |
|
|
|
|
|
|
|
roadmapLimitUsed >= roadmapLimit || |
|
|
|
|
|
|
|
roadmapTopic === currentRoadmap?.topic |
|
|
|
|
|
|
|
} |
|
|
|
> |
|
|
|
> |
|
|
|
{roadmapLimit > 0 && canGenerateMore && ( |
|
|
|
{roadmapLimit > 0 && canGenerateMore && ( |
|
|
|
<> |
|
|
|
<> |
|
|
@ -354,7 +383,7 @@ export function GenerateRoadmap() { |
|
|
|
<div |
|
|
|
<div |
|
|
|
ref={roadmapContainerRef} |
|
|
|
ref={roadmapContainerRef} |
|
|
|
id="roadmap-container" |
|
|
|
id="roadmap-container" |
|
|
|
className="relative px-4 py-5 [&>svg]:mx-auto [&>svg]:max-w-[1300px]" |
|
|
|
className="pointer-events-none relative px-4 py-5 [&>svg]:mx-auto [&>svg]:max-w-[1300px]" |
|
|
|
/> |
|
|
|
/> |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
); |
|
|
|
); |
|
|
|