computer-scienceangular-roadmapbackend-roadmapblockchain-roadmapdba-roadmapdeveloper-roadmapdevops-roadmapfrontend-roadmapgo-roadmaphactoberfestjava-roadmapjavascript-roadmapnodejs-roadmappython-roadmapqa-roadmapreact-roadmaproadmapstudy-planvue-roadmapweb3-roadmap
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
9.2 KiB
253 lines
9.2 KiB
import { ArrowUpRight, Ban, Cog, Telescope, Wand } from 'lucide-react'; |
|
import type { FormEvent } from 'react'; |
|
import { useEffect, useState } from 'react'; |
|
import { isLoggedIn } from '../../lib/jwt'; |
|
import { showLoginPopup } from '../../lib/popup'; |
|
import { cn } from '../../lib/classname.ts'; |
|
import { AITermSuggestionInput } from './AITermSuggestionInput.tsx'; |
|
import { IncreaseRoadmapLimit } from './IncreaseRoadmapLimit.tsx'; |
|
|
|
type RoadmapSearchProps = { |
|
roadmapTerm: string; |
|
setRoadmapTerm: (topic: string) => void; |
|
handleSubmit: (e: FormEvent<HTMLFormElement>) => void; |
|
loadAIRoadmapLimit: () => void; |
|
onLoadTerm: (topic: string) => void; |
|
limit: number; |
|
limitUsed: number; |
|
isKeyOnly: boolean; |
|
}; |
|
|
|
export function RoadmapSearch(props: RoadmapSearchProps) { |
|
const { |
|
roadmapTerm, |
|
setRoadmapTerm, |
|
handleSubmit, |
|
limit = 0, |
|
limitUsed = 0, |
|
onLoadTerm, |
|
loadAIRoadmapLimit, |
|
isKeyOnly, |
|
} = props; |
|
|
|
const canGenerateMore = limitUsed < limit; |
|
const [isConfiguring, setIsConfiguring] = useState(false); |
|
const [isAuthenticatedUser, setIsAuthenticatedUser] = useState(false); |
|
const [isLoadingResults, setIsLoadingResults] = useState(false); |
|
|
|
useEffect(() => { |
|
setIsAuthenticatedUser(isLoggedIn()); |
|
}, []); |
|
|
|
const randomTerms = ['OAuth', 'UI / UX', 'SRE', 'DevRel']; |
|
|
|
return ( |
|
<div className="flex flex-grow flex-col items-center px-4 py-6 sm:px-6 md:my-24 lg:my-32"> |
|
{isConfiguring && ( |
|
<IncreaseRoadmapLimit |
|
onClose={() => { |
|
setIsConfiguring(false); |
|
loadAIRoadmapLimit(); |
|
}} |
|
/> |
|
)} |
|
<div className="flex flex-col gap-0 text-center sm:gap-2"> |
|
<h1 className="relative text-2xl font-medium sm:text-3xl"> |
|
<span className="hidden sm:inline">Generate roadmaps with AI</span> |
|
<span className="inline sm:hidden">AI Roadmap Generator</span> |
|
</h1> |
|
<p className="text-base text-gray-500 sm:text-lg"> |
|
<span className="hidden sm:inline"> |
|
Enter a topic and let the AI generate a roadmap for you |
|
</span> |
|
<span className="inline sm:hidden"> |
|
Enter a topic to generate a roadmap |
|
</span> |
|
</p> |
|
</div> |
|
<div className="my-3 flex w-full max-w-[600px] flex-col items-center gap-3 sm:my-5"> |
|
<form |
|
onSubmit={(e) => { |
|
if (limit > 0 && canGenerateMore) { |
|
handleSubmit(e); |
|
} else { |
|
e.preventDefault(); |
|
} |
|
}} |
|
className="flex w-full flex-col gap-2 sm:flex-row" |
|
> |
|
<AITermSuggestionInput |
|
autoFocus={true} |
|
value={roadmapTerm} |
|
onValueChange={(value) => setRoadmapTerm(value)} |
|
placeholder="Enter a topic to generate a roadmap for" |
|
wrapperClassName="w-full" |
|
onSelect={(roadmapId, roadmapTitle) => { |
|
onLoadTerm(roadmapTitle); |
|
}} |
|
/> |
|
<button |
|
className={cn( |
|
'flex min-w-[154px] 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', |
|
)} |
|
onClick={(e) => { |
|
if (!isAuthenticatedUser) { |
|
e.preventDefault(); |
|
showLoginPopup(); |
|
} |
|
}} |
|
disabled={ |
|
isLoadingResults || |
|
(isAuthenticatedUser && |
|
(!limit || !roadmapTerm || limitUsed >= limit || isKeyOnly)) |
|
} |
|
> |
|
{isLoadingResults && ( |
|
<> |
|
<span>Please wait..</span> |
|
</> |
|
)} |
|
|
|
{!isLoadingResults && ( |
|
<> |
|
{!isAuthenticatedUser && ( |
|
<> |
|
<Wand size={20} /> |
|
Generate |
|
</> |
|
)} |
|
{isAuthenticatedUser && ( |
|
<> |
|
{(!limit || canGenerateMore) && ( |
|
<> |
|
<Wand size={20} /> |
|
Generate |
|
</> |
|
)} |
|
|
|
{limit > 0 && !canGenerateMore && ( |
|
<span className="flex items-center text-base"> |
|
<Ban size={15} className="mr-2" /> |
|
Limit reached |
|
</span> |
|
)} |
|
</> |
|
)} |
|
</> |
|
)} |
|
</button> |
|
</form> |
|
<div className="flex flex-row flex-wrap items-center justify-center gap-2"> |
|
{randomTerms.map((term) => ( |
|
<button |
|
key={term} |
|
disabled={isAuthenticatedUser && (!limit || !canGenerateMore)} |
|
type="button" |
|
onClick={() => { |
|
if (!isAuthenticatedUser) { |
|
showLoginPopup(); |
|
return; |
|
} |
|
|
|
onLoadTerm(term); |
|
}} |
|
className="flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-sm transition-colors hover:border-black hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50" |
|
> |
|
{term} <ArrowUpRight size={17} /> |
|
</button> |
|
))} |
|
<a |
|
href="/ai/explore" |
|
className="flex items-center gap-1.5 rounded-full border border-black bg-gray-700 px-2 py-0.5 text-sm text-white transition-colors hover:border-black hover:bg-black" |
|
> |
|
Explore AI Roadmaps <Telescope size={17} /> |
|
</a> |
|
</div> |
|
</div> |
|
{!isAuthenticatedUser && ( |
|
<div className="mt-8 flex max-w-[500px] flex-col items-center gap-3 rounded-xl border border-gray-400 px-4 pb-4 pt-3"> |
|
<p className={'text-center text-gray-500'}> |
|
<button |
|
onClick={showLoginPopup} |
|
className="font-medium text-purple-600 underline underline-offset-2 hover:text-purple-800" |
|
> |
|
Sign up (free and takes 2s) or login |
|
</button>{' '} |
|
to start generating roadmaps. Or explore the ones made by the |
|
community. |
|
</p> |
|
<p className="flex flex-col gap-2 text-center text-gray-500 sm:flex-row"> |
|
<a |
|
href="/ai/explore" |
|
className="flex items-center gap-1.5 rounded-full border border-purple-600 px-2.5 py-0.5 text-sm text-purple-600 transition-colors hover:bg-purple-600 hover:text-white" |
|
> |
|
Explore AI Generated Roadmaps <Telescope size={15} /> |
|
</a> |
|
<a |
|
href="/roadmaps" |
|
className="flex items-center gap-1.5 rounded-full border border-purple-600 px-2.5 py-0.5 text-sm text-purple-600 transition-colors hover:bg-purple-600 hover:text-white" |
|
> |
|
Visit Official Roadmaps <ArrowUpRight size={15} /> |
|
</a> |
|
</p> |
|
</div> |
|
)} |
|
{isKeyOnly && isAuthenticatedUser && ( |
|
<div className="mx-auto mt-12 flex max-w-[450px] flex-col items-center gap-4"> |
|
<p className={'text-center text-red-500'}> |
|
We have hit the limit for AI roadmap generation. Please try again |
|
again later or{' '} |
|
<button |
|
onClick={() => setIsConfiguring(true)} |
|
className="font-semibold text-purple-600 underline underline-offset-2" |
|
> |
|
get more credits. |
|
</button> |
|
</p> |
|
|
|
<p className="flex flex-col gap-2 text-center text-gray-500 sm:flex-row"> |
|
<a |
|
href="/ai/explore" |
|
className="flex items-center gap-1.5 rounded-full border border-purple-600 px-2.5 py-0.5 text-sm text-purple-600 transition-colors hover:bg-purple-600 hover:text-white" |
|
> |
|
Explore AI Roadmaps <Telescope size={15} /> |
|
</a> |
|
<a |
|
href="/roadmaps" |
|
className="flex items-center gap-1.5 rounded-full border border-purple-600 px-2.5 py-0.5 text-sm text-purple-600 transition-colors hover:bg-purple-600 hover:text-white" |
|
> |
|
Visit Official Roadmaps <ArrowUpRight size={15} /> |
|
</a> |
|
</p> |
|
</div> |
|
)} |
|
{!isKeyOnly && limit > 0 && isAuthenticatedUser && ( |
|
<div className="mt-12 flex flex-col items-center gap-4"> |
|
<p className="text-center text-gray-500"> |
|
You have generated{' '} |
|
<span |
|
className={ |
|
'inline-block min-w-[50px] rounded-xl border px-1.5 text-center text-sm tabular-nums text-gray-800' |
|
} |
|
> |
|
{limitUsed} of {limit} |
|
</span>{' '} |
|
roadmaps today. |
|
</p> |
|
{isAuthenticatedUser && ( |
|
<p className="flex items-center text-sm"> |
|
<button |
|
onClick={() => setIsConfiguring(true)} |
|
className="rounded-xl border border-current px-2 py-0.5 text-sm text-blue-500 transition-colors hover:bg-blue-400 hover:text-white" |
|
> |
|
Need to generate more?{' '} |
|
<span className="font-semibold">Click here.</span> |
|
</button> |
|
</p> |
|
)} |
|
</div> |
|
)} |
|
</div> |
|
); |
|
}
|
|
|