Resolve merge conflicts

fix/ai-roadmap
Kamran Ahmed 9 months ago
commit 1779992250
  1. 38
      src/components/GenerateRoadmap/GenerateRoadmap.tsx
  2. 53
      src/components/GenerateRoadmap/RoadmapTopicDetail.tsx
  3. 2
      src/lib/jwt.ts

@ -1,10 +1,10 @@
import {
type FormEvent,
type MouseEvent,
useCallback,
useEffect,
useRef,
useState,
useCallback,
type MouseEvent,
} from 'react';
import './GenerateRoadmap.css';
import { useToast } from '../../hooks/use-toast';
@ -19,7 +19,6 @@ import {
BadgeCheck,
Ban,
Download,
PencilRuler,
PenSquare,
Save,
Telescope,
@ -38,6 +37,13 @@ import { showLoginPopup } from '../../lib/popup.ts';
import { cn } from '../../lib/classname.ts';
import { RoadmapTopicDetail } from './RoadmapTopicDetail.tsx';
export type GetAIRoadmapLimitResponse = {
used: number;
limit: number;
topicUsed: number;
topicLimit: number;
};
const ROADMAP_ID_REGEX = new RegExp('@ROADMAPID:(\\w+)@');
export type RoadmapNodeDetails = {
@ -93,6 +99,8 @@ export function GenerateRoadmap() {
const [roadmapLimit, setRoadmapLimit] = useState(0);
const [roadmapLimitUsed, setRoadmapLimitUsed] = useState(0);
const [roadmapTopicLimit, setRoadmapTopicLimit] = useState(0);
const [roadmapTopicLimitUsed, setRoadmapTopicLimitUsed] = useState(0);
const renderRoadmap = async (roadmap: string) => {
const { nodes, edges } = generateAIRoadmapFromText(roadmap);
@ -249,19 +257,20 @@ export function GenerateRoadmap() {
};
const loadAIRoadmapLimit = async () => {
const { response, error } = await httpGet<{
limit: number;
used: number;
}>(`${import.meta.env.PUBLIC_API_URL}/v1-get-ai-roadmap-limit`);
const { response, error } = await httpGet<GetAIRoadmapLimitResponse>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-ai-roadmap-limit`,
);
if (error || !response) {
toast.error(error?.message || 'Something went wrong');
return;
}
const { limit, used } = response;
const { limit, used, topicLimit, topicUsed } = response;
setRoadmapLimit(limit);
setRoadmapLimitUsed(used);
setRoadmapTopicLimit(topicLimit);
setRoadmapTopicLimitUsed(topicUsed);
};
const loadAIRoadmap = async (roadmapId: string) => {
@ -369,6 +378,11 @@ export function GenerateRoadmap() {
parentTitle={selectedTopic.parentTitle}
onClose={() => setSelectedTopic(null)}
roadmapId={currentRoadmap?.id || ''}
topicLimit={roadmapTopicLimit}
topicLimitUsed={roadmapTopicLimitUsed}
onTopicContentGenerateComplete={async () => {
await loadAIRoadmapLimit();
}}
/>
)}
@ -389,23 +403,23 @@ export function GenerateRoadmap() {
Beta
</span>
</h2>
<p className="mt-1 mb-2">
<p className="mb-2 mt-1">
This is an AI generated roadmap and is not verified by{' '}
<span className={'font-semibold'}>roadmap.sh</span>. We are
currently in beta and working hard to improve the quality of
the generated roadmaps.
</p>
<p className="mt-2 mb-1.5 text-sm flex gap-2">
<p className="mb-1.5 mt-2 flex gap-2 text-sm">
<a
href="/ai/explore"
className="rounded-md text-yellow-700 border border-yellow-600 hover:bg-yellow-300 hover:text-yellow-800 px-2 py-1 flex items-center gap-1.5 transition-colors"
className="flex items-center gap-1.5 rounded-md border border-yellow-600 px-2 py-1 text-yellow-700 transition-colors hover:bg-yellow-300 hover:text-yellow-800"
>
<Telescope size={15} />
Explore other AI Roadmaps
</a>
<a
href="/roadmaps"
className="rounded-md border border-yellow-600 hover:bg-yellow-300 text-yellow-800 bg-yellow-200 px-2 py-1 flex items-center gap-1.5 transition-colors"
className="flex items-center gap-1.5 rounded-md border border-yellow-600 bg-yellow-200 px-2 py-1 text-yellow-800 transition-colors hover:bg-yellow-300"
>
<BadgeCheck size={15} />
Visit Official Roadmaps

@ -6,16 +6,29 @@ import { markdownToHtml } from '../../lib/markdown';
import { Ban, FileText, X } from 'lucide-react';
import { Spinner } from '../ReactIcons/Spinner';
import type { RoadmapNodeDetails } from './GenerateRoadmap';
import { removeAuthToken } from '../../lib/jwt';
import { isLoggedIn, removeAuthToken } from '../../lib/jwt';
import { readAIRoadmapContentStream } from '../../helper/read-stream';
import { cn } from '../../lib/classname';
import { showLoginPopup } from '../../lib/popup';
type RoadmapTopicDetailProps = RoadmapNodeDetails & {
onClose?: () => void;
roadmapId: string;
topicLimitUsed: number;
topicLimit: number;
onTopicContentGenerateComplete?: () => void;
};
export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
const { onClose, roadmapId, nodeTitle, parentTitle } = props;
const {
onClose,
roadmapId,
nodeTitle,
parentTitle,
topicLimit,
topicLimitUsed,
onTopicContentGenerateComplete,
} = props;
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
@ -28,6 +41,12 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
setIsLoading(true);
setError('');
if (topicLimitUsed >= topicLimit) {
setError('You have reached the limit of topics');
setIsLoading(false);
return;
}
if (!roadmapId || !nodeTitle) {
setIsLoading(false);
setError('Invalid roadmap id or node title');
@ -76,6 +95,7 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
setTopicHtml(markdownToHtml(result, false));
},
});
onTopicContentGenerateComplete?.();
};
// Close the topic detail when user clicks outside the topic detail
@ -109,8 +129,35 @@ 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 items-start gap-2">
<span>
<span
className={cn(
'mr-0.5 inline-block rounded-xl border px-1.5 text-center text-sm tabular-nums text-gray-800',
{
'animate-pulse border-zinc-300 bg-zinc-300 text-zinc-300':
!topicLimit,
},
)}
>
{topicLimitUsed} of {topicLimit}
</span>{' '}
topic content generated.
</span>
{!isLoggedIn() && (
<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={showLoginPopup}
>
Generate more by{' '}
<span className="font-semibold">signing up (free, takes 2s)</span>{' '}
or <span className="font-semibold">logging in</span>
</button>
)}
</div>
{isLoading && (
<div className="flex w-full justify-center">
<div className="mt-6 flex w-full justify-center">
<Spinner
outerFill="#d1d5db"
className="h-6 w-6 sm:h-12 sm:w-12"

@ -57,7 +57,7 @@ export function visitAIRoadmap(roadmapId: string) {
Cookies.set(`crv-${roadmapId}`, '1', {
path: '/',
expires: 30,
expires: 1 / 24, // 1 hour
sameSite: 'lax',
secure: !import.meta.env.DEV,
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',

Loading…
Cancel
Save