diff --git a/src/components/GenerateRoadmap/GenerateRoadmap.tsx b/src/components/GenerateRoadmap/GenerateRoadmap.tsx index 174e8c3b5..e935e3a07 100644 --- a/src/components/GenerateRoadmap/GenerateRoadmap.tsx +++ b/src/components/GenerateRoadmap/GenerateRoadmap.tsx @@ -35,6 +35,7 @@ import { cn } from '../../lib/classname.ts'; import { RoadmapTopicDetail } from './RoadmapTopicDetail.tsx'; import { AIRoadmapAlert } from './AIRoadmapAlert.tsx'; import { OpenAISettings } from './OpenAISettings.tsx'; +import { IS_KEY_ONLY_ROADMAP_GENERATION } from '../../lib/ai.ts'; export type GetAIRoadmapLimitResponse = { used: number; @@ -104,6 +105,7 @@ export function GenerateRoadmap() { const [isConfiguring, setIsConfiguring] = useState(false); const openAPIKey = getOpenAIKey(); + const isKeyOnly = IS_KEY_ONLY_ROADMAP_GENERATION; const renderRoadmap = async (roadmap: string) => { const { nodes, edges } = generateAIRoadmapFromText(roadmap); @@ -372,6 +374,7 @@ export function GenerateRoadmap() { limit={roadmapLimit} limitUsed={roadmapLimitUsed} loadAIRoadmapLimit={loadAIRoadmapLimit} + isKeyOnly={isKeyOnly} onLoadTerm={(term: string) => { setRoadmapTerm(term); loadTermRoadmap(term).finally(() => {}); @@ -427,63 +430,93 @@ export function GenerateRoadmap() { )} {!isLoading && ( -
+
-
- - - {roadmapLimitUsed} of {roadmapLimit} - {' '} - roadmaps generated. - - {!isLoggedInUser && ( - +

+ )} + {openAPIKey && ( +

+ You have added your own OpenAI API key.{' '} + +

+ )} +
+ )} + {!isKeyOnly && ( +
+ + + {roadmapLimitUsed} of {roadmapLimit} {' '} - or logging in - - )} - {isLoggedInUser && !openAPIKey && ( - - )} + roadmaps generated. + + {!isLoggedInUser && ( + + )} + {isLoggedInUser && !openAPIKey && ( + + )} - {isLoggedInUser && openAPIKey && ( - - )} -
+ {isLoggedInUser && openAPIKey && ( + + )} +
+ )}
= roadmapLimit || - roadmapTerm === currentRoadmap?.term + roadmapTerm === currentRoadmap?.term || + (isKeyOnly && !openAPIKey) } > {roadmapLimit > 0 && canGenerateMore && ( @@ -514,7 +548,7 @@ export function GenerateRoadmap() { {roadmapLimit === 0 && Please wait..} {roadmapLimit > 0 && !canGenerateMore && ( - + Limit reached diff --git a/src/components/GenerateRoadmap/OpenAISettings.tsx b/src/components/GenerateRoadmap/OpenAISettings.tsx index 2b30e3f98..29c8e0820 100644 --- a/src/components/GenerateRoadmap/OpenAISettings.tsx +++ b/src/components/GenerateRoadmap/OpenAISettings.tsx @@ -1,10 +1,6 @@ import { Modal } from '../Modal.tsx'; import { useEffect, useState } from 'react'; -import { - deleteOpenAIKey, - getOpenAIKey, - saveOpenAIKey, -} from '../../lib/jwt.ts'; +import { deleteOpenAIKey, getOpenAIKey, saveOpenAIKey } from '../../lib/jwt.ts'; import { cn } from '../../lib/classname.ts'; import { CloseIcon } from '../ReactIcons/CloseIcon.tsx'; import { useToast } from '../../hooks/use-toast.ts'; @@ -121,6 +117,10 @@ export function OpenAISettings(props: OpenAISettingsProps) { )}
+

+ We do not store your API key on our servers. +

+ {hasError && (

Please enter a valid OpenAI API key diff --git a/src/components/GenerateRoadmap/RoadmapSearch.tsx b/src/components/GenerateRoadmap/RoadmapSearch.tsx index 1797608c5..152b067cc 100644 --- a/src/components/GenerateRoadmap/RoadmapSearch.tsx +++ b/src/components/GenerateRoadmap/RoadmapSearch.tsx @@ -21,6 +21,7 @@ type RoadmapSearchProps = { onLoadTerm: (topic: string) => void; limit: number; limitUsed: number; + isKeyOnly: boolean; }; export function RoadmapSearch(props: RoadmapSearchProps) { @@ -32,16 +33,18 @@ export function RoadmapSearch(props: RoadmapSearchProps) { limitUsed = 0, onLoadTerm, loadAIRoadmapLimit, + isKeyOnly, } = props; const canGenerateMore = limitUsed < limit; const [isConfiguring, setIsConfiguring] = useState(false); const openAPIKey = getOpenAIKey(); + const isAuthenticatedUser = isLoggedIn(); const randomTerms = ['OAuth', 'APIs', 'UX Design', 'gRPC']; return ( -

+
{isConfiguring && ( { @@ -50,7 +53,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) { }} /> )} -
+

Generate roadmaps with AI AI Roadmap Generator @@ -90,30 +93,57 @@ export function RoadmapSearch(props: RoadmapSearchProps) { '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', )} - disabled={!limit || !roadmapTerm || limitUsed >= limit} + onClick={(e) => { + if (!isAuthenticatedUser) { + e.preventDefault(); + showLoginPopup(); + } + }} + disabled={ + isAuthenticatedUser && + (!limit || + !roadmapTerm || + limitUsed >= limit || + (isKeyOnly && !openAPIKey)) + } > - {(!limit || canGenerateMore) && ( + {!isAuthenticatedUser && ( <> Generate )} + {isAuthenticatedUser && ( + <> + {(!limit || canGenerateMore) && ( + <> + + Generate + + )} - {limit > 0 && !canGenerateMore && ( - - - Limit reached - + {limit > 0 && !canGenerateMore && ( + + + Limit reached + + )} + )} -
+
{randomTerms.map((term) => (
-
-

- You have generated{' '} - - {limitUsed} of {limit} - {' '} - roadmaps. -

-

- {limit > 0 && !isLoggedIn() && ( + {!isAuthenticatedUser && ( +

+

- )} -

-

- {limit > 0 && isLoggedIn() && !openAPIKey && ( - {' '} + to start generating roadmaps. Or explore the ones made by the + community. +

+

+ - By-pass all limits by{' '} - - adding your own OpenAI API key - - + Explore AI Generated Roadmaps + + + Visit Official Roadmaps + +

+
+ )} + {isKeyOnly && isAuthenticatedUser && ( +
+ {!openAPIKey && ( + <> +

+ We have hit the limit for AI roadmap generation. Please try + again later or{' '} + +

+ + )} + {openAPIKey && ( +

+ You have added your own OpenAI API key.{' '} + +

)} - {limit > 0 && isLoggedIn() && openAPIKey && ( - + Explore AI Roadmaps + + + Visit Official Roadmaps + +

+
+ )} + {!isKeyOnly && limit > 0 && isAuthenticatedUser && ( +
+

+ You have generated{' '} + + {limitUsed} of {limit} + {' '} + roadmaps. +

+ {isAuthenticatedUser && ( +

+ {!openAPIKey && ( + + )} + + {openAPIKey && ( + + )} +

)} -

-
+
+ )}

); } diff --git a/src/lib/ai.ts b/src/lib/ai.ts new file mode 100644 index 000000000..6481f0f2c --- /dev/null +++ b/src/lib/ai.ts @@ -0,0 +1 @@ +export const IS_KEY_ONLY_ROADMAP_GENERATION = false; \ No newline at end of file