Changes to the ai tutor URL

pull/8456/head
Kamran Ahmed 2 weeks ago
parent 0d9374285b
commit b75d36a8c0
  1. 2
      src/components/Analytics/GoogleAd.astro
  2. 2
      src/components/AuthenticationFlow/GitHubButton.tsx
  3. 10
      src/components/AuthenticationFlow/GoogleButton.tsx
  4. 7
      src/components/AuthenticationFlow/LinkedInButton.tsx
  5. 19
      src/components/Dashboard/DashboardAiRoadmaps.tsx
  6. 11
      src/components/Dashboard/ListDashboardCustomProgress.tsx
  7. 2
      src/components/ExploreAIRoadmap/AIRoadmapsList.tsx
  8. 2
      src/components/ExploreAIRoadmap/EmptyRoadmaps.tsx
  9. 2
      src/components/GenerateCourse/AICourse.tsx
  10. 2
      src/components/GenerateCourse/AICourseActions.tsx
  11. 2
      src/components/GenerateCourse/AICourseCard.tsx
  12. 6
      src/components/GenerateCourse/AICourseContent.tsx
  13. 2
      src/components/GenerateCourse/GenerateAICourse.tsx
  14. 2
      src/components/GenerateCourse/GetAICourse.tsx
  15. 6
      src/components/GenerateRoadmap/GenerateRoadmap.tsx
  16. 8
      src/components/GenerateRoadmap/RoadmapSearch.tsx
  17. 2
      src/components/GenerateRoadmap/RoadmapTopicDetail.tsx
  18. 10
      src/components/HeroSection/FavoriteRoadmaps.tsx
  19. 6
      src/components/Navigation/Navigation.astro
  20. 2
      src/components/RoadmapAlert.tsx
  21. 6
      src/components/RoadmapDropdownMenu/RoadmapDropdownMenu.tsx
  22. 2
      src/data/changelogs/ai-tutor-cpp-java-roadmaps.md
  23. 2
      src/helper/generate-ai-course.ts
  24. 4
      src/layouts/BaseLayout.astro
  25. 1
      src/layouts/SkeletonLayout.astro
  26. 0
      src/pages/ai-roadmaps/[aiRoadmapSlug].astro
  27. 2
      src/pages/ai-roadmaps/explore.astro
  28. 10
      src/pages/ai-roadmaps/index.astro
  29. 17
      src/pages/ai-tutor/index.astro
  30. 3
      src/pages/ai/[courseSlug].astro
  31. 14
      src/pages/ai/index.astro
  32. 3
      src/pages/ai/search.astro

@ -10,8 +10,8 @@
'roadmaps',
'community',
'start-here',
'ai-roadmaps',
'ai',
'ai-tutor',
'teams',
'about',
'account',

