mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-01 05:21:43 +02:00
fix: guides pages
This commit is contained in:
@@ -1,182 +0,0 @@
|
||||
---
|
||||
import { getGuideTableOfContent, type HeadingGroupType } from '../../lib/guide';
|
||||
import { markdownToHtml } from '../../lib/markdown';
|
||||
import {
|
||||
type QuestionGroupType,
|
||||
type QuestionType,
|
||||
} from '../../lib/question-group';
|
||||
import { slugify } from '../../lib/slugger';
|
||||
import { RelatedGuides } from '../Guide/RelatedGuides';
|
||||
import MarkdownFile from '../MarkdownFile.astro';
|
||||
import { TableOfContent } from '../TableOfContent/TableOfContent';
|
||||
import { QuestionsList } from './QuestionsList';
|
||||
|
||||
interface Props {
|
||||
questionGroup: QuestionGroupType;
|
||||
}
|
||||
|
||||
const { questionGroup } = Astro.props;
|
||||
|
||||
const { frontmatter: guideFrontmatter, author } = questionGroup;
|
||||
|
||||
// Group questions by topics
|
||||
const questionsGroupedByTopics = questionGroup.questions.reduce(
|
||||
(acc, question) => {
|
||||
question.topics?.forEach((topic) => {
|
||||
acc[topic] = [...(acc[topic] || []), question];
|
||||
});
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, QuestionType[]>,
|
||||
);
|
||||
|
||||
// Get all unique topics in the order they appear in the questions array
|
||||
const topicsInOrder: string[] = [];
|
||||
questionGroup.questions.forEach((question) => {
|
||||
question.topics?.forEach((topic) => {
|
||||
if (!topicsInOrder.includes(topic)) {
|
||||
topicsInOrder.push(topic);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const allHeadings = questionGroup.getHeadings();
|
||||
let tableOfContent: HeadingGroupType[] = [
|
||||
...getGuideTableOfContent(allHeadings),
|
||||
{
|
||||
depth: 2,
|
||||
children: [],
|
||||
slug: 'test-with-flashcards',
|
||||
text: 'Test yourself with Flashcards',
|
||||
},
|
||||
{
|
||||
depth: 2,
|
||||
children: topicsInOrder.map((topic) => {
|
||||
let topicText = topic;
|
||||
let topicSlug = slugify(topic);
|
||||
if (topic.toLowerCase() === 'beginners') {
|
||||
topicText = 'Beginner Level';
|
||||
topicSlug = 'beginner-level';
|
||||
} else if (topic.toLowerCase() === 'intermediate') {
|
||||
topicText = 'Intermediate Level';
|
||||
topicSlug = 'intermediate-level';
|
||||
} else if (topic.toLowerCase() === 'advanced') {
|
||||
topicText = 'Advanced Level';
|
||||
topicSlug = 'advanced-level';
|
||||
}
|
||||
|
||||
return {
|
||||
depth: 2,
|
||||
children: [],
|
||||
slug: topicSlug,
|
||||
text: topicText,
|
||||
};
|
||||
}),
|
||||
slug: 'questions-list',
|
||||
text: 'Questions List',
|
||||
},
|
||||
];
|
||||
|
||||
const showTableOfContent = tableOfContent.length > 0;
|
||||
---
|
||||
|
||||
<article class='lg:grid lg:max-w-full lg:grid-cols-[1fr_minmax(0,700px)_1fr]'>
|
||||
<!-- {
|
||||
showTableOfContent && (
|
||||
<div class='bg-linear-to-r from-gray-50 py-0 lg:col-start-3 lg:col-end-4 lg:row-start-1'>
|
||||
<RelatedGuides
|
||||
relatedTitle={guideFrontmatter?.relatedTitle}
|
||||
relatedGuides={questionGroup?.relatedGuides || {}}
|
||||
client:load
|
||||
/>
|
||||
<TableOfContent toc={tableOfContent} client:load />
|
||||
</div>
|
||||
)
|
||||
} -->
|
||||
|
||||
<div
|
||||
class:list={[
|
||||
'col-start-2 col-end-3 row-start-1 mx-auto max-w-[700px] py-5 sm:py-10',
|
||||
{
|
||||
'lg:border-r': showTableOfContent,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<MarkdownFile>
|
||||
<h1 class='mb-3 text-4xl font-bold text-balance'>
|
||||
{guideFrontmatter.title}
|
||||
</h1>
|
||||
{
|
||||
author && (
|
||||
<p class='my-0 flex items-center justify-start text-sm text-gray-400'>
|
||||
<a
|
||||
href={`/authors/${author?.id}`}
|
||||
class='inline-flex items-center font-medium underline-offset-2 hover:text-gray-600 hover:underline'
|
||||
>
|
||||
<img
|
||||
alt={author.frontmatter.name}
|
||||
src={author.frontmatter.imageUrl}
|
||||
class='mr-2 mb-0 inline h-5 w-5 rounded-full'
|
||||
/>
|
||||
{author.frontmatter.name}
|
||||
</a>
|
||||
<span class='mx-2 hidden sm:inline'>·</span>
|
||||
<a
|
||||
class='hidden underline-offset-2 hover:text-gray-600 sm:inline'
|
||||
href={`https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/question-groups/${questionGroup.id}`}
|
||||
target='_blank'
|
||||
>
|
||||
Improve this Guide
|
||||
</a>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
<questionGroup.Content />
|
||||
|
||||
<h2 id='test-with-flashcards'>Test yourself with Flashcards</h2>
|
||||
<p>
|
||||
You can either use these flashcards or jump to the questions list
|
||||
section below to see them in a list format.
|
||||
</p>
|
||||
<!-- <div class='mx-0 sm:-mb-32'>
|
||||
<QuestionsList
|
||||
groupId={questionGroup.id}
|
||||
questions={questionGroup.questions}
|
||||
client:load
|
||||
/>
|
||||
</div> -->
|
||||
|
||||
<h2 id='questions-list'>Questions List</h2>
|
||||
<p>
|
||||
If you prefer to see the questions in a list format, you can find them
|
||||
below.
|
||||
</p>
|
||||
|
||||
{
|
||||
topicsInOrder.map((questionLevel) => (
|
||||
<div class='mb-5'>
|
||||
<h3 id={slugify(questionLevel)} class='mb-0 capitalize'>
|
||||
{questionLevel.toLowerCase() === 'beginners' ? 'Beginner Level' :
|
||||
questionLevel.toLowerCase() === 'intermediate' ? 'Intermediate Level' :
|
||||
questionLevel.toLowerCase() === 'advanced' ? 'Advanced Level' :
|
||||
questionLevel}
|
||||
</h3>
|
||||
{questionsGroupedByTopics[questionLevel].map((q) => (
|
||||
<div class='mb-5'>
|
||||
<h4>{q.question}</h4>
|
||||
<div set:html={markdownToHtml(q.answer, false)} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
{
|
||||
questionGroup.ending && (
|
||||
<div class='mb-5'>
|
||||
<div set:html={markdownToHtml(questionGroup.ending, false)} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</MarkdownFile>
|
||||
</div>
|
||||
</article>
|
@@ -1,8 +1,6 @@
|
||||
import type { MarkdownFileType } from './file';
|
||||
import type { AuthorFileType } from './author.ts';
|
||||
import { getAllAuthors } from './author.ts';
|
||||
import type { GuideFileType } from './guide.ts';
|
||||
import { getAllGuides } from './guide.ts';
|
||||
|
||||
export interface VideoFrontmatter {
|
||||
title: string;
|
||||
|
@@ -4,7 +4,6 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { getAllBestPractices } from '../lib/best-practice';
|
||||
import { getAllQuestionGroups } from '../lib/question-group';
|
||||
import { getRoadmapsByTag } from '../lib/roadmap';
|
||||
import { getAllGuides } from '../lib/guide';
|
||||
import { getAllVideos } from '../lib/video';
|
||||
import { listOfficialGuides } from '../queries/official-guide';
|
||||
|
||||
|
@@ -1,9 +1,12 @@
|
||||
import { getAllBestPractices } from '../lib/best-practice';
|
||||
import { getAllGuides } from '../lib/guide';
|
||||
import { getRoadmapsByTag } from '../lib/roadmap';
|
||||
import { getAllVideos } from '../lib/video';
|
||||
import { getAllQuestionGroups } from '../lib/question-group';
|
||||
import { getAllProjects } from '../lib/project';
|
||||
import {
|
||||
listOfficialAuthors,
|
||||
listOfficialGuides,
|
||||
} from '../queries/official-guide';
|
||||
|
||||
// Add utility to fetch beginner roadmap file IDs
|
||||
function getBeginnerRoadmapIds() {
|
||||
@@ -18,7 +21,8 @@ function getBeginnerRoadmapIds() {
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const guides = await getAllGuides();
|
||||
const guides = await listOfficialGuides();
|
||||
const authors = await listOfficialAuthors();
|
||||
const videos = await getAllVideos();
|
||||
const questionGroups = await getAllQuestionGroups();
|
||||
const roadmaps = await getRoadmapsByTag('roadmap');
|
||||
@@ -78,17 +82,22 @@ export async function GET() {
|
||||
shortTitle: questionGroup.frontmatter.briefTitle,
|
||||
group: 'Questions',
|
||||
})),
|
||||
...guides.map((guide) => ({
|
||||
id: guide.id,
|
||||
url: guide.frontmatter.excludedBySlug
|
||||
? guide.frontmatter.excludedBySlug
|
||||
: `/guides/${guide.id}`,
|
||||
title: guide.frontmatter.title,
|
||||
description: guide.frontmatter.description,
|
||||
authorId: guide.frontmatter.authorId,
|
||||
shortTitle: guide.frontmatter.title,
|
||||
group: 'Guides',
|
||||
})),
|
||||
...guides.map((guide) => {
|
||||
const author = authors.find((author) => author._id === guide.authorId);
|
||||
const authorSlug = author?.slug || guide?.authorId;
|
||||
|
||||
return {
|
||||
id: guide.slug,
|
||||
url: guide?.roadmapId
|
||||
? `/${guide.roadmapId}/${guide.slug}`
|
||||
: `/guides/${guide.slug}`,
|
||||
title: guide.title,
|
||||
description: guide.description,
|
||||
authorId: authorSlug,
|
||||
shortTitle: guide.title,
|
||||
group: 'Guides',
|
||||
};
|
||||
}),
|
||||
...videos.map((video) => ({
|
||||
id: video.id,
|
||||
url: `/videos/${video.id}`,
|
||||
|
@@ -1,84 +0,0 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import Footer from '../../components/Footer.astro';
|
||||
import { QuestionsList } from '../../components/Questions/QuestionsList';
|
||||
|
||||
import {
|
||||
getAllQuestionGroups,
|
||||
type QuestionGroupType,
|
||||
} from '../../lib/question-group';
|
||||
import QuestionGuide from '../../components/Questions/QuestionGuide.astro';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export interface Props {
|
||||
questionGroup: QuestionGroupType;
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const questionGroups = await getAllQuestionGroups();
|
||||
|
||||
return questionGroups.map((questionGroup) => {
|
||||
return {
|
||||
params: { questionGroupId: questionGroup.id },
|
||||
props: { questionGroup },
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const { questionGroup } = Astro.props;
|
||||
const { frontmatter } = questionGroup;
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title={frontmatter.seo.title}
|
||||
briefTitle={frontmatter.briefTitle}
|
||||
description={frontmatter.seo.description}
|
||||
keywords={frontmatter.seo.keywords}
|
||||
ogImageUrl={frontmatter.seo.ogImageUrl}
|
||||
permalink={`/questions/${questionGroup.id}`}
|
||||
>
|
||||
{
|
||||
!questionGroup.frontmatter.authorId && (
|
||||
<div class='flex bg-gray-50 pb-14 pt-4 sm:pb-16 sm:pt-8'>
|
||||
<div class='container max-w-[740px]!'>
|
||||
<div class='mb-3 mt-2 text-left sm:mb-5 sm:mt-8 sm:text-center'>
|
||||
<div class='mb-2 md:mb-6'>
|
||||
<a
|
||||
href='/questions'
|
||||
class='group rounded-md text-sm font-medium text-gray-400 transition-colors duration-200 hover:text-gray-800'
|
||||
>
|
||||
<span class='inline-block transform transition-transform group-hover:translate-x-[-2px]'>
|
||||
←
|
||||
</span>
|
||||
Back to all Questions
|
||||
</a>
|
||||
</div>
|
||||
<h1 class='mb-1 text-2xl font-bold sm:mb-5 sm:text-5xl'>
|
||||
{frontmatter.title}
|
||||
</h1>
|
||||
<p class='hidden text-xl text-gray-500 sm:block'>
|
||||
{frontmatter.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<QuestionsList
|
||||
groupId={questionGroup.id}
|
||||
questions={questionGroup.questions}
|
||||
client:load
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
questionGroup.frontmatter.authorId && (
|
||||
<QuestionGuide questionGroup={questionGroup} />
|
||||
)
|
||||
}
|
||||
|
||||
<div slot='page-footer'>
|
||||
<Footer />
|
||||
</div>
|
||||
</BaseLayout>
|
@@ -1,37 +0,0 @@
|
||||
---
|
||||
import GridItem from '../../components/GridItem.astro';
|
||||
import SimplePageHeader from '../../components/SimplePageHeader.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { getAllQuestionGroups } from '../../lib/question-group';
|
||||
|
||||
const questionGroups = await getAllQuestionGroups();
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title='Questions'
|
||||
description={'Step by step guides and paths to learn different tools or technologies'}
|
||||
permalink={'/questions'}
|
||||
>
|
||||
<SimplePageHeader
|
||||
title='Questions'
|
||||
description='Quizzes to help you test and improve your knowledge and skill up'
|
||||
showYouTubeAlert={false}
|
||||
/>
|
||||
|
||||
<div class='flex bg-gray-100 pb-14 pt-4 sm:pb-16 sm:pt-8'>
|
||||
<div class='container'>
|
||||
<div class='grid grid-cols-1 gap-1 sm:grid-cols-2 sm:gap-3'>
|
||||
{
|
||||
questionGroups.map((questionGroup) => (
|
||||
<GridItem
|
||||
url={`/questions/${questionGroup.id}`}
|
||||
isNew={questionGroup.frontmatter.isNew}
|
||||
title={questionGroup.frontmatter.briefTitle}
|
||||
description={`${questionGroup.questions.length} Questions · ${questionGroup.allTopics.length} topics`}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
@@ -103,6 +103,22 @@ export async function getOfficialGuide(slug: string, roadmapId?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function listOfficialAuthors() {
|
||||
try {
|
||||
const authors = await httpGet<OfficialAuthorDocument[]>(
|
||||
`/v1-list-official-authors`,
|
||||
);
|
||||
|
||||
return authors;
|
||||
} catch (error) {
|
||||
if (FetchError.isFetchError(error) && error.status === 404) {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function getOfficialGuideHref(slug: string, roadmapId?: string) {
|
||||
const isExternal = roadmapId && roadmapId !== 'questions';
|
||||
return isExternal
|
||||
|
Reference in New Issue
Block a user