mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-02 13:52:46 +02:00
3
src/components/FAQs/Answer.astro
Normal file
3
src/components/FAQs/Answer.astro
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class='text-sm sm:text-base leading-relaxed text-left p-2 sm:p-4 text-md text-gray-800 border-t border-t-gray-300 bg-gray-100 rounded-bl-md rounded-br-md [&>p:not(:last-child)]:mb-3 [&>p>a]:underline [&>p>a]:text-blue-700'>
|
||||
<slot />
|
||||
</div>
|
42
src/components/FAQs/FAQs.astro
Normal file
42
src/components/FAQs/FAQs.astro
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
import { markdownToHtml } from '../../lib/markdown';
|
||||
import Answer from './Answer.astro';
|
||||
import Question from './Question.astro';
|
||||
|
||||
export type FAQType = {
|
||||
question: string;
|
||||
answer: string[];
|
||||
};
|
||||
|
||||
export interface Props {
|
||||
faqs: FAQType[];
|
||||
}
|
||||
|
||||
const { faqs } = Astro.props;
|
||||
|
||||
if (faqs.length === 0) {
|
||||
return '';
|
||||
}
|
||||
---
|
||||
|
||||
<div class='border-t bg-gray-100 mt-8'>
|
||||
<div class='container'>
|
||||
<div class='flex justify-between relative -top-5'>
|
||||
<h2 class='text-sm sm:text-base font-medium py-1 px-3 border bg-white rounded-md'>Frequently Asked Questions</h2>
|
||||
</div>
|
||||
|
||||
<div class='flex flex-col gap-1 pb-14'>
|
||||
{
|
||||
faqs.map((faq, questionIndex) => (
|
||||
<Question isActive={questionIndex === 0} question={faq.question}>
|
||||
<Answer>
|
||||
{faq.answer.map((answer) => (
|
||||
<p set:html={markdownToHtml(answer)} />
|
||||
))}
|
||||
</Answer>
|
||||
</Question>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,41 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import type { OfficialRoadmapQuestion } from '../../queries/official-roadmap';
|
||||
import { Question } from './Question';
|
||||
import { guideRenderer } from '../../lib/guide-renderer';
|
||||
|
||||
type FAQsProps = {
|
||||
faqs: OfficialRoadmapQuestion[];
|
||||
};
|
||||
|
||||
export function FAQs(props: FAQsProps) {
|
||||
const { faqs } = props;
|
||||
|
||||
const [activeQuestionIndex, setActiveQuestionIndex] = useState(0);
|
||||
|
||||
return (
|
||||
<div className="mt-8 border-t bg-gray-100">
|
||||
<div className="container">
|
||||
<div className="relative -top-5 flex justify-between">
|
||||
<h2 className="rounded-md border bg-white px-3 py-1 text-sm font-medium sm:text-base">
|
||||
Frequently Asked Questions
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1 pb-14">
|
||||
{faqs.map((faq, questionIndex) => (
|
||||
<Question
|
||||
key={faq._id}
|
||||
isActive={questionIndex === activeQuestionIndex}
|
||||
question={faq.title}
|
||||
onClick={() => setActiveQuestionIndex(questionIndex)}
|
||||
>
|
||||
<div className="text-md rounded-br-md rounded-bl-md border-t border-t-gray-300 bg-gray-100 p-2 text-left text-sm leading-relaxed text-gray-800 sm:p-4 sm:text-base [&>p:not(:last-child)]:mb-3 [&>p>a]:text-blue-700 [&>p>a]:underline">
|
||||
{guideRenderer.render(faq.description)}
|
||||
</div>
|
||||
</Question>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
42
src/components/FAQs/Question.astro
Normal file
42
src/components/FAQs/Question.astro
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
import Icon from '../AstroIcon.astro';
|
||||
|
||||
export interface Props {
|
||||
question: string;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
const { question, isActive = false } = Astro.props;
|
||||
---
|
||||
|
||||
<div
|
||||
class='faq-item bg-white border rounded-md hover:bg-gray-50 border-gray-300'
|
||||
>
|
||||
<button
|
||||
faq-question
|
||||
class='flex flex-row justify-between items-center p-2 sm:p-3 w-full'
|
||||
>
|
||||
<span class='text-sm sm:text-base text-left font-medium'>{question}</span>
|
||||
<Icon icon='down' class='h-6 hidden sm:block text-gray-400' />
|
||||
</button>
|
||||
<div class:list={['answer', { hidden: !isActive }]} faq-answer>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.querySelectorAll('[faq-question]').forEach((el) => {
|
||||
el.addEventListener('click', () => {
|
||||
// Hide any other visible answers
|
||||
document.querySelectorAll('[faq-answer]').forEach((element) => {
|
||||
element.classList.add('hidden');
|
||||
});
|
||||
|
||||
// Show the current answer
|
||||
const answer = el.nextElementSibling;
|
||||
if (answer) {
|
||||
answer.classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
@@ -1,29 +0,0 @@
|
||||
import { cn } from '../../lib/classname';
|
||||
import { ChevronDownIcon } from '../ReactIcons/ChevronDownIcon';
|
||||
|
||||
type QuestionProps = {
|
||||
question: string;
|
||||
isActive?: boolean;
|
||||
children: React.ReactNode;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export function Question(props: QuestionProps) {
|
||||
const { question, isActive = false, children, onClick } = props;
|
||||
|
||||
return (
|
||||
<div className="faq-item rounded-md border border-gray-300 bg-white hover:bg-gray-50">
|
||||
<button
|
||||
className="flex w-full flex-row items-center justify-between p-2 sm:p-3"
|
||||
onClick={onClick}
|
||||
>
|
||||
<span className="text-left text-sm font-medium sm:text-base">
|
||||
{question}
|
||||
</span>
|
||||
<ChevronDownIcon className="hidden h-3.5 stroke-[3] text-gray-400 sm:block" />
|
||||
</button>
|
||||
|
||||
{isActive && <div className={cn('answer')}>{children}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -1,22 +1,69 @@
|
||||
---
|
||||
import { Map } from 'lucide-react';
|
||||
import { listOfficialRoadmaps } from '../queries/official-roadmap';
|
||||
import { getQuestionGroupsByIds } from '../lib/question-group';
|
||||
import { getRoadmapsByIds, type RoadmapFrontmatter } from '../lib/roadmap';
|
||||
import { Map, Clipboard } from 'lucide-react';
|
||||
|
||||
export interface Props {
|
||||
relatedRoadmaps: string[];
|
||||
roadmap: RoadmapFrontmatter;
|
||||
}
|
||||
|
||||
const { relatedRoadmaps } = Astro.props;
|
||||
const { roadmap } = Astro.props;
|
||||
|
||||
const allRoadmaps = await listOfficialRoadmaps();
|
||||
const relatedRoadmapsDetails = allRoadmaps.filter((roadmap) =>
|
||||
relatedRoadmaps.includes(roadmap.slug),
|
||||
);
|
||||
const relatedRoadmaps = roadmap.relatedRoadmaps || [];
|
||||
const relatedRoadmapDetails = await getRoadmapsByIds(relatedRoadmaps);
|
||||
|
||||
const relatedQuestions = roadmap.relatedQuestions || [];
|
||||
const relatedQuestionDetails = await getQuestionGroupsByIds(relatedQuestions);
|
||||
---
|
||||
|
||||
{
|
||||
relatedQuestionDetails.length > 0 && (
|
||||
<div class='border-t bg-gray-100 pb-3'>
|
||||
<div class='container'>
|
||||
<div class='relative -top-5 flex justify-between'>
|
||||
<span class='text-md flex items-center rounded-md border bg-white px-3 py-1 font-medium'>
|
||||
<Clipboard className='mr-1.5 text-black' size='17px' />
|
||||
Test your Knowledge
|
||||
</span>
|
||||
<a
|
||||
href='/questions'
|
||||
class='text-md rounded-md border bg-white px-3 py-1 font-medium hover:bg-gray-50'
|
||||
>
|
||||
<span class='hidden sm:inline'>All Quizzes →</span>
|
||||
<span class='inline sm:hidden'>More →</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class='flex flex-col gap-1 pb-8'>
|
||||
{relatedQuestionDetails.map((relatedQuestionGroup) => (
|
||||
<a
|
||||
href={`/questions/${relatedQuestionGroup.id}`}
|
||||
class='flex flex-col gap-0.5 rounded-md border bg-white px-3.5 py-2 hover:bg-gray-50 sm:flex-row sm:gap-0'
|
||||
>
|
||||
<span class='inline-block min-w-[150px] font-medium'>
|
||||
{relatedQuestionGroup.title}
|
||||
</span>
|
||||
<span class='text-gray-500'>
|
||||
{relatedQuestionGroup.description}
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
relatedRoadmaps.length && (
|
||||
<div class:list={['border-t bg-gray-100']}>
|
||||
<div
|
||||
class:list={[
|
||||
'border-t bg-gray-100',
|
||||
{
|
||||
'mt-0': !relatedQuestionDetails.length,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<div class='container'>
|
||||
<div class='relative -top-5 flex justify-between'>
|
||||
<span class='text-md flex items-center rounded-md border bg-white px-3 py-1 font-medium'>
|
||||
@@ -33,15 +80,17 @@ const relatedRoadmapsDetails = allRoadmaps.filter((roadmap) =>
|
||||
</div>
|
||||
|
||||
<div class='flex flex-col gap-1 pb-8'>
|
||||
{relatedRoadmapsDetails.map((relatedRoadmap) => (
|
||||
{relatedRoadmapDetails.map((relatedRoadmap) => (
|
||||
<a
|
||||
href={`/${relatedRoadmap.slug}`}
|
||||
href={`/${relatedRoadmap.id}`}
|
||||
class='flex flex-col gap-0.5 rounded-md border bg-white px-3.5 py-2 hover:bg-gray-50 sm:flex-row sm:gap-0'
|
||||
>
|
||||
<span class='inline-block min-w-[195px] font-medium'>
|
||||
{relatedRoadmap.title.card}
|
||||
{relatedRoadmap.frontmatter.briefTitle}
|
||||
</span>
|
||||
<span class='text-gray-500'>
|
||||
{relatedRoadmap.frontmatter.briefDescription}
|
||||
</span>
|
||||
<span class='text-gray-500'>{relatedRoadmap.description}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
@@ -5,7 +5,9 @@ import {
|
||||
Bot,
|
||||
FolderKanbanIcon,
|
||||
MapIcon,
|
||||
MessageCircle,
|
||||
} from 'lucide-react';
|
||||
import { type RoadmapFrontmatter } from '../lib/roadmap';
|
||||
import LoginPopup from './AuthenticationFlow/LoginPopup.astro';
|
||||
import { DownloadRoadmapButton } from './DownloadRoadmapButton';
|
||||
import { MarkFavorite } from './FeaturedItems/MarkFavorite';
|
||||
@@ -18,16 +20,20 @@ import { PersonalizedRoadmap } from './PersonalizedRoadmap/PersonalizedRoadmap';
|
||||
export interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
note?: string;
|
||||
partner?: {
|
||||
description: string;
|
||||
link: string;
|
||||
linkText: string;
|
||||
};
|
||||
roadmapId: string;
|
||||
isUpcoming?: boolean;
|
||||
hasSearch?: boolean;
|
||||
projectCount?: number;
|
||||
coursesCount?: number;
|
||||
hasAIChat?: boolean;
|
||||
question?: RoadmapFrontmatter['question'];
|
||||
hasTopics?: boolean;
|
||||
isForkable?: boolean;
|
||||
activeTab?: 'roadmap' | 'projects' | 'courses';
|
||||
}
|
||||
@@ -37,8 +43,12 @@ const {
|
||||
description,
|
||||
roadmapId,
|
||||
partner,
|
||||
isUpcoming = false,
|
||||
note,
|
||||
hasTopics = false,
|
||||
hasAIChat = false,
|
||||
projectCount = 0,
|
||||
question,
|
||||
activeTab = 'roadmap',
|
||||
coursesCount = 0,
|
||||
} = Astro.props;
|
||||
|
@@ -10,12 +10,10 @@ import { useOutsideClick } from '../hooks/use-outside-click';
|
||||
import { markdownToHtml } from '../lib/markdown';
|
||||
import { cn } from '../lib/classname';
|
||||
import { useScrollPosition } from '../hooks/use-scroll-position';
|
||||
import type { JSONContent } from '@tiptap/core';
|
||||
import { guideRenderer } from '../lib/guide-renderer';
|
||||
|
||||
type RoadmapTitleQuestionProps = {
|
||||
question: string;
|
||||
answer: JSONContent;
|
||||
answer: string;
|
||||
roadmapId?: string;
|
||||
};
|
||||
|
||||
@@ -40,24 +38,24 @@ export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
|
||||
'rounded-0 -mx-4 sm:mx-0': isAnswerVisible,
|
||||
// @FIXME:
|
||||
// The line below is to keep the question hidden on mobile devices except for
|
||||
// the frontend roadmap. This is because we did not use to have the question
|
||||
// the frontend roadmap. This is because we did not use to have the question
|
||||
// on mobile devices before and we don't want to cause any SEO issues. It will
|
||||
// be enabled on other roadmaps in the future.
|
||||
},
|
||||
)}
|
||||
>
|
||||
{isAnswerVisible && (
|
||||
<div className="fixed top-0 right-0 left-0 z-100 h-full items-center justify-center overflow-x-hidden overflow-y-auto overscroll-contain bg-black/50"></div>
|
||||
<div className="fixed left-0 right-0 top-0 z-100 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50"></div>
|
||||
)}
|
||||
<h2
|
||||
className="z-50 flex cursor-pointer items-center px-2 py-2 text-sm font-medium select-none"
|
||||
className="z-50 flex cursor-pointer select-none items-center px-2 py-2 text-sm font-medium"
|
||||
aria-expanded={isAnswerVisible ? 'true' : 'false'}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setIsAnswerVisible(!isAnswerVisible);
|
||||
}}
|
||||
>
|
||||
<span className="flex grow items-center select-none">
|
||||
<span className="flex grow select-none items-center">
|
||||
<Info className="mr-1.5 inline-block h-4 w-4" strokeWidth={2.5} />
|
||||
{question}
|
||||
</span>
|
||||
@@ -67,7 +65,7 @@ export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
|
||||
</h2>
|
||||
|
||||
<div
|
||||
className={`absolute top-0 right-0 left-0 z-100 mt-0 border bg-white ${
|
||||
className={`absolute left-0 right-0 top-0 z-100 mt-0 border bg-white ${
|
||||
isAnswerVisible ? 'rounded-0 block sm:rounded-md' : 'hidden'
|
||||
}`}
|
||||
ref={ref}
|
||||
@@ -75,7 +73,7 @@ export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
|
||||
{isAnswerVisible && (
|
||||
<h2
|
||||
className={cn(
|
||||
'sticky top-0 flex cursor-pointer items-center rounded-t-md border-b bg-white px-[7px] py-[9px] text-base font-medium select-none',
|
||||
'sticky top-0 flex cursor-pointer select-none items-center rounded-t-md border-b bg-white px-[7px] py-[9px] text-base font-medium',
|
||||
)}
|
||||
onClick={() => {
|
||||
setIsAnswerVisible(false);
|
||||
@@ -97,11 +95,9 @@ export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
|
||||
</h2>
|
||||
)}
|
||||
<div
|
||||
className="bg-gray-100 p-3 text-base [&>h2]:mt-5 [&>h2]:mb-2 [&>h2]:text-[17px] [&>h2]:font-medium [&>p]:mb-3 [&>p]:leading-relaxed [&>p]:font-normal [&>p]:text-gray-800 [&>p:last-child]:mb-0 [&>p>a]:font-semibold [&>p>a]:underline [&>p>a]:underline-offset-2 [&>ul>li]:mb-2 [&>ul>li]:font-normal"
|
||||
// dangerouslySetInnerHTML={{ __html: markdownToHtml(answer, false) }}
|
||||
>
|
||||
{guideRenderer.render(answer)}
|
||||
</div>
|
||||
className="bg-gray-100 p-3 text-base [&>h2]:mb-2 [&>h2]:mt-5 [&>h2]:text-[17px] [&>h2]:font-medium [&>p:last-child]:mb-0 [&>p>a]:font-semibold [&>p>a]:underline [&>p>a]:underline-offset-2 [&>p]:mb-3 [&>p]:font-normal [&>p]:leading-relaxed [&>p]:text-gray-800 [&>ul>li]:mb-2 [&>ul>li]:font-normal"
|
||||
dangerouslySetInnerHTML={{ __html: markdownToHtml(answer, false) }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import type { FAQType } from '../../../components/FAQs/FAQs';
|
||||
import type { FAQType } from '../../../components/FAQs/FAQs.astro';
|
||||
|
||||
export const faqs: FAQType[] = [
|
||||
{
|
||||
@@ -11,13 +11,13 @@ export const faqs: FAQType[] = [
|
||||
{
|
||||
question: 'What is reinforcement learning?',
|
||||
answer: [
|
||||
'[Reinforcement learning](https://towardsdatascience.com/reinforcement-learning-101-e24b50e1d292) (RL) is a type of machine learning where an agent learns to make decisions by interacting with an environment. Unlike traditional supervised learning, RL does not rely on labeled data. Instead, the agent learns by taking actions and receiving feedback in the form of rewards or penalties. Over time, it aims to maximize cumulative rewards by refining its strategy based on past experiences. RL is often used in areas like robotics, game AI, and autonomous systems, where the goal is to develop intelligent behaviors through trial and error.',
|
||||
'[Reinforcement learning](https://towardsdatascience.com/reinforcement-learning-101-e24b50e1d292) (RL) is a type of machine learning where an agent learns to make decisions by interacting with an environment. Unlike traditional supervised learning, RL does not rely on labeled data. Instead, the agent learns by taking actions and receiving feedback in the form of rewards or penalties. Over time, it aims to maximize cumulative rewards by refining its strategy based on past experiences. RL is often used in areas like robotics, game AI, and autonomous systems, where the goal is to develop intelligent behaviors through trial and error.',
|
||||
],
|
||||
},
|
||||
{
|
||||
question: 'Do AI Engineers need a degree?',
|
||||
answer: [
|
||||
'While a degree in computer science, data science, or a related field can provide a solid foundation for becoming an AI engineer, it is not strictly necessary. Many successful AI engineers are self-taught or have gained expertise through online courses, certifications, and hands-on projects.',
|
||||
'While a degree in computer science, data science, or a related field can provide a solid foundation for becoming an AI engineer, it is not strictly necessary. Many successful AI engineers are self-taught or have gained expertise through online courses, certifications, and hands-on projects.'
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import type { FAQType } from '../../../components/FAQs/FAQs';
|
||||
import type { FAQType } from '../../../components/FAQs/FAQs.astro';
|
||||
|
||||
export const faqs: FAQType[] = [
|
||||
{
|
||||
|
@@ -1,50 +1,50 @@
|
||||
---
|
||||
import type { FAQType } from '../../../components/FAQs/FAQs';
|
||||
import type { FAQType } from '../../../components/FAQs/FAQs.astro';
|
||||
|
||||
export const faqs: FAQType[] = [
|
||||
{
|
||||
question: 'Is Frontend Development really coding?',
|
||||
answer: [
|
||||
'Do frontend developers really code? The answer is yes, absolutely.',
|
||||
'The fact that frontend developers are full-time developers who produce an output that is visually appealing (thanks to the designs provided by others) sometimes confuses others, making them believe that frontend developers aren\’t really coding. However, that couldn\’t be further from the truth.',
|
||||
'As a frontend developer, you\’ll be coding all the time.',
|
||||
'While in some companies, the frontend developer is also a skilled designer or UX engineer, those are not the typical profiles. As a frontend dev, your learning focus should be coding-related (i.e coding best practices, software design patterns, frontend architecture, etc).',
|
||||
"Do frontend developers really code? The answer is yes, absolutely.",
|
||||
"The fact that frontend developers are full-time developers who produce an output that is visually appealing (thanks to the designs provided by others) sometimes confuses others, making them believe that frontend developers aren\’t really coding. However, that couldn\’t be further from the truth.",
|
||||
"As a frontend developer, you\’ll be coding all the time.",
|
||||
"While in some companies, the frontend developer is also a skilled designer or UX engineer, those are not the typical profiles. As a frontend dev, your learning focus should be coding-related (i.e coding best practices, software design patterns, frontend architecture, etc).",
|
||||
],
|
||||
},
|
||||
{
|
||||
question: 'Is Frontend Developer a good career?',
|
||||
answer: [
|
||||
'As the web space and technology in general continue to evolve, the role of frontend developers becomes more relevant. In the end, providing a web version of an application has quite a lot of benefits, making it the obvious version (unless there is a specific requirement forcing for native development) for most systems, meaning that frontend developers have the potential to be involved in all types of projects and companies.',
|
||||
'This renders the frontend developer career one of the most versatile and in-demand paths in the web tech industry.',
|
||||
"As the web space and technology in general continue to evolve, the role of frontend developers becomes more relevant. In the end, providing a web version of an application has quite a lot of benefits, making it the obvious version (unless there is a specific requirement forcing for native development) for most systems, meaning that frontend developers have the potential to be involved in all types of projects and companies.",
|
||||
"This renders the frontend developer career one of the most versatile and in-demand paths in the web tech industry.",
|
||||
],
|
||||
},
|
||||
{
|
||||
question: 'How to prepare for a frontend developer interview?',
|
||||
answer: [
|
||||
'If you\’re looking to prepare for a frontend developer interview, make sure you\’ve tackled the fundamentals of the technologies you\’ll work with.',
|
||||
'And while that is one of the most impactful things you can do for your frontend career, consider focusing on the following points as well:',
|
||||
'1. **Master the Fundamentals**: Ensure a solid understanding of HTML, CSS, and JavaScript, as they are crucial for the role of frontend developer.',
|
||||
'2. **Practice Coding**: Improve your skills through mini-projects or coding challenges on platforms like LeetCode, especially those focused on front-end development. Using these skills to create a [web developer portfolio](https://roadmap.sh/frontend/web-developer-portfolio) can help you in many ways.',
|
||||
'3. **Learn Modern Frameworks**: Familiarize yourself with popular frameworks like React, Angular, or Vue.js.',
|
||||
'4. **Know Your Tools**: Be comfortable with version control systems, testing, and build tools, which are vital for all types of development, including frontend.',
|
||||
'5. **Understand UI/UX Principles**: Learn about accessibility, responsive design, and intuitive interfaces to stand out.',
|
||||
'6. **Research the Company**: Show interest by learning about the company\’s business and products.',
|
||||
'7. **Enhance Communication Skills**: Good communication skills are essential for success in any role, so make sure to work on them.',
|
||||
"If you\’re looking to prepare for a frontend developer interview, make sure you\’ve tackled the fundamentals of the technologies you\’ll work with.",
|
||||
"And while that is one of the most impactful things you can do for your frontend career, consider focusing on the following points as well:",
|
||||
"1. **Master the Fundamentals**: Ensure a solid understanding of HTML, CSS, and JavaScript, as they are crucial for the role of frontend developer.",
|
||||
"2. **Practice Coding**: Improve your skills through mini-projects or coding challenges on platforms like LeetCode, especially those focused on front-end development. Using these skills to create a [web developer portfolio](https://roadmap.sh/frontend/web-developer-portfolio) can help you in many ways.",
|
||||
"3. **Learn Modern Frameworks**: Familiarize yourself with popular frameworks like React, Angular, or Vue.js.",
|
||||
"4. **Know Your Tools**: Be comfortable with version control systems, testing, and build tools, which are vital for all types of development, including frontend.",
|
||||
"5. **Understand UI/UX Principles**: Learn about accessibility, responsive design, and intuitive interfaces to stand out.",
|
||||
"6. **Research the Company**: Show interest by learning about the company\’s business and products.",
|
||||
"7. **Enhance Communication Skills**: Good communication skills are essential for success in any role, so make sure to work on them.",
|
||||
],
|
||||
},
|
||||
{
|
||||
question: 'How is Frontend Development different from Backend Development?',
|
||||
answer: [
|
||||
'The main difference between frontend development and backend development is that frontend development focuses on the UI, while [backend development](https://roadmap.sh/backend) focuses more on the server-side logic.',
|
||||
'You see, frontend development works with the user interface and user experience, dealing with the design, layout, and interactivity of a website or application using HTML, CSS, and JavaScript (or TypeScript).',
|
||||
'On the other hand, backend development handles the server-side logic, databases, and application functionality, ensuring data is processed and served correctly. The tech stack for backend development is much bigger with more options, such as Python, Java, or Node.js.',
|
||||
'Both options are equally interesting and challenging, so it\’s not really a question of backend vs frontend, but instead of understanding where you feel more comfortable and what type of systems you enjoy creating.',
|
||||
"The main difference between frontend development and backend development is that frontend development focuses on the UI, while [backend development](https://roadmap.sh/backend) focuses more on the server-side logic.",
|
||||
"You see, frontend development works with the user interface and user experience, dealing with the design, layout, and interactivity of a website or application using HTML, CSS, and JavaScript (or TypeScript).",
|
||||
"On the other hand, backend development handles the server-side logic, databases, and application functionality, ensuring data is processed and served correctly. The tech stack for backend development is much bigger with more options, such as Python, Java, or Node.js.",
|
||||
"Both options are equally interesting and challenging, so it\’s not really a question of backend vs frontend, but instead of understanding where you feel more comfortable and what type of systems you enjoy creating.",
|
||||
],
|
||||
},
|
||||
{
|
||||
question: 'What are the job titles of a Frontend Developer?',
|
||||
answer: [
|
||||
'Front-end developers are also known as front-end engineers, front-end web developers, JavaScript Developers, HTML/CSS Developer, front-end web designers, and front-end web architects.',
|
||||
"Front-end developers are also known as front-end engineers, front-end web developers, JavaScript Developers, HTML/CSS Developer, front-end web designers, and front-end web architects.",
|
||||
"Each of these roles mostly encompasses the same front-end development skills but requires different levels of expertise in different [front-end development skills](https://roadmap.sh/frontend/developer-skills). It's better to look at the job description to get an idea about the job requirements.",
|
||||
],
|
||||
},
|
||||
@@ -57,16 +57,16 @@ export const faqs: FAQType[] = [
|
||||
{
|
||||
question: 'How long does it take to become a Frontend Developer?',
|
||||
answer: [
|
||||
'The amount of time it takes to become a frontend developer can vary depending on several factors, such as your learning pace, previous experience, and the amount of time you are able to dedicate to learning.',
|
||||
"The amount of time it takes to become a frontend developer can vary depending on several factors, such as your learning pace, previous experience, and the amount of time you are able to dedicate to learning.",
|
||||
"However, to give you a rough idea, if you are a complete beginner, it could take you anywhere from 3 to 6 months to get a job as an entry-level frontend developer. If you are already familiar with some of the frontend technologies, it could take you anywhere from 1 to 3 months. What's important is to practice as much as you can while you are learning i.e., by building as many projects as you can. You should also participate in online communities and ask for feedback from more experienced developers to accelerate your learning process.",
|
||||
],
|
||||
},
|
||||
{
|
||||
question: 'What are the Frontend Developer salaries?',
|
||||
answer: [
|
||||
'Frontend developer salaries can vary depending on factors such as location, experience, and company size. According to data from Glassdoor, the average base salary for a frontend developer in the United States is around $80,000 per year. However, this number can vary greatly depending on location, with the highest-paying cities such as San Francisco, Seattle, and New York having an average salary of $110,000 to $130,000.',
|
||||
"Frontend developer salaries can vary depending on factors such as location, experience, and company size. According to data from Glassdoor, the average base salary for a frontend developer in the United States is around $80,000 per year. However, this number can vary greatly depending on location, with the highest-paying cities such as San Francisco, Seattle, and New York having an average salary of $110,000 to $130,000.",
|
||||
"It's important to keep in mind that these are just averages, and salaries can vary greatly depending on factors such as experience level, specific skills, and the company you work for. With more experience and specific skills, you can expect to earn more.",
|
||||
'It is worth looking at a range of resources, including salary surveys and job boards, to get a general understanding of the current market in your location and experience level. Also, try reaching out to other professionals in the field and understanding their experience and salary ranges.',
|
||||
"It is worth looking at a range of resources, including salary surveys and job boards, to get a general understanding of the current market in your location and experience level. Also, try reaching out to other professionals in the field and understanding their experience and salary ranges.",
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -79,8 +79,8 @@ export const faqs: FAQType[] = [
|
||||
{
|
||||
question: 'What are Frontend Frameworks?',
|
||||
answer: [
|
||||
'[Frontend frameworks](https://roadmap.sh/frontend/frameworks) are collections of tools and libraries that help developers build web applications more efficiently. They provide a structure for the code, making it easier to build and maintain complex applications.',
|
||||
'Some popular frontend frameworks include React, Angular, and Vue.js. These frameworks provide a set of tools and libraries that help developers build user interfaces, manage state, and interact with APIs.',
|
||||
"[Frontend frameworks](https://roadmap.sh/frontend/frameworks) are collections of tools and libraries that help developers build web applications more efficiently. They provide a structure for the code, making it easier to build and maintain complex applications.",
|
||||
"Some popular frontend frameworks include React, Angular, and Vue.js. These frameworks provide a set of tools and libraries that help developers build user interfaces, manage state, and interact with APIs.",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import type { FAQType } from '../../../components/FAQs/FAQs';
|
||||
import type { FAQType } from '../../../components/FAQs/FAQs.astro';
|
||||
|
||||
export const faqs: FAQType[] = [
|
||||
{
|
||||
|
@@ -30,12 +30,10 @@ export interface MarkType {
|
||||
attrs?: Record<string, any> | undefined;
|
||||
}
|
||||
|
||||
export type GuideRendererOptions = {};
|
||||
|
||||
export class GuideRenderer {
|
||||
private marksOrder = ['underline', 'bold', 'italic', 'textStyle', 'link'];
|
||||
|
||||
render(content: JSONContent) {
|
||||
render(content: JSONContent): JSX.Element[] {
|
||||
const nodes = content.content || [];
|
||||
const jsxNodes = nodes
|
||||
.map((node, index) => {
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import type { OfficialRoadmapQuestion } from '../queries/official-roadmap';
|
||||
import { renderMarkdownFromJson } from './markdown-renderer';
|
||||
import type { FAQType } from '../components/FAQs/FAQs.astro';
|
||||
|
||||
type ArticleSchemaProps = {
|
||||
url: string;
|
||||
@@ -42,16 +41,16 @@ export function generateArticleSchema(article: ArticleSchemaProps) {
|
||||
};
|
||||
}
|
||||
|
||||
export function generateFAQSchema(faqs: OfficialRoadmapQuestion[]) {
|
||||
export function generateFAQSchema(faqs: FAQType[]) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'FAQPage',
|
||||
mainEntity: faqs.map((faq) => ({
|
||||
'@type': 'Question',
|
||||
name: faq.title,
|
||||
name: faq.question,
|
||||
acceptedAnswer: {
|
||||
'@type': 'Answer',
|
||||
text: renderMarkdownFromJson(faq.description, { join: ' ' }),
|
||||
text: faq.answer.join(' '),
|
||||
},
|
||||
})),
|
||||
};
|
||||
|
@@ -1,134 +0,0 @@
|
||||
import type { JSONContent } from '@tiptap/core';
|
||||
|
||||
export type MarkdownRendererOptions = {
|
||||
join?: string;
|
||||
};
|
||||
|
||||
export class MarkdownRenderer {
|
||||
private marksOrder = ['underline', 'bold', 'italic', 'textStyle', 'link'];
|
||||
|
||||
render(content: JSONContent, options: MarkdownRendererOptions = {}): string {
|
||||
const nodes = content.content || [];
|
||||
return nodes
|
||||
.map((node) => this.renderNode(node))
|
||||
.join(options?.join || '\n\n');
|
||||
}
|
||||
|
||||
private renderNode(node: JSONContent): string {
|
||||
const type = node.type || '';
|
||||
|
||||
if (type in this) {
|
||||
// @ts-expect-error dynamic lookup
|
||||
return this[type]?.(node);
|
||||
}
|
||||
|
||||
console.warn(`Node type "${type}" is not supported.`);
|
||||
return '';
|
||||
}
|
||||
|
||||
private getText(node: JSONContent): string {
|
||||
if (node.type === 'text') return node.text || '';
|
||||
if (node.content)
|
||||
return node.content.map((child) => this.getText(child)).join('');
|
||||
return '';
|
||||
}
|
||||
|
||||
private content(node: JSONContent): string {
|
||||
return (node.content || []).map((child) => this.renderNode(child)).join('');
|
||||
}
|
||||
|
||||
private renderMark(node: JSONContent): string {
|
||||
let text = node.text || '';
|
||||
let marks = node.marks || [];
|
||||
marks.sort(
|
||||
(a, b) =>
|
||||
this.marksOrder.indexOf(a.type) - this.marksOrder.indexOf(b.type),
|
||||
);
|
||||
|
||||
return marks.reduce((acc, mark) => {
|
||||
if (mark.type === 'bold') return `**${acc}**`;
|
||||
if (mark.type === 'italic') return `*${acc}*`;
|
||||
if (mark.type === 'underline') return `_${acc}_`; // fallback since markdown has no underline
|
||||
if (mark.type === 'code') return `\`${acc}\``;
|
||||
if (mark.type === 'link') return `[${acc}](${mark.attrs?.href})`;
|
||||
return acc;
|
||||
}, text);
|
||||
}
|
||||
|
||||
// ---- Nodes ----
|
||||
private paragraph(node: JSONContent): string {
|
||||
return this.content(node);
|
||||
}
|
||||
|
||||
private text(node: JSONContent): string {
|
||||
return node.marks ? this.renderMark(node) : node.text || '';
|
||||
}
|
||||
|
||||
private heading(node: JSONContent): string {
|
||||
const level = node.attrs?.level || 1;
|
||||
const prefix = '#'.repeat(level);
|
||||
return `${prefix} ${this.content(node)}`;
|
||||
}
|
||||
|
||||
private bulletList(node: JSONContent): string {
|
||||
return (node.content || [])
|
||||
.map((child) => `- ${this.renderNode(child)}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
private orderedList(node: JSONContent): string {
|
||||
return (node.content || [])
|
||||
.map((child, i) => `${i + 1}. ${this.renderNode(child)}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
private listItem(node: JSONContent): string {
|
||||
return this.content(node);
|
||||
}
|
||||
|
||||
private blockquote(node: JSONContent): string {
|
||||
return this.content(node)
|
||||
.split('\n')
|
||||
.map((line) => `> ${line}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
private codeBlock(node: JSONContent): string {
|
||||
const code = this.getText(node);
|
||||
const language = node.attrs?.language || '';
|
||||
return `\`\`\`${language}\n${code}\n\`\`\``;
|
||||
}
|
||||
|
||||
private horizontalRule(): string {
|
||||
return `---`;
|
||||
}
|
||||
|
||||
private image(node: JSONContent): string {
|
||||
const { src, alt } = node.attrs || {};
|
||||
return ``;
|
||||
}
|
||||
|
||||
private table(node: JSONContent): string {
|
||||
const rows = (node.content || []).filter((n) => n.type === 'tableRow');
|
||||
return rows.map((row) => this.renderNode(row)).join('\n');
|
||||
}
|
||||
|
||||
private tableRow(node: JSONContent): string {
|
||||
return `| ${this.content(node)} |`;
|
||||
}
|
||||
|
||||
private tableHeader(node: JSONContent): string {
|
||||
return this.content(node);
|
||||
}
|
||||
|
||||
private tableCell(node: JSONContent): string {
|
||||
return this.content(node);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderMarkdownFromJson(
|
||||
json: JSONContent,
|
||||
options: MarkdownRendererOptions = {},
|
||||
) {
|
||||
return new MarkdownRenderer().render(json, options);
|
||||
}
|
43
src/pages/[roadmapId]/ai.astro
Normal file
43
src/pages/[roadmapId]/ai.astro
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
import { CheckSubscriptionVerification } from '../../components/Billing/CheckSubscriptionVerification';
|
||||
import { RoadmapAIChat } from '../../components/RoadmapAIChat/RoadmapAIChat';
|
||||
import SkeletonLayout from '../../layouts/SkeletonLayout.astro';
|
||||
import { AITutorLayout } from '../../components/AITutor/AITutorLayout';
|
||||
import { getRoadmapById, getRoadmapIds } from '../../lib/roadmap';
|
||||
|
||||
type Props = {
|
||||
roadmapId: string;
|
||||
};
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const roadmapIds = await getRoadmapIds();
|
||||
|
||||
return roadmapIds.map((roadmapId) => ({
|
||||
params: { roadmapId },
|
||||
}));
|
||||
}
|
||||
|
||||
const { roadmapId } = Astro.params as Props;
|
||||
|
||||
const roadmapDetail = await getRoadmapById(roadmapId);
|
||||
|
||||
const canonicalUrl = `https://roadmap.sh/${roadmapId}/ai`;
|
||||
const roadmapBriefTitle = roadmapDetail.frontmatter.briefTitle;
|
||||
---
|
||||
|
||||
<SkeletonLayout
|
||||
title={`${roadmapBriefTitle} AI Mentor`}
|
||||
description=`Learn anything ${roadmapBriefTitle} with AI Tutor. Pick a topic, choose a difficulty level and the AI will guide you through the learning process.`
|
||||
canonicalUrl={canonicalUrl}
|
||||
>
|
||||
<AITutorLayout
|
||||
activeTab='chat'
|
||||
wrapperClassName='flex-row p-0 lg:p-0 overflow-hidden'
|
||||
client:load
|
||||
>
|
||||
<RoadmapAIChat roadmapId={roadmapId} client:load />
|
||||
<CheckSubscriptionVerification client:load />
|
||||
</AITutorLayout>
|
||||
</SkeletonLayout>
|
@@ -70,6 +70,7 @@ const courses = roadmapData.courses || [];
|
||||
<RoadmapHeader
|
||||
title={title}
|
||||
description={description}
|
||||
note={roadmapData.note}
|
||||
partner={roadmapData.partner}
|
||||
roadmapId={roadmapId}
|
||||
hasTopics={roadmapData.hasTopics}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
---
|
||||
import { EditorRoadmap } from '../../components/EditorRoadmap/EditorRoadmap';
|
||||
import { FAQs } from '../../components/FAQs/FAQs';
|
||||
import FAQs, { type FAQType } from '../../components/FAQs/FAQs.astro';
|
||||
import FrameRenderer from '../../components/FrameRenderer/FrameRenderer.astro';
|
||||
import RelatedRoadmaps from '../../components/RelatedRoadmaps.astro';
|
||||
import RoadmapHeader from '../../components/RoadmapHeader.astro';
|
||||
import { ShareIcons } from '../../components/ShareIcons/ShareIcons';
|
||||
@@ -12,23 +13,20 @@ import {
|
||||
generateFAQSchema,
|
||||
} from '../../lib/jsonld-schema';
|
||||
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
||||
import { type RoadmapFrontmatter, getRoadmapIds } from '../../lib/roadmap';
|
||||
import RoadmapNote from '../../components/RoadmapNote.astro';
|
||||
import { RoadmapTitleQuestion } from '../../components/RoadmapTitleQuestion';
|
||||
import ResourceProgressStats from '../../components/ResourceProgressStats.astro';
|
||||
import { getProjectsByRoadmapId } from '../../lib/project';
|
||||
import { CheckSubscriptionVerification } from '../../components/Billing/CheckSubscriptionVerification';
|
||||
import {
|
||||
listOfficialRoadmaps,
|
||||
officialRoadmapDetails,
|
||||
} from '../../queries/official-roadmap';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const officialRoadmaps = await listOfficialRoadmaps();
|
||||
const roadmapIds = await getRoadmapIds();
|
||||
|
||||
return officialRoadmaps.map((roadmap) => ({
|
||||
params: { roadmapId: roadmap.slug },
|
||||
return roadmapIds.map((roadmapId) => ({
|
||||
params: { roadmapId },
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -37,67 +35,60 @@ interface Params extends Record<string, string | undefined> {
|
||||
}
|
||||
|
||||
const { roadmapId } = Astro.params as Params;
|
||||
const roadmapData = await officialRoadmapDetails(roadmapId);
|
||||
if (!roadmapData) {
|
||||
return Astro.rewrite('/404');
|
||||
}
|
||||
const roadmapFile = await import(
|
||||
`../../data/roadmaps/${roadmapId}/${roadmapId}.md`
|
||||
);
|
||||
const { faqs: roadmapFAQs = [] } = await import(
|
||||
`../../data/roadmaps/${roadmapId}/faqs.astro`
|
||||
);
|
||||
const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
|
||||
|
||||
let jsonLdSchema = [];
|
||||
|
||||
const datePublished = DateTime.fromJSDate(
|
||||
new Date(roadmapData?.createdAt),
|
||||
).toFormat('yyyy-MM-dd');
|
||||
const dateModified = DateTime.fromJSDate(
|
||||
new Date(roadmapData?.updatedAt),
|
||||
).toFormat('yyyy-MM-dd');
|
||||
if (roadmapData.schema) {
|
||||
const roadmapSchema = roadmapData.schema;
|
||||
jsonLdSchema.push(
|
||||
generateArticleSchema({
|
||||
url: `https://roadmap.sh/${roadmapId}`,
|
||||
headline: roadmapSchema.headline,
|
||||
description: roadmapSchema.description,
|
||||
datePublished: roadmapSchema.datePublished,
|
||||
dateModified: roadmapSchema.dateModified,
|
||||
imageUrl: roadmapSchema.imageUrl,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const baseUrl = import.meta.env.DEV
|
||||
? `http://localhost:8080`
|
||||
: `https://roadmap.sh`;
|
||||
|
||||
jsonLdSchema.push(
|
||||
generateArticleSchema({
|
||||
url: `https://roadmap.sh/${roadmapId}`,
|
||||
headline: roadmapData?.seo?.title || roadmapData?.title?.page,
|
||||
description: roadmapData?.description,
|
||||
datePublished,
|
||||
dateModified,
|
||||
imageUrl: `${baseUrl}/roadmaps/${roadmapId}.png`,
|
||||
}),
|
||||
);
|
||||
if (roadmapFAQs.length) {
|
||||
jsonLdSchema.push(generateFAQSchema(roadmapFAQs as unknown as FAQType[]));
|
||||
}
|
||||
|
||||
const ogImageUrl =
|
||||
roadmapData?.openGraph?.image ||
|
||||
roadmapData?.seo?.ogImageUrl ||
|
||||
getOpenGraphImageUrl({
|
||||
group: 'roadmap',
|
||||
resourceId: roadmapId,
|
||||
});
|
||||
|
||||
const question = roadmapData?.questions?.find(
|
||||
(question) => question.type === 'main',
|
||||
);
|
||||
const faqs =
|
||||
roadmapData?.questions?.filter((question) => question.type === 'faq') || [];
|
||||
if (faqs.length) {
|
||||
jsonLdSchema.push(generateFAQSchema(faqs));
|
||||
}
|
||||
|
||||
const question = roadmapData?.question;
|
||||
const note = roadmapData.note;
|
||||
const projects = await getProjectsByRoadmapId(roadmapId);
|
||||
// const courses = roadmapData.courses || [];
|
||||
const courses = roadmapData.courses || [];
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
permalink={`/${roadmapId}`}
|
||||
title={roadmapData?.seo?.title || roadmapData?.title.page}
|
||||
briefTitle={roadmapData.title.card}
|
||||
title={roadmapData?.seo?.title}
|
||||
briefTitle={roadmapData.briefTitle}
|
||||
ogImageUrl={ogImageUrl}
|
||||
description={roadmapData.seo.description}
|
||||
keywords={roadmapData.seo.keywords}
|
||||
noIndex={false}
|
||||
noIndex={roadmapData.isUpcoming}
|
||||
jsonLd={jsonLdSchema}
|
||||
resourceId={roadmapId}
|
||||
resourceType='roadmap'
|
||||
>
|
||||
<!-- Preload the font being used in the renderer -->
|
||||
<link
|
||||
rel='preload'
|
||||
href='/fonts/balsamiq.woff2'
|
||||
@@ -110,21 +101,25 @@ const projects = await getProjectsByRoadmapId(roadmapId);
|
||||
<TopicDetail
|
||||
resourceId={roadmapId}
|
||||
resourceType='roadmap'
|
||||
renderer='editor'
|
||||
renderer={roadmapData.renderer}
|
||||
client:idle
|
||||
canSubmitContribution={true}
|
||||
/>
|
||||
|
||||
<div class='bg-gray-50'>
|
||||
<RoadmapHeader
|
||||
title={roadmapData.title.page}
|
||||
title={roadmapData.title}
|
||||
description={roadmapData.description}
|
||||
note={roadmapData.note}
|
||||
partner={roadmapData.partner}
|
||||
roadmapId={roadmapId}
|
||||
isForkable={true}
|
||||
hasTopics={roadmapData.hasTopics}
|
||||
isUpcoming={roadmapData.isUpcoming}
|
||||
isForkable={roadmapData.isForkable}
|
||||
question={roadmapData.question}
|
||||
projectCount={projects.length}
|
||||
coursesCount={0}
|
||||
hasAIChat={true}
|
||||
coursesCount={courses.length}
|
||||
hasAIChat={roadmapData.renderer === 'editor'}
|
||||
/>
|
||||
|
||||
<div class='container mt-2.5'>
|
||||
@@ -147,23 +142,33 @@ const projects = await getProjectsByRoadmapId(roadmapId);
|
||||
<ShareIcons
|
||||
resourceId={roadmapId}
|
||||
resourceType='roadmap'
|
||||
description={roadmapData.description}
|
||||
description={roadmapData.briefDescription}
|
||||
pageUrl={`https://roadmap.sh/${roadmapId}`}
|
||||
client:load
|
||||
/>
|
||||
|
||||
<EditorRoadmap
|
||||
resourceId={roadmapId}
|
||||
resourceType='roadmap'
|
||||
dimensions={roadmapData.dimensions!}
|
||||
client:load
|
||||
/>
|
||||
{
|
||||
roadmapData?.renderer === 'editor' ? (
|
||||
<EditorRoadmap
|
||||
resourceId={roadmapId}
|
||||
resourceType='roadmap'
|
||||
dimensions={roadmapData.dimensions!}
|
||||
client:load
|
||||
/>
|
||||
) : (
|
||||
<FrameRenderer
|
||||
resourceType={'roadmap'}
|
||||
resourceId={roadmapId}
|
||||
dimensions={roadmapData.dimensions}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<UserProgressModal
|
||||
resourceId={roadmapId}
|
||||
resourceType='roadmap'
|
||||
renderer='editor'
|
||||
renderer={roadmapData?.renderer}
|
||||
client:only='react'
|
||||
/>
|
||||
|
||||
@@ -175,8 +180,9 @@ const projects = await getProjectsByRoadmapId(roadmapId);
|
||||
)
|
||||
}
|
||||
|
||||
<FAQs faqs={faqs} client:load />
|
||||
<RelatedRoadmaps relatedRoadmaps={roadmapData?.relatedRoadmaps || []} />
|
||||
<FAQs faqs={roadmapFAQs as unknown as FAQType[]} />
|
||||
|
||||
<RelatedRoadmaps roadmap={roadmapData} />
|
||||
</div>
|
||||
|
||||
<CheckSubscriptionVerification client:load />
|
||||
|
@@ -76,9 +76,13 @@ const { response: userCounts } =
|
||||
<RoadmapHeader
|
||||
title={title}
|
||||
description={description}
|
||||
note={roadmapData.note}
|
||||
partner={roadmapData.partner}
|
||||
roadmapId={roadmapId}
|
||||
hasTopics={roadmapData.hasTopics}
|
||||
isUpcoming={roadmapData.isUpcoming}
|
||||
isForkable={roadmapData.isForkable}
|
||||
question={roadmapData.question}
|
||||
activeTab='projects'
|
||||
projectCount={projects.length}
|
||||
coursesCount={roadmapData.courses?.length || 0}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { queryOptions } from '@tanstack/react-query';
|
||||
import { FetchError, httpGet } from '../lib/query-http';
|
||||
import { httpGet } from '../lib/query-http';
|
||||
import type { Node, Edge } from '@roadmapsh/editor';
|
||||
|
||||
export const allowedOfficialRoadmapType = ['skill', 'role'] as const;
|
||||
@@ -42,9 +42,6 @@ export interface OfficialRoadmapDocument {
|
||||
description: string;
|
||||
keywords: string[];
|
||||
};
|
||||
openGraph?: {
|
||||
image?: string;
|
||||
};
|
||||
partner?: {
|
||||
description: string;
|
||||
linkText: string;
|
||||
@@ -71,35 +68,3 @@ export function officialRoadmapOptions(slug: string) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function officialRoadmapDetails(roadmapSlug: string) {
|
||||
try {
|
||||
const roadmap = await httpGet<OfficialRoadmapDocument>(
|
||||
`/v1-official-roadmap/${roadmapSlug}`,
|
||||
);
|
||||
|
||||
return roadmap;
|
||||
} catch (error) {
|
||||
if (FetchError.isFetchError(error) && error.status === 404) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function listOfficialRoadmaps() {
|
||||
try {
|
||||
const roadmaps = await httpGet<OfficialRoadmapDocument[]>(
|
||||
`/v1-list-official-roadmaps`,
|
||||
);
|
||||
|
||||
return roadmaps;
|
||||
} catch (error) {
|
||||
if (FetchError.isFetchError(error) && error.status === 404) {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user