mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-06 17:26:29 +02:00
Add related questions below roadmaps
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
---
|
---
|
||||||
|
import { getQuestionGroupsByIds } from '../lib/question-group';
|
||||||
import { getRoadmapsByIds, RoadmapFrontmatter } from '../lib/roadmap';
|
import { getRoadmapsByIds, RoadmapFrontmatter } from '../lib/roadmap';
|
||||||
|
import { Map, Clipboard } from 'lucide-react';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
roadmap: RoadmapFrontmatter;
|
roadmap: RoadmapFrontmatter;
|
||||||
@@ -8,35 +10,87 @@ export interface Props {
|
|||||||
const { roadmap } = Astro.props;
|
const { roadmap } = Astro.props;
|
||||||
|
|
||||||
const relatedRoadmaps = roadmap.relatedRoadmaps || [];
|
const relatedRoadmaps = roadmap.relatedRoadmaps || [];
|
||||||
if (!relatedRoadmaps.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const relatedRoadmapDetails = await getRoadmapsByIds(relatedRoadmaps);
|
const relatedRoadmapDetails = await getRoadmapsByIds(relatedRoadmaps);
|
||||||
|
|
||||||
|
const relatedQuestions = roadmap.relatedQuestions || [];
|
||||||
|
const relatedQuestionDetails = await getQuestionGroupsByIds(relatedQuestions);
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class='border-t bg-gray-100'>
|
{
|
||||||
<div class='container'>
|
relatedQuestionDetails.length > 0 && (
|
||||||
<div class='flex justify-between relative -top-5'>
|
<div class='border-t bg-gray-100 pb-3'>
|
||||||
<span class='text-md font-medium py-1 px-3 border bg-white rounded-md'>Related Roadmaps</span>
|
<div class='container'>
|
||||||
<a href='/roadmaps' class='text-md font-medium py-1 px-3 border bg-white rounded-md hover:bg-gray-50'>
|
<div class='relative -top-5 flex justify-between'>
|
||||||
<span class='hidden sm:inline'>All Roadmaps →</span>
|
<span class='text-md flex items-center rounded-md border bg-white px-3 py-1 font-medium'>
|
||||||
<span class='inline sm:hidden'>More →</span>
|
<Clipboard className='mr-1.5 text-black' size='17px' />
|
||||||
</a>
|
Test your Knowledge
|
||||||
</div>
|
<span class='ml-2 rounded-md border border-yellow-300 bg-yellow-100 px-1 py-0.5 text-xs uppercase'>
|
||||||
|
New
|
||||||
<div class='flex flex-col gap-1 pb-8'>
|
</span>
|
||||||
{
|
</span>
|
||||||
relatedRoadmapDetails.map((relatedRoadmap) => (
|
|
||||||
<a
|
<a
|
||||||
href={`/${relatedRoadmap.id}`}
|
href='/roadmaps'
|
||||||
class='py-2 px-3.5 bg-white border rounded-md hover:bg-gray-50 flex flex-col sm:flex-row gap-0.5 sm:gap-0'
|
class='text-md rounded-md border bg-white px-3 py-1 font-medium hover:bg-gray-50'
|
||||||
>
|
>
|
||||||
<span class='font-medium inline-block min-w-[150px]'>{relatedRoadmap.frontmatter.briefTitle}</span>
|
<span class='hidden sm:inline'>All Quizzes →</span>
|
||||||
<span class='text-gray-500'>{relatedRoadmap.frontmatter.briefDescription}</span>
|
<span class='inline sm:hidden'>More →</span>
|
||||||
</a>
|
</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>
|
</div>
|
||||||
</div>
|
)
|
||||||
</div>
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
relatedRoadmaps.length && (
|
||||||
|
<div class='border-t bg-gray-100'>
|
||||||
|
<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'>
|
||||||
|
<Map className='text-black mr-1.5' size='17px' />
|
||||||
|
Related Roadmaps
|
||||||
|
</span>
|
||||||
|
<a
|
||||||
|
href='/roadmaps'
|
||||||
|
class='text-md rounded-md border bg-white px-3 py-1 font-medium hover:bg-gray-50'
|
||||||
|
>
|
||||||
|
<span class='hidden sm:inline'>All Roadmaps →</span>
|
||||||
|
<span class='inline sm:hidden'>More →</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='flex flex-col gap-1 pb-8'>
|
||||||
|
{relatedRoadmapDetails.map((relatedRoadmap) => (
|
||||||
|
<a
|
||||||
|
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-[150px] font-medium'>
|
||||||
|
{relatedRoadmap.frontmatter.briefTitle}
|
||||||
|
</span>
|
||||||
|
<span class='text-gray-500'>
|
||||||
|
{relatedRoadmap.frontmatter.briefDescription}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@@ -37,6 +37,8 @@ relatedRoadmaps:
|
|||||||
- 'react'
|
- 'react'
|
||||||
- 'vue'
|
- 'vue'
|
||||||
- 'nodejs'
|
- 'nodejs'
|
||||||
|
relatedQuestions:
|
||||||
|
- 'javascript'
|
||||||
sitemap:
|
sitemap:
|
||||||
priority: 1
|
priority: 1
|
||||||
changefreq: 'monthly'
|
changefreq: 'monthly'
|
||||||
|
@@ -63,6 +63,8 @@ relatedRoadmaps:
|
|||||||
- 'javascript'
|
- 'javascript'
|
||||||
- 'nodejs'
|
- 'nodejs'
|
||||||
- 'postgresql-dba'
|
- 'postgresql-dba'
|
||||||
|
relatedQuestions:
|
||||||
|
- 'javascript'
|
||||||
sitemap:
|
sitemap:
|
||||||
priority: 1
|
priority: 1
|
||||||
changefreq: 'monthly'
|
changefreq: 'monthly'
|
||||||
|
@@ -61,6 +61,9 @@ relatedRoadmaps:
|
|||||||
- 'angular'
|
- 'angular'
|
||||||
- 'vue'
|
- 'vue'
|
||||||
- 'design-system'
|
- 'design-system'
|
||||||
|
relatedQuestions:
|
||||||
|
- 'javascript'
|
||||||
|
- 'react'
|
||||||
sitemap:
|
sitemap:
|
||||||
priority: 1
|
priority: 1
|
||||||
changefreq: 'monthly'
|
changefreq: 'monthly'
|
||||||
|
@@ -45,6 +45,9 @@ relatedRoadmaps:
|
|||||||
- 'react'
|
- 'react'
|
||||||
- 'angular'
|
- 'angular'
|
||||||
- 'vue'
|
- 'vue'
|
||||||
|
relatedQuestions:
|
||||||
|
- 'javascript'
|
||||||
|
- 'react'
|
||||||
sitemap:
|
sitemap:
|
||||||
priority: 1
|
priority: 1
|
||||||
changefreq: 'monthly'
|
changefreq: 'monthly'
|
||||||
|
@@ -75,6 +75,8 @@ relatedRoadmaps:
|
|||||||
- 'system-design'
|
- 'system-design'
|
||||||
- 'graphql'
|
- 'graphql'
|
||||||
- 'frontend'
|
- 'frontend'
|
||||||
|
relatedQuestions:
|
||||||
|
- 'javascript'
|
||||||
sitemap:
|
sitemap:
|
||||||
priority: 1
|
priority: 1
|
||||||
changefreq: 'monthly'
|
changefreq: 'monthly'
|
||||||
|
@@ -40,6 +40,9 @@ relatedRoadmaps:
|
|||||||
- 'vue'
|
- 'vue'
|
||||||
- 'nodejs'
|
- 'nodejs'
|
||||||
- 'design-system'
|
- 'design-system'
|
||||||
|
relatedQuestions:
|
||||||
|
- 'react'
|
||||||
|
- 'javascript'
|
||||||
sitemap:
|
sitemap:
|
||||||
priority: 1
|
priority: 1
|
||||||
changefreq: 'monthly'
|
changefreq: 'monthly'
|
||||||
|
@@ -49,6 +49,8 @@ relatedRoadmaps:
|
|||||||
- 'react'
|
- 'react'
|
||||||
- 'angular'
|
- 'angular'
|
||||||
- 'nodejs'
|
- 'nodejs'
|
||||||
|
relatedQuestions:
|
||||||
|
- 'javascript'
|
||||||
sitemap:
|
sitemap:
|
||||||
priority: 1
|
priority: 1
|
||||||
changefreq: 'monthly'
|
changefreq: 'monthly'
|
||||||
|
@@ -54,7 +54,7 @@ export async function getAllQuestionGroups(): Promise<QuestionGroupType[]> {
|
|||||||
`/src/data/question-groups/*/*.md`,
|
`/src/data/question-groups/*/*.md`,
|
||||||
{
|
{
|
||||||
eager: true,
|
eager: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const answerFilesMap = await import.meta.glob<string>(
|
const answerFilesMap = await import.meta.glob<string>(
|
||||||
@@ -63,7 +63,7 @@ export async function getAllQuestionGroups(): Promise<QuestionGroupType[]> {
|
|||||||
{
|
{
|
||||||
eager: true,
|
eager: true,
|
||||||
as: 'raw',
|
as: 'raw',
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return Object.values(questionGroupFilesMap)
|
return Object.values(questionGroupFilesMap)
|
||||||
@@ -116,3 +116,32 @@ export async function getAllQuestionGroups(): Promise<QuestionGroupType[]> {
|
|||||||
})
|
})
|
||||||
.sort((a, b) => a.frontmatter.order - b.frontmatter.order);
|
.sort((a, b) => a.frontmatter.order - b.frontmatter.order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getQuestionGroupsByIds(
|
||||||
|
ids: string[],
|
||||||
|
): Promise<{ id: string; title: string; description: string }[]> {
|
||||||
|
if (!ids?.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const questionGroupFilesMap = import.meta.glob<
|
||||||
|
MarkdownFileType<RawQuestionGroupFrontmatter>
|
||||||
|
>(`/src/data/question-groups/*/*.md`, {
|
||||||
|
eager: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Object.values(questionGroupFilesMap)
|
||||||
|
.map((group) => {
|
||||||
|
const fileId = group?.file?.split('/')?.pop()?.replace('.md', '');
|
||||||
|
const frontmatter = group.frontmatter;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: fileId!,
|
||||||
|
title: frontmatter.briefTitle,
|
||||||
|
description: `${frontmatter.questions.length} Questions`,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((group) => {
|
||||||
|
return ids.includes(group.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@@ -34,6 +34,7 @@ export interface RoadmapFrontmatter {
|
|||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
};
|
};
|
||||||
relatedRoadmaps: string[];
|
relatedRoadmaps: string[];
|
||||||
|
relatedQuestions: string[];
|
||||||
sitemap: {
|
sitemap: {
|
||||||
priority: number;
|
priority: number;
|
||||||
changefreq: string;
|
changefreq: string;
|
||||||
@@ -61,7 +62,7 @@ export async function getRoadmapIds() {
|
|||||||
'/src/data/roadmaps/*/*.md',
|
'/src/data/roadmaps/*/*.md',
|
||||||
{
|
{
|
||||||
eager: true,
|
eager: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return Object.keys(roadmapFiles).map(roadmapPathToId);
|
return Object.keys(roadmapFiles).map(roadmapPathToId);
|
||||||
@@ -74,13 +75,13 @@ export async function getRoadmapIds() {
|
|||||||
* @returns Promisified RoadmapFileType[]
|
* @returns Promisified RoadmapFileType[]
|
||||||
*/
|
*/
|
||||||
export async function getRoadmapsByTag(
|
export async function getRoadmapsByTag(
|
||||||
tag: string
|
tag: string,
|
||||||
): Promise<RoadmapFileType[]> {
|
): Promise<RoadmapFileType[]> {
|
||||||
const roadmapFilesMap = await import.meta.glob<RoadmapFileType>(
|
const roadmapFilesMap = await import.meta.glob<RoadmapFileType>(
|
||||||
'/src/data/roadmaps/*/*.md',
|
'/src/data/roadmaps/*/*.md',
|
||||||
{
|
{
|
||||||
eager: true,
|
eager: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const roadmapFiles = Object.values(roadmapFilesMap);
|
const roadmapFiles = Object.values(roadmapFilesMap);
|
||||||
@@ -92,7 +93,7 @@ export async function getRoadmapsByTag(
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
return filteredRoadmaps.sort(
|
return filteredRoadmaps.sort(
|
||||||
(a, b) => a.frontmatter.order - b.frontmatter.order
|
(a, b) => a.frontmatter.order - b.frontmatter.order,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +102,7 @@ export async function getRoadmapById(id: string): Promise<RoadmapFileType> {
|
|||||||
'/src/data/roadmaps/*/*.md',
|
'/src/data/roadmaps/*/*.md',
|
||||||
{
|
{
|
||||||
eager: true,
|
eager: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const roadmapFile = Object.values(roadmapFilesMap).find((roadmapFile) => {
|
const roadmapFile = Object.values(roadmapFilesMap).find((roadmapFile) => {
|
||||||
@@ -119,7 +120,11 @@ export async function getRoadmapById(id: string): Promise<RoadmapFileType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getRoadmapsByIds(
|
export async function getRoadmapsByIds(
|
||||||
ids: string[]
|
ids: string[],
|
||||||
): Promise<RoadmapFileType[]> {
|
): Promise<RoadmapFileType[]> {
|
||||||
|
if (!ids?.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.all(ids.map((id) => getRoadmapById(id)));
|
return Promise.all(ids.map((id) => getRoadmapById(id)));
|
||||||
}
|
}
|
||||||
|
@@ -48,7 +48,7 @@ if (roadmapData.schema) {
|
|||||||
datePublished: roadmapSchema.datePublished,
|
datePublished: roadmapSchema.datePublished,
|
||||||
dateModified: roadmapSchema.dateModified,
|
dateModified: roadmapSchema.dateModified,
|
||||||
imageUrl: roadmapSchema.imageUrl,
|
imageUrl: roadmapSchema.imageUrl,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +116,7 @@ if (roadmapFAQs.length) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<FAQs faqs={roadmapFAQs} />
|
<FAQs faqs={roadmapFAQs} />
|
||||||
|
|
||||||
<RelatedRoadmaps roadmap={roadmapData} />
|
<RelatedRoadmaps roadmap={roadmapData} />
|
||||||
</div>
|
</div>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
Reference in New Issue
Block a user