mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-22 17:02:58 +02:00
Improve AI Tutor landing page, and Sidebar (#8969)
* Add tree to AI tutor sidebar * Update featured card design * Featured card changes * Improve feature cards * Active item bug in ai tutor sidebar * Add demo button on premium page
This commit is contained in:
1
.astro/types.d.ts
vendored
1
.astro/types.d.ts
vendored
@@ -1,2 +1 @@
|
|||||||
/// <reference types="astro/client" />
|
/// <reference types="astro/client" />
|
||||||
/// <reference path="content.d.ts" />
|
|
@@ -1,26 +1,29 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import {
|
import {
|
||||||
BookOpen,
|
BookOpen,
|
||||||
|
ChevronDown,
|
||||||
|
ChevronRight,
|
||||||
Compass,
|
Compass,
|
||||||
|
FileText,
|
||||||
Map,
|
Map,
|
||||||
MessageCircle,
|
MessageCircle,
|
||||||
Plus,
|
|
||||||
Star,
|
Star,
|
||||||
Swords,
|
Swords,
|
||||||
X,
|
X,
|
||||||
|
Zap
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { isLoggedIn } from '../../lib/jwt';
|
import { getUrlParams } from '../../lib/browser';
|
||||||
import { useIsPaidUser } from '../../queries/billing';
|
|
||||||
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
|
||||||
import { AITutorLogo } from '../ReactIcons/AITutorLogo';
|
|
||||||
import { queryClient } from '../../stores/query-client';
|
|
||||||
import { aiLimitOptions } from '../../queries/ai-course';
|
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { getPercentage } from '../../lib/number';
|
|
||||||
import { AILimitsPopup } from '../GenerateCourse/AILimitsPopup';
|
|
||||||
import { cn } from '../../lib/classname';
|
import { cn } from '../../lib/classname';
|
||||||
import { UserDropdown } from './UserDropdown';
|
import { isLoggedIn } from '../../lib/jwt';
|
||||||
|
import { aiLimitOptions } from '../../queries/ai-course';
|
||||||
|
import { useIsPaidUser } from '../../queries/billing';
|
||||||
|
import { queryClient } from '../../stores/query-client';
|
||||||
|
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
||||||
|
import { AILimitsPopup } from '../GenerateCourse/AILimitsPopup';
|
||||||
|
import { AITutorLogo } from '../ReactIcons/AITutorLogo';
|
||||||
import { UpgradeSidebarCard } from './UpgradeSidebarCard';
|
import { UpgradeSidebarCard } from './UpgradeSidebarCard';
|
||||||
|
import { UserDropdown } from './UserDropdown';
|
||||||
|
|
||||||
type AITutorSidebarProps = {
|
type AITutorSidebarProps = {
|
||||||
isFloating: boolean;
|
isFloating: boolean;
|
||||||
@@ -33,13 +36,33 @@ const sidebarItems = [
|
|||||||
key: 'new',
|
key: 'new',
|
||||||
label: 'Create with AI',
|
label: 'Create with AI',
|
||||||
href: '/ai',
|
href: '/ai',
|
||||||
icon: Plus,
|
icon: Zap,
|
||||||
},
|
children: [
|
||||||
{
|
{
|
||||||
key: 'quiz',
|
key: 'create-course',
|
||||||
label: 'Test my Skills',
|
label: 'Course',
|
||||||
href: '/ai/quiz',
|
href: '/ai?format=course',
|
||||||
icon: Swords,
|
icon: BookOpen,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'create-guide',
|
||||||
|
label: 'Guide',
|
||||||
|
href: '/ai?format=guide',
|
||||||
|
icon: FileText,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'create-roadmap',
|
||||||
|
label: 'Roadmap',
|
||||||
|
href: '/ai?format=roadmap',
|
||||||
|
icon: Map,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'quiz',
|
||||||
|
label: 'Quiz',
|
||||||
|
href: '/ai/quiz',
|
||||||
|
icon: Swords,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'chat',
|
key: 'chat',
|
||||||
@@ -78,7 +101,11 @@ export type AITutorTab = (typeof sidebarItems)[number]['key'];
|
|||||||
export function AITutorSidebar(props: AITutorSidebarProps) {
|
export function AITutorSidebar(props: AITutorSidebarProps) {
|
||||||
const { activeTab, isFloating, onClose } = props;
|
const { activeTab, isFloating, onClose } = props;
|
||||||
|
|
||||||
|
const [format, setFormat] = useState('');
|
||||||
const [isInitialLoad, setIsInitialLoad] = useState(true);
|
const [isInitialLoad, setIsInitialLoad] = useState(true);
|
||||||
|
const [expandedItems, setExpandedItems] = useState<Record<string, boolean>>({
|
||||||
|
new: true, // Keep "Create with AI" expanded by default
|
||||||
|
});
|
||||||
|
|
||||||
const [showAILimitsPopup, setShowAILimitsPopup] = useState(false);
|
const [showAILimitsPopup, setShowAILimitsPopup] = useState(false);
|
||||||
const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false);
|
const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false);
|
||||||
@@ -89,15 +116,21 @@ export function AITutorSidebar(props: AITutorSidebarProps) {
|
|||||||
queryClient,
|
queryClient,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { used, limit } = limits ?? { used: 0, limit: 0 };
|
|
||||||
const totalPercentage = getPercentage(used, limit);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const { format } = getUrlParams();
|
||||||
|
setFormat(format || 'course');
|
||||||
setIsInitialLoad(false);
|
setIsInitialLoad(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const isLoading = isPaidUserLoading || isLimitsLoading;
|
const isLoading = isPaidUserLoading || isLimitsLoading;
|
||||||
|
|
||||||
|
const toggleExpanded = (key: string) => {
|
||||||
|
setExpandedItems((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[key]: !prev[key],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isUpgradeModalOpen && (
|
{isUpgradeModalOpen && (
|
||||||
@@ -156,7 +189,27 @@ export function AITutorSidebar(props: AITutorSidebarProps) {
|
|||||||
<AITutorSidebarItem
|
<AITutorSidebarItem
|
||||||
item={item}
|
item={item}
|
||||||
isActive={activeTab === item.key}
|
isActive={activeTab === item.key}
|
||||||
|
isExpanded={expandedItems[item.key] || false}
|
||||||
|
onToggleExpanded={() => toggleExpanded(item.key)}
|
||||||
/>
|
/>
|
||||||
|
{item.children && expandedItems[item.key] && (
|
||||||
|
<ul className="relative list-none">
|
||||||
|
<div className="absolute top-0 bottom-0 left-7 w-px bg-gray-200" />
|
||||||
|
{item.children.map((child) => (
|
||||||
|
<li key={child.key}>
|
||||||
|
<AITutorSidebarItem
|
||||||
|
item={child}
|
||||||
|
isActive={
|
||||||
|
(activeTab === item.key &&
|
||||||
|
`create-${format}` === child.key) ||
|
||||||
|
activeTab === child.key
|
||||||
|
}
|
||||||
|
isChild={true}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
@@ -179,35 +232,83 @@ export function AITutorSidebar(props: AITutorSidebarProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SidebarItem = {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
href: string;
|
||||||
|
icon: any;
|
||||||
|
children?: {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
href: string;
|
||||||
|
icon: any;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type ChildItem = {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
href: string;
|
||||||
|
icon: any;
|
||||||
|
};
|
||||||
|
|
||||||
type AITutorSidebarItemProps = {
|
type AITutorSidebarItemProps = {
|
||||||
item: (typeof sidebarItems)[number];
|
item: SidebarItem | ChildItem;
|
||||||
as?: 'a' | 'button';
|
as?: 'a' | 'button';
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
|
isExpanded?: boolean;
|
||||||
|
onToggleExpanded?: () => void;
|
||||||
|
isChild?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
function AITutorSidebarItem(props: AITutorSidebarItemProps) {
|
function AITutorSidebarItem(props: AITutorSidebarItemProps) {
|
||||||
const { item, as = 'a', onClick, className, isActive } = props;
|
const {
|
||||||
|
item,
|
||||||
|
as = 'a',
|
||||||
|
onClick,
|
||||||
|
className,
|
||||||
|
isActive,
|
||||||
|
isExpanded,
|
||||||
|
onToggleExpanded,
|
||||||
|
isChild,
|
||||||
|
} = props;
|
||||||
|
|
||||||
const Component = as;
|
const hasChildren = 'children' in item && item.children;
|
||||||
|
const Component = hasChildren ? 'button' : as;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
{...(as === 'a' ? { href: item.href } : {})}
|
{...(Component === 'a' && !hasChildren ? { href: item.href } : {})}
|
||||||
{...(as === 'button' ? { onClick } : {})}
|
{...(Component === 'button'
|
||||||
|
? { onClick: hasChildren ? onToggleExpanded : onClick }
|
||||||
|
: {})}
|
||||||
className={cn(
|
className={cn(
|
||||||
'font-regular flex w-full items-center border-r-2 px-5 py-2 text-sm transition-all',
|
'font-regular flex w-full items-center border-r-2 px-5 py-2 text-sm transition-all',
|
||||||
isActive
|
isActive && !hasChildren
|
||||||
? 'border-r-black bg-gray-100 text-black'
|
? 'border-r-black bg-gray-100 text-black'
|
||||||
: 'border-r-transparent text-gray-500 hover:border-r-gray-300',
|
: 'border-r-transparent text-gray-500',
|
||||||
|
!isActive && !hasChildren && 'hover:bg-gray-50 hover:text-gray-700',
|
||||||
|
!isActive && hasChildren && 'hover:text-gray-700',
|
||||||
|
isChild && 'border-r-transparent py-1.5 pl-11',
|
||||||
|
isChild && isActive && 'border-r-black border-r-2 bg-gray-100 text-black',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className="flex grow items-center">
|
<span className="flex grow items-center">
|
||||||
<item.icon className="mr-2 size-4" />
|
{!isChild && <item.icon className="mr-2 size-4" />}
|
||||||
{item.label}
|
{item.label}
|
||||||
</span>
|
</span>
|
||||||
|
{hasChildren && (
|
||||||
|
<span className="ml-auto">
|
||||||
|
{isExpanded ? (
|
||||||
|
<ChevronDown className="size-4" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="size-4" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</Component>
|
</Component>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ import {
|
|||||||
} from './QuestionAnswerChat';
|
} from './QuestionAnswerChat';
|
||||||
import { useToast } from '../../hooks/use-toast';
|
import { useToast } from '../../hooks/use-toast';
|
||||||
import { cn } from '../../lib/classname';
|
import { cn } from '../../lib/classname';
|
||||||
import { getUrlParams } from '../../lib/browser';
|
import { getUrlParams, setUrlParams } from '../../lib/browser';
|
||||||
import { useParams } from '../../hooks/use-params';
|
import { useParams } from '../../hooks/use-params';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { aiLimitOptions } from '../../queries/ai-course';
|
import { aiLimitOptions } from '../../queries/ai-course';
|
||||||
@@ -205,7 +205,10 @@ export function ContentGenerator() {
|
|||||||
<FormatItem
|
<FormatItem
|
||||||
key={format.value}
|
key={format.value}
|
||||||
label={format.label}
|
label={format.label}
|
||||||
onClick={() => setSelectedFormat(format.value)}
|
onClick={() => {
|
||||||
|
setSelectedFormat(format.value);
|
||||||
|
setUrlParams({ format: format.value });
|
||||||
|
}}
|
||||||
icon={format.icon}
|
icon={format.icon}
|
||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
/>
|
/>
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { Lock, Play } from 'lucide-react';
|
import { Lock, Play } from 'lucide-react';
|
||||||
|
import { markdownToHtml } from '../../lib/markdown';
|
||||||
|
|
||||||
interface FeatureCardProps {
|
interface FeatureCardProps {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -42,29 +43,32 @@ export function FeatureCard(props: FeatureCardProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<div className="group relative overflow-hidden rounded-lg border border-slate-700 bg-[#151F33] p-4 hover:border-blue-400">
|
||||||
onClick={onClick}
|
<button
|
||||||
className="group relative overflow-hidden rounded-lg border border-slate-700 bg-[#151F33] p-4 text-left hover:border-blue-400"
|
onClick={onClick}
|
||||||
>
|
className="relative block aspect-video w-full cursor-pointer overflow-hidden rounded-lg bg-slate-900/50"
|
||||||
<span className="group relative block aspect-video w-full cursor-pointer overflow-hidden rounded-lg bg-slate-900/50">
|
>
|
||||||
<img
|
<img
|
||||||
src={thumbnail}
|
src={thumbnail}
|
||||||
alt={title}
|
alt={title}
|
||||||
className="absolute inset-0 h-full w-full object-cover"
|
className="absolute inset-0 h-full w-full object-cover"
|
||||||
/>
|
/>
|
||||||
</span>
|
<span className="absolute inset-0 -m-4 flex items-center justify-center bg-black/30 opacity-0 group-hover:opacity-100">
|
||||||
<span className="absolute inset-0 flex items-center justify-center bg-black/30 opacity-0 group-hover:opacity-100">
|
<span className="flex h-12 w-12 items-center justify-center rounded-full bg-white/10 backdrop-blur-sm">
|
||||||
<span className="flex h-12 w-12 items-center justify-center rounded-full bg-white/10 backdrop-blur-sm">
|
<Play className="h-6 w-6 fill-current text-white" strokeWidth={2} />
|
||||||
<Play className="h-6 w-6 fill-current text-white" strokeWidth={2} />
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</button>
|
||||||
<span className="absolute top-1 right-1 rounded bg-black/60 px-2 py-1 text-xs text-white backdrop-blur-sm">
|
<span className="absolute top-1 right-1 rounded bg-black/60 px-2 py-1 text-xs text-white backdrop-blur-sm">
|
||||||
{duration}
|
{duration}
|
||||||
</span>
|
</span>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<h3 className="mb-1 text-sm font-medium text-white">{title}</h3>
|
<h3 className="mb-1 text-sm font-medium text-white">{title}</h3>
|
||||||
<p className="text-xs leading-relaxed text-slate-400">{description}</p>
|
<p
|
||||||
|
className="text-xs [&_a]:text-blue-400 [&_a]:underline-offset-2 [&_a]:underline leading-relaxed text-slate-400"
|
||||||
|
dangerouslySetInnerHTML={{ __html: markdownToHtml(description) }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
@@ -3,6 +3,7 @@ import {
|
|||||||
Bot,
|
Bot,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Clock,
|
Clock,
|
||||||
|
MousePointerClick,
|
||||||
PartyPopper,
|
PartyPopper,
|
||||||
Users2,
|
Users2,
|
||||||
Wand2,
|
Wand2,
|
||||||
@@ -161,10 +162,10 @@ export function PremiumPage() {
|
|||||||
Generate unlimited courses about any topic, get career guidance
|
Generate unlimited courses about any topic, get career guidance
|
||||||
and instant answers from AI, test your skills and more
|
and instant answers from AI, test your skills and more
|
||||||
</p>
|
</p>
|
||||||
<div className="flex justify-center">
|
<div className="flex flex-col items-center justify-center gap-4 sm:flex-row">
|
||||||
<a
|
<a
|
||||||
href="#pricing"
|
href="#pricing"
|
||||||
className="group mx-auto block rounded-2xl bg-gradient-to-b from-indigo-600 to-indigo-700 px-8 py-4 shadow-lg transition-all hover:-translate-y-0.5 hover:shadow-xl hover:shadow-indigo-500/25"
|
className="group block rounded-2xl bg-gradient-to-b from-indigo-600 to-indigo-700 px-8 py-4 shadow-sm transition-all hover:-translate-y-0.5"
|
||||||
>
|
>
|
||||||
<span className="flex items-center justify-center gap-3 text-lg text-white sm:hidden">
|
<span className="flex items-center justify-center gap-3 text-lg text-white sm:hidden">
|
||||||
Upgrade now
|
Upgrade now
|
||||||
@@ -179,6 +180,21 @@ export function PremiumPage() {
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
<button
|
||||||
|
data-demo-button="true"
|
||||||
|
onClick={() => {
|
||||||
|
if (isLoggedIn()) {
|
||||||
|
window.location.href = '/ai';
|
||||||
|
} else {
|
||||||
|
showLoginPopup();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="group relative flex items-center justify-center gap-2 overflow-hidden rounded-2xl border border-purple-500/30 bg-transparent px-6 py-4 text-base font-medium text-purple-500 shadow-sm transition-all hover:-translate-y-0.5 hover:bg-purple-500/10 hover:text-purple-400 hover:border-purple-300/30 focus:outline-none active:ring-0"
|
||||||
|
>
|
||||||
|
<MousePointerClick className="h-5 w-5" aria-hidden="true" />
|
||||||
|
<span className="lg:hidden">Try Demo</span>
|
||||||
|
<span className="hidden lg:inline">Try for Free</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-5 flex items-center justify-center gap-2 text-sm">
|
<p className="mt-5 flex items-center justify-center gap-2 text-sm">
|
||||||
<Clock className="h-3 w-3 text-white" />
|
<Clock className="h-3 w-3 text-white" />
|
||||||
|
@@ -10,18 +10,10 @@ export const paidFeaturesList = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const features = [
|
export const features = [
|
||||||
{
|
|
||||||
title: 'Chat with Roadmaps',
|
|
||||||
description:
|
|
||||||
'Ask questions and get instant answers on any roadmap through AI',
|
|
||||||
videoId: 'fq0UgNcj3Ek',
|
|
||||||
thumbnail: 'https://assets.roadmap.sh/guest/chat-with-roadmaps-ew2l9.png',
|
|
||||||
duration: '2:17',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'Unlimited AI Courses',
|
title: 'Unlimited AI Courses',
|
||||||
description:
|
description:
|
||||||
'No more paying for expensive courses, create unlimited courses with AI',
|
'No more paying for expensive courses, [create unlimited courses](/ai) with AI',
|
||||||
videoId: 'uCcQNhdVUVQ',
|
videoId: 'uCcQNhdVUVQ',
|
||||||
thumbnail: 'https://assets.roadmap.sh/guest/ai-courses-m07ra.png',
|
thumbnail: 'https://assets.roadmap.sh/guest/ai-courses-m07ra.png',
|
||||||
duration: '3:07',
|
duration: '3:07',
|
||||||
@@ -29,15 +21,32 @@ export const features = [
|
|||||||
{
|
{
|
||||||
title: 'In-depth Guides',
|
title: 'In-depth Guides',
|
||||||
description:
|
description:
|
||||||
'Create unlimited personalized in-depth learning guides with AI',
|
'Create unlimited personalized [in-depth learning guides](/ai?format=guide) with AI',
|
||||||
videoId: '5kwYjCg2Lu4',
|
videoId: '5kwYjCg2Lu4',
|
||||||
thumbnail: 'https://assets.roadmap.sh/guest/ai-guides-s4bjj.png',
|
thumbnail: 'https://assets.roadmap.sh/guest/ai-guides-s4bjj.png',
|
||||||
duration: '1:33',
|
duration: '1:33',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Learn Roadmap Topics',
|
||||||
|
description:
|
||||||
|
'Learn [roadmap topics directly](/backend) from within the nodes without leaving the roadmap',
|
||||||
|
videoId: '0jZ1Lse1Y3E',
|
||||||
|
thumbnail: 'https://assets.roadmap.sh/guest/smarter-roadmaps-f46ku.png',
|
||||||
|
duration: '3:11',
|
||||||
|
startTime: '5',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Chat with Roadmaps',
|
||||||
|
description:
|
||||||
|
'Ask questions and [get instant answers](/frontend) on any roadmap through AI',
|
||||||
|
videoId: 'fq0UgNcj3Ek',
|
||||||
|
thumbnail: 'https://assets.roadmap.sh/guest/chat-with-roadmaps-ew2l9.png',
|
||||||
|
duration: '2:17',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'AI as Learning Companion',
|
title: 'AI as Learning Companion',
|
||||||
description:
|
description:
|
||||||
'Use AI-powered learning companion to accelerate your roadmap progress',
|
'Use [AI-powered learning companion](/ai/roadmap-chat) to accelerate your roadmap progress',
|
||||||
videoId: 'Jso_HRviEOE',
|
videoId: 'Jso_HRviEOE',
|
||||||
thumbnail: 'https://assets.roadmap.sh/guest/roadmap-ai-tools-adhqq.png',
|
thumbnail: 'https://assets.roadmap.sh/guest/roadmap-ai-tools-adhqq.png',
|
||||||
duration: '2:45',
|
duration: '2:45',
|
||||||
@@ -46,24 +55,16 @@ export const features = [
|
|||||||
{
|
{
|
||||||
title: 'Your Personal Coach',
|
title: 'Your Personal Coach',
|
||||||
description:
|
description:
|
||||||
'Get career guidance, roadmap and personalized growth suggestions',
|
'Get career guidance, personalized growth suggestions using [AI Tutor Chat](/ai/chat)',
|
||||||
videoId: '77VwAeFmoIw',
|
videoId: '77VwAeFmoIw',
|
||||||
thumbnail: 'https://assets.roadmap.sh/guest/career-guidance-t2mpu.png',
|
thumbnail: 'https://assets.roadmap.sh/guest/career-guidance-t2mpu.png',
|
||||||
duration: '3:45',
|
duration: '3:45',
|
||||||
startTime: '4',
|
startTime: '4',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'Learn Roadmap Topics',
|
|
||||||
description:
|
|
||||||
'Learn roadmap topics directly from within the nodes without leaving the roadmap',
|
|
||||||
videoId: '0jZ1Lse1Y3E',
|
|
||||||
thumbnail: 'https://assets.roadmap.sh/guest/smarter-roadmaps-f46ku.png',
|
|
||||||
duration: '3:11',
|
|
||||||
startTime: '5'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'Test Yourself',
|
title: 'Test Yourself',
|
||||||
description: 'Test your knowledge and prepare for interviews with AI',
|
description:
|
||||||
|
'Let AI help you [test your knowledge](/ai/quiz) and prepare for interviews',
|
||||||
videoId: 'ScruGC8uzjo',
|
videoId: 'ScruGC8uzjo',
|
||||||
thumbnail: 'https://assets.roadmap.sh/guest/test-yourself-uwzqo.png',
|
thumbnail: 'https://assets.roadmap.sh/guest/test-yourself-uwzqo.png',
|
||||||
duration: '2:15',
|
duration: '2:15',
|
||||||
@@ -71,7 +72,7 @@ export const features = [
|
|||||||
{
|
{
|
||||||
title: 'Powerful Roadmap Editor',
|
title: 'Powerful Roadmap Editor',
|
||||||
description:
|
description:
|
||||||
'Create and edit roadmaps with ease using our powerful roadmap editor',
|
'[Create and edit roadmaps](/account/roadmaps) with ease using our powerful roadmap editor',
|
||||||
videoId: 'L2HZIHIgwOI',
|
videoId: 'L2HZIHIgwOI',
|
||||||
thumbnail:
|
thumbnail:
|
||||||
'https://assets.roadmap.sh/guest/ai-based-roadmap-editor-ochm8.png',
|
'https://assets.roadmap.sh/guest/ai-based-roadmap-editor-ochm8.png',
|
||||||
|
Reference in New Issue
Block a user