mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-02 22:02:39 +02:00
Revert "chore: replace roadmap listing"
This reverts commit c4c28944ee
.
This commit is contained in:
@@ -23,12 +23,7 @@ type EditorRoadmapProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function EditorRoadmap(props: EditorRoadmapProps) {
|
export function EditorRoadmap(props: EditorRoadmapProps) {
|
||||||
const {
|
const { resourceId, resourceType = 'roadmap', dimensions, hasChat = true } = props;
|
||||||
resourceId,
|
|
||||||
resourceType = 'roadmap',
|
|
||||||
dimensions,
|
|
||||||
hasChat = true,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const [hasSwitchedRoadmap, setHasSwitchedRoadmap] = useState(false);
|
const [hasSwitchedRoadmap, setHasSwitchedRoadmap] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
import type { RoadmapFileType } from '../lib/roadmap';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
url: string;
|
url: string;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -25,7 +27,7 @@ const { url, title, description, isNew } = Astro.props;
|
|||||||
|
|
||||||
{
|
{
|
||||||
isNew && (
|
isNew && (
|
||||||
<span class='absolute right-1 bottom-1.5 flex items-center gap-1.5 rounded-xs text-xs font-semibold text-purple-500 uppercase sm:px-1.5'>
|
<span class='flex items-center gap-1.5 absolute bottom-1.5 right-1 rounded-xs text-xs font-semibold uppercase text-purple-500 sm:px-1.5'>
|
||||||
<span class='relative flex h-2 w-2'>
|
<span class='relative flex h-2 w-2'>
|
||||||
<span class='absolute inline-flex h-full w-full animate-ping rounded-full bg-purple-400 opacity-75' />
|
<span class='absolute inline-flex h-full w-full animate-ping rounded-full bg-purple-400 opacity-75' />
|
||||||
<span class='relative inline-flex h-2 w-2 rounded-full bg-purple-500' />
|
<span class='relative inline-flex h-2 w-2 rounded-full bg-purple-500' />
|
||||||
|
@@ -1,8 +1,5 @@
|
|||||||
import {
|
|
||||||
officialRoadmapDetails,
|
|
||||||
type OfficialRoadmapDocument,
|
|
||||||
} from '../queries/official-roadmap';
|
|
||||||
import type { MarkdownFileType } from './file';
|
import type { MarkdownFileType } from './file';
|
||||||
|
import { getRoadmapById, type RoadmapFileType } from './roadmap';
|
||||||
|
|
||||||
export const projectDifficulties = [
|
export const projectDifficulties = [
|
||||||
'beginner',
|
'beginner',
|
||||||
@@ -31,7 +28,7 @@ export interface ProjectFrontmatter {
|
|||||||
|
|
||||||
export type ProjectFileType = MarkdownFileType<ProjectFrontmatter> & {
|
export type ProjectFileType = MarkdownFileType<ProjectFrontmatter> & {
|
||||||
id: string;
|
id: string;
|
||||||
roadmaps: OfficialRoadmapDocument[];
|
roadmaps: RoadmapFileType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,7 +85,7 @@ export async function getProjectById(
|
|||||||
const project = await import(`../data/projects/${groupId}.md`);
|
const project = await import(`../data/projects/${groupId}.md`);
|
||||||
const roadmapIds = project.frontmatter.roadmapIds || [];
|
const roadmapIds = project.frontmatter.roadmapIds || [];
|
||||||
const roadmaps = await Promise.all(
|
const roadmaps = await Promise.all(
|
||||||
roadmapIds.map((roadmapId: string) => officialRoadmapDetails(roadmapId)),
|
roadmapIds.map((roadmapId: string) => getRoadmapById(roadmapId)),
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -1,7 +1,19 @@
|
|||||||
import type { PageType } from '../components/CommandMenu/CommandMenu';
|
import type { PageType } from '../components/CommandMenu/CommandMenu';
|
||||||
|
import type { MarkdownFileType } from './file';
|
||||||
import { httpGet } from './http';
|
import { httpGet } from './http';
|
||||||
import type { ResourceType } from './resource-progress';
|
import type { ResourceType } from './resource-progress';
|
||||||
|
|
||||||
|
export function resourceTitleFromId(id: string): string {
|
||||||
|
if (id === 'devops') {
|
||||||
|
return 'DevOps';
|
||||||
|
}
|
||||||
|
|
||||||
|
return id
|
||||||
|
.split('-')
|
||||||
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
export type AllowedRoadmapRenderer = 'balsamiq' | 'editor';
|
export type AllowedRoadmapRenderer = 'balsamiq' | 'editor';
|
||||||
|
|
||||||
export interface RoadmapFrontmatter {
|
export interface RoadmapFrontmatter {
|
||||||
@@ -64,6 +76,99 @@ export interface RoadmapFrontmatter {
|
|||||||
renderer?: AllowedRoadmapRenderer;
|
renderer?: AllowedRoadmapRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RoadmapFileType = MarkdownFileType<RoadmapFrontmatter> & {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function roadmapPathToId(filePath: string): string {
|
||||||
|
const fileName = filePath.split('/').pop() || '';
|
||||||
|
|
||||||
|
return fileName.replace('.md', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the IDs of all the roadmaps available on the website
|
||||||
|
*
|
||||||
|
* @returns string[] Array of roadmap IDs
|
||||||
|
*/
|
||||||
|
export async function getRoadmapIds() {
|
||||||
|
const roadmapFiles = import.meta.glob<RoadmapFileType>(
|
||||||
|
'/src/data/roadmaps/*/*.md',
|
||||||
|
{
|
||||||
|
eager: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return Object.keys(roadmapFiles).map(roadmapPathToId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the roadmap files which have the given tag assigned
|
||||||
|
*
|
||||||
|
* @param tag Tag assigned to roadmap
|
||||||
|
* @returns Promisified RoadmapFileType[]
|
||||||
|
*/
|
||||||
|
export async function getRoadmapsByTag(
|
||||||
|
tag: string,
|
||||||
|
): Promise<RoadmapFileType[]> {
|
||||||
|
const roadmapFilesMap = import.meta.glob<RoadmapFileType>(
|
||||||
|
'/src/data/roadmaps/*/*.md',
|
||||||
|
{
|
||||||
|
eager: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const roadmapFiles: RoadmapFileType[] = Object.values(roadmapFilesMap);
|
||||||
|
const filteredRoadmaps = roadmapFiles
|
||||||
|
.filter((roadmapFile) => roadmapFile.frontmatter.tags?.includes(tag))
|
||||||
|
.map((roadmapFile) => ({
|
||||||
|
...roadmapFile,
|
||||||
|
id: roadmapPathToId(roadmapFile.file),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return filteredRoadmaps.sort(
|
||||||
|
(a, b) => a.frontmatter.order - b.frontmatter.order,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRoadmapById(id: string): Promise<RoadmapFileType> {
|
||||||
|
const roadmapFilesMap: Record<string, RoadmapFileType> =
|
||||||
|
import.meta.glob<RoadmapFileType>('/src/data/roadmaps/*/*.md', {
|
||||||
|
eager: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const roadmapFile = Object.values(roadmapFilesMap).find((roadmapFile) => {
|
||||||
|
return roadmapPathToId(roadmapFile.file) === id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!roadmapFile) {
|
||||||
|
throw new Error(`Roadmap with ID ${id} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...roadmapFile,
|
||||||
|
id: roadmapPathToId(roadmapFile.file),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRoadmapsByIds(
|
||||||
|
ids: string[],
|
||||||
|
): Promise<RoadmapFileType[]> {
|
||||||
|
if (!ids?.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(ids.map((id) => getRoadmapById(id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRoadmapFaqsById(roadmapId: string): Promise<string[]> {
|
||||||
|
const { faqs } = await import(
|
||||||
|
`../data/roadmaps/${roadmapId}/faqs.astro`
|
||||||
|
).catch(() => ({}));
|
||||||
|
|
||||||
|
return faqs || [];
|
||||||
|
}
|
||||||
|
|
||||||
export async function getResourceMeta(
|
export async function getResourceMeta(
|
||||||
resourceType: ResourceType,
|
resourceType: ResourceType,
|
||||||
resourceId: string,
|
resourceId: string,
|
||||||
|
@@ -1,13 +1,10 @@
|
|||||||
---
|
---
|
||||||
import Icon from '../components/AstroIcon.astro';
|
import Icon from '../components/AstroIcon.astro';
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
import { listOfficialRoadmaps } from '../queries/official-roadmap';
|
import { getRoadmapIds } from '../lib/roadmap';
|
||||||
|
|
||||||
const roadmapIds = await listOfficialRoadmaps();
|
const roadmapIds = await getRoadmapIds();
|
||||||
const legacyRoadmapUrls = [
|
const legacyRoadmapUrls = [...roadmapIds.map((id) => `/${id}/`), '/roadmaps/'];
|
||||||
...roadmapIds.map((roadmap) => `/${roadmap.slug}/`),
|
|
||||||
'/roadmaps/',
|
|
||||||
];
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title='Page not found' permalink={'/404'} noIndex={true}>
|
<BaseLayout title='Page not found' permalink={'/404'} noIndex={true}>
|
||||||
@@ -21,26 +18,20 @@ const legacyRoadmapUrls = [
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class='bg-gray-100'>
|
<div class='bg-gray-100'>
|
||||||
<div
|
<div class='py-10 md:py-32 container flex flex-col md:flex-row items-center justify-center gap-7'>
|
||||||
class='container flex flex-col items-center justify-center gap-7 py-10 md:flex-row md:py-32'
|
|
||||||
>
|
|
||||||
<Icon icon='construction' class='hidden md:block' />
|
<Icon icon='construction' class='hidden md:block' />
|
||||||
<div class='text-left md:text-left'>
|
<div class='text-left md:text-left'>
|
||||||
<h1
|
<h1
|
||||||
class='bg-linear-to-t from-black to-gray-600 bg-clip-text text-2xl leading-normal font-extrabold text-transparent md:text-5xl md:leading-normal'
|
class='font-extrabold text-transparent text-2xl leading-normal md:text-5xl md:leading-normal bg-clip-text bg-linear-to-t from-black to-gray-600'
|
||||||
>
|
>
|
||||||
Page not found!
|
Page not found!
|
||||||
</h1>
|
</h1>
|
||||||
<p class='text-md mb-2 md:text-xl'>
|
<p class='text-md md:text-xl mb-2'>Sorry, we couldn't find the page you are looking for.</p>
|
||||||
Sorry, we couldn't find the page you are looking for.
|
|
||||||
</p>
|
|
||||||
<p>
|
<p>
|
||||||
<a class='text-blue-700 underline' href='/'>Homepage</a> · <a
|
<a class='underline text-blue-700' href='/'>Homepage</a> · <a
|
||||||
href='/roadmaps'
|
href='/roadmaps'
|
||||||
class='text-blue-700 underline'>Roadmaps</a
|
class='underline text-blue-700'>Roadmaps</a
|
||||||
> · <a href='/best-practices' class='text-blue-700 underline'
|
> · <a href='/best-practices' class='underline text-blue-700'>Best Practices</a>
|
||||||
>Best Practices</a
|
|
||||||
>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<!-- ---
|
---
|
||||||
import RoadmapHeader from '../../components/RoadmapHeader.astro';
|
import RoadmapHeader from '../../components/RoadmapHeader.astro';
|
||||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||||
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
||||||
@@ -163,4 +163,4 @@ const courses = roadmapData.courses || [];
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</BaseLayout> -->
|
</BaseLayout>
|
||||||
|
@@ -5,19 +5,16 @@ import { ProjectsList } from '../../components/Projects/ProjectsList';
|
|||||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||||
import { getProjectsByRoadmapId } from '../../lib/project';
|
import { getProjectsByRoadmapId } from '../../lib/project';
|
||||||
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
||||||
|
import { type RoadmapFrontmatter, getRoadmapIds } from '../../lib/roadmap';
|
||||||
import { projectApi } from '../../api/project';
|
import { projectApi } from '../../api/project';
|
||||||
import {
|
|
||||||
listOfficialRoadmaps,
|
|
||||||
officialRoadmapDetails,
|
|
||||||
} from '../../queries/official-roadmap';
|
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const roadmapIds = await listOfficialRoadmaps();
|
const roadmapIds = await getRoadmapIds();
|
||||||
|
|
||||||
return roadmapIds.map((roadmap) => ({
|
return roadmapIds.map((roadmapId) => ({
|
||||||
params: { roadmapId: roadmap.slug },
|
params: { roadmapId },
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,14 +23,15 @@ interface Params extends Record<string, string | undefined> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { roadmapId } = Astro.params as Params;
|
const { roadmapId } = Astro.params as Params;
|
||||||
const roadmapData = await officialRoadmapDetails(roadmapId);
|
const roadmapFile = await import(
|
||||||
if (!roadmapData) {
|
`../../data/roadmaps/${roadmapId}/${roadmapId}.md`
|
||||||
return Astro.rewrite('/404');
|
);
|
||||||
}
|
|
||||||
|
const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
|
||||||
|
|
||||||
// update og for projects
|
// update og for projects
|
||||||
const ogImageUrl =
|
const ogImageUrl =
|
||||||
roadmapData?.openGraph?.image ||
|
roadmapData?.seo?.ogImageUrl ||
|
||||||
getOpenGraphImageUrl({
|
getOpenGraphImageUrl({
|
||||||
group: 'roadmap',
|
group: 'roadmap',
|
||||||
resourceId: roadmapId,
|
resourceId: roadmapId,
|
||||||
@@ -46,13 +44,13 @@ const descriptionNoun: Record<string, string> = {
|
|||||||
'Product Manager': 'Product Management',
|
'Product Manager': 'Product Management',
|
||||||
};
|
};
|
||||||
|
|
||||||
const title = `${roadmapData.title.card} Projects`;
|
const title = `${roadmapData.briefTitle} Projects`;
|
||||||
const description = `Project ideas to take you from beginner to advanced in ${descriptionNoun[roadmapData.title.card] || roadmapData.title.card}`;
|
const description = `Project ideas to take you from beginner to advanced in ${descriptionNoun[roadmapData.briefTitle] || roadmapData.briefTitle}`;
|
||||||
|
|
||||||
// `Seeking backend projects to enhance your skills? Explore our top 20 project ideas, from simple apps to complex systems. Start building today!`
|
// `Seeking backend projects to enhance your skills? Explore our top 20 project ideas, from simple apps to complex systems. Start building today!`
|
||||||
const seoTitle = `${roadmapData.title.card} Projects`;
|
const seoTitle = `${roadmapData.briefTitle} Projects`;
|
||||||
const nounTitle =
|
const nounTitle =
|
||||||
descriptionNoun[roadmapData?.title.card] || roadmapData.title.card;
|
descriptionNoun[roadmapData?.briefTitle] || roadmapData.briefTitle;
|
||||||
const seoDescription = `Seeking ${nounTitle.toLowerCase()} projects to enhance your skills? Explore our top 20 project ideas, from simple apps to complex systems. Start building today!`;
|
const seoDescription = `Seeking ${nounTitle.toLowerCase()} projects to enhance your skills? Explore our top 20 project ideas, from simple apps to complex systems. Start building today!`;
|
||||||
|
|
||||||
const projects = await getProjectsByRoadmapId(roadmapId);
|
const projects = await getProjectsByRoadmapId(roadmapId);
|
||||||
@@ -67,7 +65,7 @@ const { response: userCounts } =
|
|||||||
permalink={`/${roadmapId}/projects`}
|
permalink={`/${roadmapId}/projects`}
|
||||||
title={seoTitle}
|
title={seoTitle}
|
||||||
description={seoDescription}
|
description={seoDescription}
|
||||||
briefTitle={roadmapData.title.card}
|
briefTitle={roadmapData.briefTitle}
|
||||||
ogImageUrl={ogImageUrl}
|
ogImageUrl={ogImageUrl}
|
||||||
keywords={roadmapData.seo.keywords}
|
keywords={roadmapData.seo.keywords}
|
||||||
noIndex={projects.length === 0}
|
noIndex={projects.length === 0}
|
||||||
@@ -75,16 +73,15 @@ const { response: userCounts } =
|
|||||||
resourceType='roadmap'
|
resourceType='roadmap'
|
||||||
>
|
>
|
||||||
<div class='bg-gray-50'>
|
<div class='bg-gray-50'>
|
||||||
<!-- // TODO: get courses count -->
|
|
||||||
<RoadmapHeader
|
<RoadmapHeader
|
||||||
title={title}
|
title={title}
|
||||||
description={description}
|
description={description}
|
||||||
partner={roadmapData.partner}
|
partner={roadmapData.partner}
|
||||||
roadmapId={roadmapId}
|
roadmapId={roadmapId}
|
||||||
isForkable={true}
|
isForkable={roadmapData.isForkable}
|
||||||
activeTab='projects'
|
activeTab='projects'
|
||||||
projectCount={projects.length}
|
projectCount={projects.length}
|
||||||
coursesCount={0}
|
coursesCount={roadmapData.courses?.length || 0}
|
||||||
hasAIChat={true}
|
hasAIChat={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@@ -1,19 +1,17 @@
|
|||||||
---
|
---
|
||||||
import { EditorRoadmap } from '../../components/EditorRoadmap/EditorRoadmap';
|
import { EditorRoadmap } from '../../components/EditorRoadmap/EditorRoadmap';
|
||||||
|
import FrameRenderer from '../../components/FrameRenderer/FrameRenderer.astro';
|
||||||
import SkeletonLayout from '../../layouts/SkeletonLayout.astro';
|
import SkeletonLayout from '../../layouts/SkeletonLayout.astro';
|
||||||
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
||||||
import {
|
import { type RoadmapFrontmatter, getRoadmapIds } from '../../lib/roadmap';
|
||||||
listOfficialRoadmaps,
|
|
||||||
officialRoadmapDetails,
|
|
||||||
} from '../../queries/official-roadmap';
|
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const roadmapIds = await listOfficialRoadmaps();
|
const roadmapIds = await getRoadmapIds();
|
||||||
|
|
||||||
return roadmapIds.map((roadmap) => ({
|
return roadmapIds.map((roadmapId) => ({
|
||||||
params: { roadmapId: roadmap.slug },
|
params: { roadmapId },
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,13 +20,13 @@ interface Params extends Record<string, string | undefined> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { roadmapId } = Astro.params as Params;
|
const { roadmapId } = Astro.params as Params;
|
||||||
const roadmapData = await officialRoadmapDetails(roadmapId);
|
const roadmapFile = await import(
|
||||||
if (!roadmapData) {
|
`../../data/roadmaps/${roadmapId}/${roadmapId}.md`
|
||||||
return Astro.rewrite('/404');
|
);
|
||||||
}
|
const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
|
||||||
|
|
||||||
const ogImageUrl =
|
const ogImageUrl =
|
||||||
roadmapData?.openGraph?.image ||
|
roadmapData?.seo?.ogImageUrl ||
|
||||||
getOpenGraphImageUrl({
|
getOpenGraphImageUrl({
|
||||||
group: 'roadmap',
|
group: 'roadmap',
|
||||||
resourceId: roadmapId,
|
resourceId: roadmapId,
|
||||||
@@ -37,8 +35,8 @@ const ogImageUrl =
|
|||||||
|
|
||||||
<SkeletonLayout
|
<SkeletonLayout
|
||||||
permalink={`/${roadmapId}`}
|
permalink={`/${roadmapId}`}
|
||||||
title={roadmapData?.seo?.title || roadmapData.title.page}
|
title={roadmapData?.seo?.title}
|
||||||
briefTitle={roadmapData.title.card}
|
briefTitle={roadmapData.briefTitle}
|
||||||
ogImageUrl={ogImageUrl}
|
ogImageUrl={ogImageUrl}
|
||||||
description={roadmapData.seo.description}
|
description={roadmapData.seo.description}
|
||||||
keywords={roadmapData.seo.keywords}
|
keywords={roadmapData.seo.keywords}
|
||||||
@@ -46,13 +44,23 @@ const ogImageUrl =
|
|||||||
resourceType='roadmap'
|
resourceType='roadmap'
|
||||||
noIndex={true}
|
noIndex={true}
|
||||||
>
|
>
|
||||||
<div class='relative container max-w-[1000px]!'>
|
<div class='container relative max-w-[1000px]!'>
|
||||||
<EditorRoadmap
|
{
|
||||||
resourceId={roadmapId}
|
roadmapData?.renderer === 'editor' ? (
|
||||||
resourceType='roadmap'
|
<EditorRoadmap
|
||||||
dimensions={roadmapData.dimensions!}
|
resourceId={roadmapId}
|
||||||
client:load
|
resourceType='roadmap'
|
||||||
hasChat={false}
|
dimensions={roadmapData.dimensions!}
|
||||||
/>
|
client:load
|
||||||
|
hasChat={false}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FrameRenderer
|
||||||
|
resourceType={'roadmap'}
|
||||||
|
resourceId={roadmapId}
|
||||||
|
dimensions={roadmapData.dimensions}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</SkeletonLayout>
|
</SkeletonLayout>
|
||||||
|
@@ -1,53 +1,56 @@
|
|||||||
---
|
---
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import { DashboardPage } from '../components/Dashboard/DashboardPage';
|
import { DashboardPage } from '../components/Dashboard/DashboardPage';
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
import { getAllBestPractices } from '../lib/best-practice';
|
import { getAllBestPractices } from '../lib/best-practice';
|
||||||
import { getAllQuestionGroups } from '../lib/question-group';
|
import { getAllQuestionGroups } from '../lib/question-group';
|
||||||
|
import { getRoadmapsByTag } from '../lib/roadmap';
|
||||||
import { getAllVideos } from '../lib/video';
|
import { getAllVideos } from '../lib/video';
|
||||||
import { listOfficialGuides } from '../queries/official-guide';
|
import { listOfficialGuides } from '../queries/official-guide';
|
||||||
import {
|
|
||||||
isNewRoadmap,
|
|
||||||
listOfficialRoadmaps,
|
|
||||||
} from '../queries/official-roadmap';
|
|
||||||
import type { BuiltInRoadmap } from '../components/Dashboard/PersonalDashboard';
|
|
||||||
|
|
||||||
const roadmaps = await listOfficialRoadmaps();
|
const roleRoadmaps = await getRoadmapsByTag('role-roadmap');
|
||||||
const roleRoadmaps = roadmaps.filter((roadmap) => roadmap.type === 'role');
|
const skillRoadmaps = await getRoadmapsByTag('skill-roadmap');
|
||||||
const skillRoadmaps = roadmaps.filter((roadmap) => roadmap.type === 'skill');
|
|
||||||
const bestPractices = await getAllBestPractices();
|
const bestPractices = await getAllBestPractices();
|
||||||
const questionGroups = await getAllQuestionGroups();
|
const questionGroups = await getAllQuestionGroups();
|
||||||
const guides = await listOfficialGuides();
|
const guides = await listOfficialGuides();
|
||||||
const videos = await getAllVideos();
|
const videos = await getAllVideos();
|
||||||
|
|
||||||
const enrichedRoleRoadmaps: BuiltInRoadmap[] = roleRoadmaps.map((roadmap) => {
|
const enrichedRoleRoadmaps = roleRoadmaps
|
||||||
return {
|
.filter((roadmapItem) => !roadmapItem.frontmatter.isHidden)
|
||||||
id: roadmap.slug,
|
.map((roadmap) => {
|
||||||
url: `/${roadmap.slug}`,
|
const { frontmatter } = roadmap;
|
||||||
title: roadmap.title.card,
|
|
||||||
description: roadmap.description,
|
return {
|
||||||
relatedRoadmapIds: roadmap.relatedRoadmaps,
|
id: roadmap.id,
|
||||||
renderer: 'editor',
|
url: `/${roadmap.id}`,
|
||||||
isNew: isNewRoadmap(roadmap.createdAt),
|
title: frontmatter.briefTitle,
|
||||||
metadata: {
|
description: frontmatter.briefDescription,
|
||||||
tags: ['role-roadmap'],
|
relatedRoadmapIds: frontmatter.relatedRoadmaps,
|
||||||
},
|
renderer: frontmatter.renderer,
|
||||||
};
|
isNew: frontmatter.isNew,
|
||||||
});
|
metadata: {
|
||||||
const enrichedSkillRoadmaps: BuiltInRoadmap[] = skillRoadmaps.map((roadmap) => {
|
tags: frontmatter.tags,
|
||||||
return {
|
},
|
||||||
id: roadmap.slug,
|
};
|
||||||
url: `/${roadmap.slug}`,
|
});
|
||||||
title: roadmap.title.card === 'Go' ? 'Go Roadmap' : roadmap.title.card,
|
const enrichedSkillRoadmaps = skillRoadmaps
|
||||||
description: roadmap.description,
|
.filter((roadmapItem) => !roadmapItem.frontmatter.isHidden)
|
||||||
relatedRoadmapIds: roadmap.relatedRoadmaps,
|
.map((roadmap) => {
|
||||||
renderer: 'editor',
|
const { frontmatter } = roadmap;
|
||||||
isNew: isNewRoadmap(roadmap.createdAt),
|
|
||||||
metadata: {
|
return {
|
||||||
tags: ['skill-roadmap'],
|
id: roadmap.id,
|
||||||
},
|
url: `/${roadmap.id}`,
|
||||||
};
|
title:
|
||||||
});
|
frontmatter.briefTitle === 'Go' ? 'Go Roadmap' : frontmatter.briefTitle,
|
||||||
|
description: frontmatter.briefDescription,
|
||||||
|
relatedRoadmapIds: frontmatter.relatedRoadmaps,
|
||||||
|
renderer: frontmatter.renderer,
|
||||||
|
isNew: frontmatter.isNew,
|
||||||
|
metadata: {
|
||||||
|
tags: frontmatter.tags,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const enrichedBestPractices = bestPractices.map((bestPractice) => {
|
const enrichedBestPractices = bestPractices.map((bestPractice) => {
|
||||||
const { frontmatter } = bestPractice;
|
const { frontmatter } = bestPractice;
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
---
|
---
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import ChangelogBanner from '../components/ChangelogBanner.astro';
|
import ChangelogBanner from '../components/ChangelogBanner.astro';
|
||||||
import { FeaturedGuideList } from '../components/FeaturedGuides/FeaturedGuideList';
|
import { FeaturedGuideList } from '../components/FeaturedGuides/FeaturedGuideList';
|
||||||
import FeaturedItems from '../components/FeaturedItems/FeaturedItems.astro';
|
import FeaturedItems from '../components/FeaturedItems/FeaturedItems.astro';
|
||||||
@@ -7,16 +6,12 @@ import { FeaturedVideoList } from '../components/FeaturedVideos/FeaturedVideoLis
|
|||||||
import HeroSection from '../components/HeroSection/HeroSection.astro';
|
import HeroSection from '../components/HeroSection/HeroSection.astro';
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
import { getAllBestPractices } from '../lib/best-practice';
|
import { getAllBestPractices } from '../lib/best-practice';
|
||||||
|
import { getRoadmapsByTag } from '../lib/roadmap';
|
||||||
import { getAllVideos } from '../lib/video';
|
import { getAllVideos } from '../lib/video';
|
||||||
import { listOfficialGuides } from '../queries/official-guide';
|
import { listOfficialGuides } from '../queries/official-guide';
|
||||||
import {
|
|
||||||
isNewRoadmap,
|
|
||||||
listOfficialRoadmaps,
|
|
||||||
} from '../queries/official-roadmap';
|
|
||||||
|
|
||||||
const roadmaps = await listOfficialRoadmaps();
|
const roleRoadmaps = await getRoadmapsByTag('role-roadmap');
|
||||||
const roleRoadmaps = roadmaps.filter((roadmap) => roadmap.type === 'role');
|
const skillRoadmaps = await getRoadmapsByTag('skill-roadmap');
|
||||||
const skillRoadmaps = roadmaps.filter((roadmap) => roadmap.type === 'skill');
|
|
||||||
const bestPractices = await getAllBestPractices();
|
const bestPractices = await getAllBestPractices();
|
||||||
|
|
||||||
export const projectGroups = [
|
export const projectGroups = [
|
||||||
@@ -52,32 +47,33 @@ const videos = await getAllVideos();
|
|||||||
|
|
||||||
<FeaturedItems
|
<FeaturedItems
|
||||||
heading='Role-based Roadmaps'
|
heading='Role-based Roadmaps'
|
||||||
featuredItems={roleRoadmaps.map((roadmapItem) => {
|
featuredItems={roleRoadmaps
|
||||||
const isNew = isNewRoadmap(roadmapItem.createdAt);
|
.filter((roadmapItem) => !roadmapItem.frontmatter.isHidden)
|
||||||
|
.map((roadmapItem) => ({
|
||||||
return {
|
text: roadmapItem.frontmatter.briefTitle,
|
||||||
text: roadmapItem.title.card,
|
url: `/${roadmapItem.id}`,
|
||||||
url: `/${roadmapItem.slug}`,
|
isNew: roadmapItem.frontmatter.isNew,
|
||||||
isNew,
|
isUpcoming: roadmapItem.frontmatter.isUpcoming,
|
||||||
};
|
}))}
|
||||||
})}
|
|
||||||
showCreateRoadmap={true}
|
showCreateRoadmap={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FeaturedItems
|
<FeaturedItems
|
||||||
heading='Skill-based Roadmaps'
|
heading='Skill-based Roadmaps'
|
||||||
featuredItems={skillRoadmaps.map((roadmapItem) => {
|
featuredItems={skillRoadmaps
|
||||||
const isNew = isNewRoadmap(roadmapItem.createdAt);
|
.filter((roadmapItem) => !roadmapItem.frontmatter.isHidden)
|
||||||
|
.map((roadmapItem) => ({
|
||||||
return {
|
|
||||||
text:
|
text:
|
||||||
roadmapItem.title.card === 'Go'
|
roadmapItem.frontmatter.briefTitle === 'Go'
|
||||||
? 'Go Roadmap'
|
? 'Go Roadmap'
|
||||||
: roadmapItem.title.card.replace('Software Design', 'Design'),
|
: roadmapItem.frontmatter.briefTitle.replace(
|
||||||
url: `/${roadmapItem.slug}`,
|
'Software Design',
|
||||||
isNew,
|
'Design',
|
||||||
};
|
),
|
||||||
})}
|
url: `/${roadmapItem.id}`,
|
||||||
|
isNew: roadmapItem.frontmatter.isNew,
|
||||||
|
isUpcoming: roadmapItem.frontmatter.isUpcoming,
|
||||||
|
}))}
|
||||||
showCreateRoadmap={true}
|
showCreateRoadmap={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { getAllBestPractices } from '../lib/best-practice';
|
import { getAllBestPractices } from '../lib/best-practice';
|
||||||
|
import { getRoadmapsByTag } from '../lib/roadmap';
|
||||||
import { getAllVideos } from '../lib/video';
|
import { getAllVideos } from '../lib/video';
|
||||||
import { getAllQuestionGroups } from '../lib/question-group';
|
import { getAllQuestionGroups } from '../lib/question-group';
|
||||||
import { getAllProjects } from '../lib/project';
|
import { getAllProjects } from '../lib/project';
|
||||||
@@ -6,7 +7,6 @@ import {
|
|||||||
listOfficialAuthors,
|
listOfficialAuthors,
|
||||||
listOfficialGuides,
|
listOfficialGuides,
|
||||||
} from '../queries/official-guide';
|
} from '../queries/official-guide';
|
||||||
import { listOfficialRoadmaps } from '../queries/official-roadmap';
|
|
||||||
|
|
||||||
// Add utility to fetch beginner roadmap file IDs
|
// Add utility to fetch beginner roadmap file IDs
|
||||||
function getBeginnerRoadmapIds() {
|
function getBeginnerRoadmapIds() {
|
||||||
@@ -25,49 +25,40 @@ export async function GET() {
|
|||||||
const authors = await listOfficialAuthors();
|
const authors = await listOfficialAuthors();
|
||||||
const videos = await getAllVideos();
|
const videos = await getAllVideos();
|
||||||
const questionGroups = await getAllQuestionGroups();
|
const questionGroups = await getAllQuestionGroups();
|
||||||
const roadmaps = await listOfficialRoadmaps();
|
const roadmaps = await getRoadmapsByTag('roadmap');
|
||||||
|
|
||||||
const bestPractices = await getAllBestPractices();
|
const bestPractices = await getAllBestPractices();
|
||||||
const projects = await getAllProjects();
|
const projects = await getAllProjects();
|
||||||
|
|
||||||
// Transform main roadmaps into page objects first so that we can reuse their meta for beginner variants
|
// Transform main roadmaps into page objects first so that we can reuse their meta for beginner variants
|
||||||
const roadmapPages = roadmaps
|
const roadmapPages = roadmaps.map((roadmap) => ({
|
||||||
.map((roadmap) => {
|
id: roadmap.id,
|
||||||
const isBeginner = roadmap.slug.endsWith('-beginner');
|
url: `/${roadmap.id}`,
|
||||||
if (!isBeginner) {
|
title: roadmap.frontmatter.briefTitle,
|
||||||
return {
|
shortTitle: roadmap.frontmatter.title,
|
||||||
id: roadmap.slug,
|
description: roadmap.frontmatter.briefDescription,
|
||||||
url: `/${roadmap.slug}`,
|
group: 'Roadmaps',
|
||||||
title: roadmap.title.card,
|
metadata: {
|
||||||
shortTitle: roadmap.title.card,
|
tags: roadmap.frontmatter.tags,
|
||||||
description: roadmap.description,
|
},
|
||||||
group: 'Roadmaps',
|
renderer: roadmap?.frontmatter?.renderer || 'balsamiq',
|
||||||
metadata: {
|
}));
|
||||||
tags:
|
|
||||||
roadmap.type === 'role' ? ['role-roadmap'] : ['skill-roadmap'],
|
|
||||||
},
|
|
||||||
renderer: 'editor',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentSlug = roadmap.slug.replace('-beginner', '');
|
// Generate beginner roadmap page objects
|
||||||
const parentMeta = roadmaps.find((r) => r.slug === parentSlug);
|
const beginnerRoadmapPages = getBeginnerRoadmapIds()
|
||||||
|
.map((beginnerId) => {
|
||||||
|
const parentId = beginnerId.replace('-beginner', '');
|
||||||
|
const parentMeta = roadmapPages.find((page) => page.id === parentId);
|
||||||
|
|
||||||
if (!parentMeta) {
|
if (!parentMeta) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: roadmap.slug,
|
...parentMeta,
|
||||||
url: `/${parentSlug}?r=${roadmap.slug}`,
|
id: beginnerId,
|
||||||
title: `${parentMeta.title.page} Beginner`,
|
url: `/${parentId}?r=${beginnerId}`,
|
||||||
shortTitle: `${parentMeta.title.page} Beginner`,
|
title: `${parentMeta.title} Beginner`,
|
||||||
description: parentMeta.description,
|
shortTitle: `${parentMeta.shortTitle} Beginner`,
|
||||||
group: 'Roadmaps',
|
|
||||||
metadata: {
|
|
||||||
tags: ['beginner-roadmap'],
|
|
||||||
},
|
|
||||||
renderer: 'editor',
|
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
@@ -75,6 +66,7 @@ export async function GET() {
|
|||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify([
|
JSON.stringify([
|
||||||
...roadmapPages,
|
...roadmapPages,
|
||||||
|
...beginnerRoadmapPages,
|
||||||
...bestPractices.map((bestPractice) => ({
|
...bestPractices.map((bestPractice) => ({
|
||||||
id: bestPractice.id,
|
id: bestPractice.id,
|
||||||
url: `/best-practices/${bestPractice.id}`,
|
url: `/best-practices/${bestPractice.id}`,
|
||||||
|
@@ -1,27 +1,23 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||||
import { getRoadmapsProjects } from '../../lib/project';
|
import { getRoadmapsProjects } from '../../lib/project';
|
||||||
|
import { getRoadmapsByIds } from '../../lib/roadmap';
|
||||||
import { ProjectsPageHeader } from '../../components/Projects/ProjectsPageHeader';
|
import { ProjectsPageHeader } from '../../components/Projects/ProjectsPageHeader';
|
||||||
import { ProjectsPage } from '../../components/Projects/ProjectsPage';
|
import { ProjectsPage } from '../../components/Projects/ProjectsPage';
|
||||||
import { projectApi } from '../../api/project';
|
import { projectApi } from '../../api/project';
|
||||||
import { listOfficialRoadmaps } from '../../queries/official-roadmap';
|
|
||||||
|
|
||||||
const roadmapProjects = await getRoadmapsProjects();
|
const roadmapProjects = await getRoadmapsProjects();
|
||||||
const allRoadmapIds = Object.keys(roadmapProjects);
|
const allRoadmapIds = Object.keys(roadmapProjects);
|
||||||
|
|
||||||
const roadmaps = await listOfficialRoadmaps();
|
const allRoadmaps = await getRoadmapsByIds(allRoadmapIds);
|
||||||
const allRoadmaps = roadmaps.filter((roadmap) =>
|
|
||||||
allRoadmapIds.includes(roadmap.slug),
|
|
||||||
);
|
|
||||||
|
|
||||||
const enrichedRoadmaps = allRoadmaps.map((roadmap) => {
|
const enrichedRoadmaps = allRoadmaps.map((roadmap) => {
|
||||||
const projects = (roadmapProjects[roadmap.slug] || []).sort((a, b) => {
|
const projects = (roadmapProjects[roadmap.id] || []).sort((a, b) => {
|
||||||
return a.frontmatter.sort - b.frontmatter.sort;
|
return a.frontmatter.sort - b.frontmatter.sort;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: roadmap.slug,
|
id: roadmap.id,
|
||||||
title: roadmap.title.card,
|
title: roadmap.frontmatter.briefTitle,
|
||||||
projects,
|
projects,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -46,5 +42,5 @@ const { response: userCounts } =
|
|||||||
userCounts={userCounts || {}}
|
userCounts={userCounts || {}}
|
||||||
client:load
|
client:load
|
||||||
/>
|
/>
|
||||||
<div slot='changelog-banner'></div>
|
<div slot="changelog-banner" />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
---
|
---
|
||||||
import { RoadmapsPage } from '../components/Roadmaps/RoadmapsPage';
|
import { RoadmapsPage } from '../components/Roadmaps/RoadmapsPage';
|
||||||
import { RoadmapsPageHeader } from '../components/Roadmaps/RoadmapsPageHeader';
|
import { RoadmapsPageHeader } from '../components/Roadmaps/RoadmapsPageHeader';
|
||||||
|
import GridItem from '../components/GridItem.astro';
|
||||||
|
import SimplePageHeader from '../components/SimplePageHeader.astro';
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
|
import { getRoadmapsByTag } from '../lib/roadmap';
|
||||||
import ChangelogBanner from '../components/ChangelogBanner.astro';
|
import ChangelogBanner from '../components/ChangelogBanner.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
import { FetchError, httpGet } from '../lib/query-http';
|
import { FetchError, httpGet } from '../lib/query-http';
|
||||||
import type { Node, Edge } from '@roadmapsh/editor';
|
import type { Node, Edge } from '@roadmapsh/editor';
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
|
|
||||||
export const allowedOfficialRoadmapType = ['skill', 'role'] as const;
|
export const allowedOfficialRoadmapType = ['skill', 'role'] as const;
|
||||||
export type AllowedOfficialRoadmapType =
|
export type AllowedOfficialRoadmapType =
|
||||||
@@ -104,11 +103,3 @@ export async function listOfficialRoadmaps() {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNewRoadmap(createdAt: Date) {
|
|
||||||
return (
|
|
||||||
createdAt &&
|
|
||||||
DateTime.now().diff(DateTime.fromJSDate(new Date(createdAt)), 'days').days <
|
|
||||||
45
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user