Add open ai saving

fix/ai-roadmap
Kamran Ahmed 9 months ago
parent 612e750370
commit 943def6d7c
  1. 10
      src/components/GenerateRoadmap/GenerateRoadmap.tsx
  2. 14
      src/components/GenerateRoadmap/OpenAISettings.tsx
  3. 6
      src/components/GenerateRoadmap/RoadmapSearch.tsx
  4. 31
      src/components/GenerateRoadmap/RoadmapTopicDetail.tsx
  5. 6
      src/lib/jwt.ts

@ -13,7 +13,7 @@ import { renderFlowJSON } from '../../../editor/renderer/renderer';
import { replaceChildren } from '../../lib/dom';
import { readAIRoadmapStream } from '../../helper/read-stream';
import {
getOpenAPIKey,
getOpenAIKey,
isLoggedIn,
removeAuthToken,
visitAIRoadmap,
@ -103,7 +103,7 @@ export function GenerateRoadmap() {
const [roadmapTopicLimitUsed, setRoadmapTopicLimitUsed] = useState(0);
const [isConfiguring, setIsConfiguring] = useState(false);
const openAPIKey = getOpenAPIKey();
const openAPIKey = getOpenAIKey();
const renderRoadmap = async (roadmap: string) => {
const { nodes, edges } = generateAIRoadmapFromText(roadmap);
@ -401,6 +401,10 @@ export function GenerateRoadmap() {
nodeType={selectedNode.nodeType}
nodeTitle={selectedNode.nodeTitle}
parentTitle={selectedNode.parentTitle}
onConfigureOpenAI={() => {
setSelectedNode(null);
setIsConfiguring(true);
}}
onClose={() => {
setSelectedNode(null);
loadAIRoadmapLimit().finally(() => {});
@ -470,7 +474,7 @@ export function GenerateRoadmap() {
className="flex flex-row items-center gap-1 rounded-xl border border-current px-2 py-0.5 text-sm text-blue-500 transition-colors hover:bg-blue-400 hover:text-white"
>
<Cog size={15} />
Configure OpenAI API key
Configure OpenAI key
</button>
)}
</div>

@ -1,9 +1,9 @@
import { Modal } from '../Modal.tsx';
import { useEffect, useState } from 'react';
import {
deleteOpenAPIKey,
getOpenAPIKey,
saveOpenAPIKey,
deleteOpenAIKey,
getOpenAIKey,
saveOpenAIKey,
} from '../../lib/jwt.ts';
import { cn } from '../../lib/classname.ts';
import { CloseIcon } from '../ReactIcons/CloseIcon.tsx';
@ -26,7 +26,7 @@ export function OpenAISettings(props: OpenAISettingsProps) {
const toast = useToast();
useEffect(() => {
const apiKey = getOpenAPIKey();
const apiKey = getOpenAIKey();
setOpenaiApiKey(apiKey || '');
setDefaultOpenAIKey(apiKey || '');
}, []);
@ -59,7 +59,7 @@ export function OpenAISettings(props: OpenAISettingsProps) {
const normalizedKey = openaiApiKey.trim();
if (!normalizedKey) {
deleteOpenAPIKey();
deleteOpenAIKey();
toast.success('OpenAI API key removed');
onClose();
return;
@ -85,7 +85,7 @@ export function OpenAISettings(props: OpenAISettingsProps) {
}
// Save the API key to cookies
saveOpenAPIKey(normalizedKey);
saveOpenAIKey(normalizedKey);
toast.success('OpenAI API key saved');
onClose();
}}
@ -151,7 +151,7 @@ export function OpenAISettings(props: OpenAISettingsProps) {
<button
type="button"
onClick={() => {
deleteOpenAPIKey();
deleteOpenAIKey();
onClose();
toast.success('OpenAI API key removed');
}}

@ -7,7 +7,7 @@ import {
Wand,
} from 'lucide-react';
import type { FormEvent } from 'react';
import { getOpenAPIKey, isLoggedIn } from '../../lib/jwt';
import { getOpenAIKey, isLoggedIn } from '../../lib/jwt';
import { showLoginPopup } from '../../lib/popup';
import { cn } from '../../lib/classname.ts';
import { useState } from 'react';
@ -36,7 +36,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
const canGenerateMore = limitUsed < limit;
const [isConfiguring, setIsConfiguring] = useState(false);
const openAPIKey = getOpenAPIKey();
const openAPIKey = getOpenAIKey();
const randomTerms = ['OAuth', 'APIs', 'UX Design', 'gRPC'];
@ -178,7 +178,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
className="flex flex-row items-center gap-1 rounded-xl border border-current px-2 py-0.5 text-sm text-blue-500 transition-colors hover:bg-blue-400 hover:text-white"
>
<Cog size={15} />
Configure OpenAI API key
Configure OpenAI key
</button>
)}
</p>

@ -3,13 +3,14 @@ import { useEffect, useMemo, useRef, useState } from 'react';
import { useKeydown } from '../../hooks/use-keydown';
import { useOutsideClick } from '../../hooks/use-outside-click';
import { markdownToHtml } from '../../lib/markdown';
import { Ban, FileText, X } from 'lucide-react';
import { Ban, Cog, FileText, X } from 'lucide-react';
import { Spinner } from '../ReactIcons/Spinner';
import type { RoadmapNodeDetails } from './GenerateRoadmap';
import { isLoggedIn, removeAuthToken } from '../../lib/jwt';
import { getOpenAIKey, isLoggedIn, removeAuthToken } from '../../lib/jwt';
import { readAIRoadmapContentStream } from '../../helper/read-stream';
import { cn } from '../../lib/classname';
import { showLoginPopup } from '../../lib/popup';
import { OpenAISettings } from './OpenAISettings.tsx';
type RoadmapTopicDetailProps = RoadmapNodeDetails & {
onClose?: () => void;
@ -17,6 +18,7 @@ type RoadmapTopicDetailProps = RoadmapNodeDetails & {
topicLimitUsed: number;
topicLimit: number;
onTopicContentGenerateComplete?: () => void;
onConfigureOpenAI?: () => void;
};
export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
@ -28,6 +30,7 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
topicLimit,
topicLimitUsed,
onTopicContentGenerateComplete,
onConfigureOpenAI,
} = props;
const [isLoading, setIsLoading] = useState(false);
@ -121,6 +124,7 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
}, []);
const hasContent = topicHtml?.length > 0;
const openAIKey = getOpenAIKey();
return (
<div className={'relative z-50'}>
@ -129,7 +133,7 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
tabIndex={0}
className="fixed right-0 top-0 z-40 h-screen w-full overflow-y-auto bg-white p-4 focus:outline-0 sm:max-w-[600px] sm:p-6"
>
<div className="flex flex-col sm:flex-row items-start gap-2">
<div className="flex flex-col items-start gap-2 sm:flex-row">
<span>
<span
className={cn(
@ -149,8 +153,25 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
className="rounded-xl border border-current px-1.5 py-0.5 text-left text-sm font-medium text-blue-500 sm:text-center"
onClick={showLoginPopup}
>
Generate more by{' '}
<span className="font-semibold">logging in</span>
Generate more by <span className="font-semibold">logging in</span>
</button>
)}
{isLoggedIn() && !openAIKey && (
<button
className="rounded-xl border border-current px-1.5 py-0.5 text-left text-sm font-medium text-blue-500 sm:text-center"
onClick={onConfigureOpenAI}
>
By-pass all limits by{' '}
<span className="font-semibold">adding your own OpenAI Key</span>
</button>
)}
{isLoggedIn() && openAIKey && (
<button
className="flex items-center gap-1 rounded-xl border border-current px-1.5 py-0.5 text-left text-sm font-medium text-blue-500 sm:text-center"
onClick={onConfigureOpenAI}
>
<Cog className="-mt-0.5 inline-block h-4 w-4" />
Configure OpenAI Key
</button>
)}
</div>

@ -64,14 +64,14 @@ export function visitAIRoadmap(roadmapId: string) {
});
}
export function deleteOpenAPIKey() {
export function deleteOpenAIKey() {
Cookies.remove('oak', {
path: '/',
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
}
export function saveOpenAPIKey(apiKey: string) {
export function saveOpenAIKey(apiKey: string) {
Cookies.set('oak', apiKey, {
path: '/',
expires: 365,
@ -81,6 +81,6 @@ export function saveOpenAPIKey(apiKey: string) {
});
}
export function getOpenAPIKey() {
export function getOpenAIKey() {
return Cookies.get('oak');
}

Loading…
Cancel
Save