@ -127,7 +127,7 @@ export function GitHubButton(props: GitHubButtonProps) {
// For non authentication pages, we want to redirect back to the page
// the user was on before they clicked the social login button
if (!['/login', '/signup'].includes(window.location.pathname)) {
const pagePath = ['/respond-invite', '/befriend', '/r', '/ai'].includes(
const pagePath = ['/respond-invite', '/befriend', '/r', '/ai-roadmaps'].includes(
window.location.pathname,
)
? window.location.pathname + window.location.search

@ -1,9 +1,6 @@
import { useEffect, useState } from 'react';
import Cookies from 'js-cookie';
import {
FIRST_LOGIN_PARAM,
TOKEN_COOKIE_NAME,
setAuthToken,
FIRST_LOGIN_PARAM, setAuthToken
} from '../../lib/jwt';
import { httpGet } from '../../lib/http';
import { COURSE_PURCHASE_PARAM } from '../../lib/jwt';
@ -11,8 +8,7 @@ import { GoogleIcon } from '../ReactIcons/GoogleIcon.tsx';
import { Spinner } from '../ReactIcons/Spinner.tsx';
import { CHECKOUT_AFTER_LOGIN_KEY } from './CourseLoginPopup.tsx';
import {
getStoredUtmParams,
triggerUtmRegistration,
triggerUtmRegistration
} from '../../lib/browser.ts';
import { cn } from '../../lib/classname.ts';
@ -132,7 +128,7 @@ export function GoogleButton(props: GoogleButtonProps) {
'/respond-invite',
'/befriend',
'/r',
'/ai',
'/ai-roadmaps',
].includes(window.location.pathname)
? window.location.pathname + window.location.search
: window.location.pathname;

@ -1,10 +1,7 @@
import { useEffect, useState } from 'react';
import Cookies from 'js-cookie';
import {
FIRST_LOGIN_PARAM,
COURSE_PURCHASE_PARAM,
TOKEN_COOKIE_NAME,
setAuthToken,
COURSE_PURCHASE_PARAM, setAuthToken
} from '../../lib/jwt';
import { cn } from '../../lib/classname.ts';
import { httpGet } from '../../lib/http';
@ -131,7 +128,7 @@ export function LinkedInButton(props: LinkedInButtonProps) {
'/respond-invite',
'/befriend',
'/r',
'/ai',
'/ai-roadmaps',
].includes(window.location.pathname)
? window.location.pathname + window.location.search
: window.location.pathname;

@ -1,15 +1,6 @@
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
import { DashboardCustomProgressCard } from './DashboardCustomProgressCard';
import { DashboardCardLink } from './DashboardCardLink';
import { useState } from 'react';
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal';
import { Simulate } from 'react-dom/test-utils';
import {
ArrowUpRight,
Bot,
BrainCircuit,
Map,
PencilRuler,
BrainCircuit
} from 'lucide-react';
type DashboardAiRoadmapsProps = {
@ -30,7 +21,7 @@ export function DashboardAiRoadmaps(props: DashboardAiRoadmapsProps) {
<h2 className="text-xs uppercase text-gray-400">My AI Roadmaps</h2>
<a
href="/ai/explore"
href="/ai-roadmaps/explore"
className="rounded-full bg-gray-200 px-2.5 py-0.5 text-xs font-medium text-gray-700 hover:bg-gray-300 hover:text-black"
>
AI Generated Roadmaps
@ -41,7 +32,7 @@ export function DashboardAiRoadmaps(props: DashboardAiRoadmapsProps) {
<DashboardCardLink
className="mt-0"
icon={BrainCircuit}
href="/ai"
href="/ai-roadmaps"
title="Generate Roadmaps with AI"
description="You can generate your own roadmap with AI"
/>
@ -61,7 +52,7 @@ export function DashboardAiRoadmaps(props: DashboardAiRoadmapsProps) {
{roadmaps.map((roadmap) => (
<a
key={roadmap.id}
href={`/ai/${roadmap.slug}`}
href={`/ai-roadmaps/${roadmap.slug}`}
className="relative truncate rounded-md border bg-white p-2.5 text-left text-sm shadow-sm hover:border-gray-400 hover:bg-gray-50"
>
{roadmap.title}
@ -70,7 +61,7 @@ export function DashboardAiRoadmaps(props: DashboardAiRoadmapsProps) {
<a
className="flex items-center justify-center rounded-lg border border-dashed border-gray-300 bg-white p-2.5 text-sm font-medium text-gray-500 hover:bg-gray-50 hover:text-gray-600"
href={'/ai'}
href={'/ai-roadmaps'}
>
+ Generate New
</a>

@ -3,13 +3,8 @@ import { DashboardCustomProgressCard } from './DashboardCustomProgressCard';
import { DashboardCardLink } from './DashboardCardLink';
import { useState } from 'react';
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal';
import { Simulate } from 'react-dom/test-utils';
import {
ArrowUpRight,
Bot,
BrainCircuit,
Map,
PencilRuler,
BrainCircuit, PencilRuler
} from 'lucide-react';
type ListDashboardCustomProgressProps = {
@ -63,7 +58,7 @@ export function ListDashboardCustomProgress(
<DashboardCardLink
className="mt-0"
icon={BrainCircuit}
href="/ai"
href="/ai-roadmaps"
title="Generate Roadmaps with AI"
description="You can generate your own roadmap with AI"
/>
@ -99,7 +94,7 @@ export function ListDashboardCustomProgress(
<a
className="flex min-h-[80px] items-center justify-center rounded-lg border border-dashed border-gray-300 bg-white p-4 text-sm font-medium text-gray-500 hover:bg-gray-50 hover:text-gray-600"
href={'/ai'}
href={'/ai-roadmaps'}
onClick={(e) => {
if (!isAIGeneratedRoadmaps) {
e.preventDefault();

@ -26,7 +26,7 @@ export function AIRoadmapsList(props: AIRoadmapsListProps) {
return (
<ul className="mb-4 grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3">
{roadmaps.map((roadmap) => {
const roadmapLink = `/ai/${roadmap.slug}`;
const roadmapLink = `/ai-roadmaps/${roadmap.slug}`;
return (
<a

@ -12,7 +12,7 @@ export function EmptyRoadmaps() {
</p>
<div className="flex flex-col items-center gap-1 sm:flex-row sm:gap-1.5">
<a
href="/ai"
href="/ai-roadmaps"
className="flex w-full items-center gap-1.5 rounded-md bg-gray-900 px-3 py-1.5 text-xs text-white sm:w-auto sm:text-sm"
>
<Wand2 className="h-4 w-4" />

@ -68,7 +68,7 @@ export function AICourse(props: AICourseProps) {
});
}
window.location.href = `/ai-tutor/search?term=${encodeURIComponent(keyword)}&difficulty=${difficulty}&id=${sessionId}`;
window.location.href = `/ai/search?term=${encodeURIComponent(keyword)}&difficulty=${difficulty}&id=${sessionId}`;
}
return (

@ -63,7 +63,7 @@ export function AICourseActions(props: AICourseActionsType) {
{isOpen && (
<div className="absolute right-0 top-8 z-10 w-48 overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg">
<a
href={`/ai-tutor/${courseSlug}`}
href={`/ai/${courseSlug}`}
className="flex w-full items-center gap-1.5 p-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-black disabled:cursor-not-allowed disabled:opacity-70"
>
<Play className="h-3.5 w-3.5" />

@ -35,7 +35,7 @@ export function AICourseCard(props: AICourseCardProps) {
return (
<div className="relative">
<a
href={`/ai-tutor/${course.slug}`}
href={`/ai/${course.slug}`}
className="hover:border-gray-3 00 group relative flex w-full flex-col overflow-hidden rounded-lg border border-gray-200 bg-white p-4 text-left transition-all hover:bg-gray-50"
>
<div className="flex items-center justify-between">

@ -189,7 +189,7 @@ export function AICourseContent(props: AICourseContentProps) {
<p className="mt-5 text-sm text-black">
<a
href="/ai-tutor"
href="/ai"
className="font-medium underline underline-offset-2"
>
Back to AI Tutor
@ -201,7 +201,7 @@ export function AICourseContent(props: AICourseContentProps) {
{(isNotFound || !showUpgradeButton) && (
<div className="my-5">
<a
href="/ai-tutor"
href="/ai"
className="rounded-md bg-black px-6 py-2 text-sm font-medium text-white hover:bg-opacity-80"
>
Create a course with AI
@ -222,7 +222,7 @@ export function AICourseContent(props: AICourseContentProps) {
<div className="border-b border-gray-200 bg-gray-100">
<div className="flex items-center justify-between px-4 py-2">
<a
href="/ai-tutor"
href="/ai"
onClick={(e) => {
if (isViewingLesson) {
e.preventDefault();

@ -101,7 +101,7 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
options;
if (!isLoggedIn()) {
window.location.href = '/ai-tutor';
window.location.href = '/ai';
return;
}

@ -27,7 +27,7 @@ export function GetAICourse(props: GetAICourseProps) {
useEffect(() => {
if (!isLoggedIn()) {
window.location.href = '/ai-tutor';
window.location.href = '/ai';
}
}, [isLoggedIn]);

@ -19,7 +19,7 @@ import {
} from '../../lib/jwt';
import { RoadmapSearch } from './RoadmapSearch.tsx';
import { Spinner } from '../ReactIcons/Spinner.tsx';
import { Ban, Cog, Download, PenSquare, Save, Wand } from 'lucide-react';
import { Ban, Download, PenSquare, Save, Wand } from 'lucide-react';
import { ShareRoadmapButton } from '../ShareRoadmapButton.tsx';
import { httpGet, httpPost } from '../../lib/http.ts';
import { pageProgressMessage } from '../../stores/page.ts';
@ -199,7 +199,7 @@ export function GenerateRoadmap(props: GenerateRoadmapProps) {
roadmapSlug,
},
'',
`${origin}/ai/${roadmapSlug}`,
`${origin}/ai-roadmaps/${roadmapSlug}`,
);
}
@ -473,7 +473,7 @@ export function GenerateRoadmap(props: GenerateRoadmapProps) {
);
}
const pageUrl = `https://roadmap.sh/ai/${roadmapSlug}`;
const pageUrl = `https://roadmap.sh/ai-roadmaps/${roadmapSlug}`;
const canGenerateMore = roadmapLimitUsed < roadmapLimit || isPaidUser;
const isGenerateButtonDisabled =
isLoadingResults ||

@ -1,4 +1,4 @@
import { ArrowUpRight, Ban, Cog, Telescope, Wand } from 'lucide-react';
import { ArrowUpRight, Ban, Telescope, Wand } from 'lucide-react';
import type { FormEvent } from 'react';
import { useEffect, useState } from 'react';
import { isLoggedIn } from '../../lib/jwt';
@ -160,7 +160,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
</button>
))}
<a
href="/ai/explore"
href="/ai-roadmaps/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} />
@ -181,7 +181,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
</p>
<p className="flex flex-col gap-2 text-center text-gray-500 sm:flex-row">
<a
href="/ai/explore"
href="/ai-roadmaps/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} />
@ -210,7 +210,7 @@ export function RoadmapSearch(props: RoadmapSearchProps) {
<p className="flex flex-col gap-2 text-center text-gray-500 sm:flex-row">
<a
href="/ai/explore"
href="/ai-roadmaps/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} />

@ -208,7 +208,7 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
{!isStreaming && (
<div className="mt-4">
<a
href="/ai-tutor"
href="/ai"
className="mb-1 mt-2 inline-flex items-center rounded-md bg-yellow-400 px-3 py-2 text-sm font-medium text-gray-800 no-underline hover:bg-yellow-500"
>
Dive deeper using AI Tutor

@ -4,9 +4,7 @@ import {
Plus,
Sparkle,
Eye,
EyeOff,
Square,
SquareCheckBig,
EyeOff, SquareCheckBig
} from 'lucide-react';
import { useState } from 'react';
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions.tsx';
@ -145,7 +143,7 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
<>
No AI roadmaps found
<a
href="/ai"
href="/ai-roadmaps"
className="ml-1.5 inline-flex items-center gap-1 font-medium text-blue-500 underline-offset-2 hover:underline"
>
<SquareCheckBig className="size-3.5" strokeWidth={2.5} />
@ -160,7 +158,7 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
resourceId={aiRoadmap.id}
resourceType={'roadmap'}
resourceTitle={aiRoadmap.title}
url={`/ai/${aiRoadmap.slug}`}
url={`/ai-roadmaps/${aiRoadmap.slug}`}
percentageDone={0}
allowFavorite={false}
isTrackable={false}
@ -168,7 +166,7 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
))}
<a
href="/ai"
href="/ai-roadmaps"
className={
'flex h-full w-full items-center justify-center gap-1 overflow-hidden rounded-md border border-dashed border-gray-800 p-3 text-sm text-gray-400 hover:border-gray-600 hover:bg-gray-900 hover:text-gray-300'
}

@ -7,8 +7,6 @@ import { AccountDropdown } from './AccountDropdown';
import { CourseAnnouncement } from '../SQLCourse/CourseAnnouncement';
---
<CourseAnnouncement client:load />
<div class='bg-slate-900 py-5 text-white sm:py-8'>
<nav class='container flex items-center justify-between'>
<div class='flex items-center gap-5'>
@ -21,7 +19,7 @@ import { CourseAnnouncement } from '../SQLCourse/CourseAnnouncement';
</a>
<a
href='/ai-tutor'
href='/ai'
class='group relative inline text-gray-400 hover:text-white sm:hidden'
>
AI Tutor
@ -35,7 +33,7 @@ import { CourseAnnouncement } from '../SQLCourse/CourseAnnouncement';
</a>
<RoadmapDropdownMenu client:load />
<a
href='/ai-tutor'
href='/ai'
class='group relative mr-3 text-blue-300 hover:text-white'
>
AI Tutor

@ -56,7 +56,7 @@ export function RoadmapAlert(props: RoadmapAlertProps) {
Community Roadmaps
</a>
<a
href="/ai/explore"
href="/ai-roadmaps/explore"
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} />

@ -1,5 +1,5 @@
import { ChevronDown, Globe, Menu, Sparkles, Map } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { ChevronDown, Globe, Sparkles, Map } from 'lucide-react';
import { useEffect, useRef } from 'react';
import { useOutsideClick } from '../../hooks/use-outside-click';
import { cn } from '../../lib/classname';
import {
@ -17,7 +17,7 @@ const links = [
isHighlighted: true,
},
{
link: '/ai',
link: '/ai-roadmaps',
label: 'AI Roadmaps',
description: 'Generate roadmaps with AI',
Icon: Sparkles,

@ -13,7 +13,7 @@ date: 2025-04-03
We have revised the C++ and Java roadmaps and introduced an AI tutor to help you learn anything.
- We just launched an [AI Tutor](https://roadmap.sh/ai-tutor), just give it a topic, pick a difficulty level and it will generate a personalized study plan for you. There is a map view, quizzes an embedded chat to help you along the way.
- We just launched an [AI Tutor](https://roadmap.sh/ai), just give it a topic, pick a difficulty level and it will generate a personalized study plan for you. There is a map view, quizzes an embedded chat to help you along the way.
- [C++ roadmap](https://roadmap.sh/cpp) has been revised with improved content
- We have also redrawn the [Java roadmap](https://roadmap.sh/java) from scratch, replacing the deprecated items, adding new content and improving the overall structure.

@ -131,7 +131,7 @@ export async function generateCourse(options: GenerateCourseOptions) {
difficulty,
},
'',
`${origin}/ai-tutor/${extractedCourseSlug}`,
`${origin}/ai/${extractedCourseSlug}`,
);
}

@ -17,6 +17,7 @@ import Clarity from '../components/Analytics/Clarity.astro';
import RedditPixel from '../components/Analytics/RedditPixel.astro';
import GoogleAd from '../components/Analytics/GoogleAd.astro';
import GoogleAdSlot from '../components/Analytics/GoogleAdSlot.astro';
import { CourseAnnouncement } from '../components/SQLCourse/CourseAnnouncement';
export interface Props {
title: string;
@ -165,6 +166,9 @@ const gaPageIdentifier = Astro.url.pathname
<GoogleAd />
</head>
<body class='flex min-h-screen flex-col'>
<slot name='course-announcement'>
<CourseAnnouncement client:load />
</slot>
<slot name='page-header'>
<Navigation />
</slot>

@ -7,6 +7,7 @@ const props = Astro.props;
---
<BaseLayout {...props}>
<div slot='course-announcement'></div>
<div slot='page-header'></div>
<slot />
<div slot='page-footer'></div>

@ -3,7 +3,7 @@ import { ExploreAIRoadmap } from '../../components/ExploreAIRoadmap/ExploreAIRoa
import BaseLayout from '../../layouts/BaseLayout.astro';
---
<BaseLayout title='Explore AI Generated Roadmaps' permalink="/ai/explore">
<BaseLayout title='Explore AI Generated Roadmaps' permalink="/ai-roadmaps/explore">
<ExploreAIRoadmap client:load />
<div slot="changelog-banner" />
</BaseLayout>

@ -0,0 +1,10 @@
---
import { GenerateRoadmap } from '../../components/GenerateRoadmap/GenerateRoadmap';
import BaseLayout from '../../layouts/BaseLayout.astro';
import { CheckSubscriptionVerification } from '../../components/Billing/CheckSubscriptionVerification';
---
<BaseLayout title='Roadmap AI' permalink='/ai-roadmaps'>
<GenerateRoadmap client:load />
<CheckSubscriptionVerification client:load />
</BaseLayout>

@ -1,17 +0,0 @@
---
import { AICourse } from '../../components/GenerateCourse/AICourse';
import BaseLayout from '../../layouts/BaseLayout.astro';
import { CheckSubscriptionVerification } from '../../components/Billing/CheckSubscriptionVerification';
const ogImage = 'https://roadmap.sh/og-images/ai-tutor.png';
---
<BaseLayout
title='AI Tutor'
noIndex={true}
ogImageUrl={ogImage}
description='Learn anything with AI Tutor. Pick a topic, and the AI will guide you through the learning process.'
>
<AICourse client:load />
<CheckSubscriptionVerification client:load />
</BaseLayout>

@ -17,8 +17,9 @@ const { courseSlug } = Astro.params as Params;
briefTitle='AI Tutor'
description='AI Tutor'
keywords={['ai', 'tutor', 'education', 'learning']}
canonicalUrl={`/ai-tutor/${courseSlug}`}
canonicalUrl={`/ai/${courseSlug}`}
>
<div slot='course-announcement'></div>
<GetAICourse client:load courseSlug={courseSlug} />
<CheckSubscriptionVerification client:load />
</SkeletonLayout>

@ -1,10 +1,18 @@
---
import { GenerateRoadmap } from '../../components/GenerateRoadmap/GenerateRoadmap';
import { AICourse } from '../../components/GenerateCourse/AICourse';
import BaseLayout from '../../layouts/BaseLayout.astro';
import { CheckSubscriptionVerification } from '../../components/Billing/CheckSubscriptionVerification';
const ogImage = 'https://roadmap.sh/og-images/ai-tutor.png';
---
<BaseLayout title='Roadmap AI' permalink='/ai'>
<GenerateRoadmap client:load />
<BaseLayout
title='Roadmap AI'
noIndex={true}
ogImageUrl={ogImage}
description='Learn anything with AI Tutor. Pick a topic, choose a difficulty level and the AI will guide you through the learning process.'
>
<div slot='course-announcement'></div>
<AICourse client:load />
<CheckSubscriptionVerification client:load />
</BaseLayout>

@ -9,7 +9,8 @@ import { CheckSubscriptionVerification } from '../../components/Billing/CheckSub
briefTitle='AI Tutor'
description='AI Tutor'
keywords={['ai', 'tutor', 'education', 'learning']}
canonicalUrl='/ai-tutor/search'
canonicalUrl='/ai/search'
noIndex={true}
>
<GenerateAICourse client:load />
<CheckSubscriptionVerification client:load />
Loading…
Cancel
Save