mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-30 12:40:03 +02:00
Guides listing page
This commit is contained in:
40
src/components/GuideListItem.astro
Normal file
40
src/components/GuideListItem.astro
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
import type { GuideFileType } from "../lib/guide";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
guide: GuideFileType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { guide } = Astro.props;
|
||||||
|
const { frontmatter, id } = guide;
|
||||||
|
---
|
||||||
|
|
||||||
|
<a
|
||||||
|
class:list={[
|
||||||
|
"block no-underline py-2 group text-md items-center text-gray-600 hover:text-blue-600 flex justify-between border-b",
|
||||||
|
]}
|
||||||
|
href={`/guides/${id}`}
|
||||||
|
>
|
||||||
|
<span class="group-hover:translate-x-2 transition-transform">
|
||||||
|
{frontmatter.title}
|
||||||
|
|
||||||
|
{
|
||||||
|
frontmatter.isNew && (
|
||||||
|
<span class="bg-green-300 text-green-900 text-xs font-medium px-1.5 py-0.5 rounded-sm uppercase ml-1.5">
|
||||||
|
New
|
||||||
|
<span class="hidden sm:inline">
|
||||||
|
·
|
||||||
|
{new Date(frontmatter.date).toLocaleString("default", {
|
||||||
|
month: "long",
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
<span class="capitalize text-gray-500 text-xs hidden sm:block">
|
||||||
|
{frontmatter.type}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="text-gray-400 text-xs block sm:hidden"> »</span>
|
||||||
|
</a>
|
59
src/lib/guide.ts
Normal file
59
src/lib/guide.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import type { MarkdownFileType } from "./file";
|
||||||
|
|
||||||
|
export interface GuideFrontmatter {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
author: {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
imageUrl: string;
|
||||||
|
};
|
||||||
|
seo: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
isNew: boolean;
|
||||||
|
type: "visual" | "textual";
|
||||||
|
date: string;
|
||||||
|
sitemap: {
|
||||||
|
priority: number;
|
||||||
|
changefreq: "daily" | "weekly" | "monthly" | "yealry";
|
||||||
|
};
|
||||||
|
tags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GuideFileType = MarkdownFileType<GuideFrontmatter> & {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates id from the given guide file
|
||||||
|
* @param filePath Markdown file path
|
||||||
|
*
|
||||||
|
* @returns unique guide identifier
|
||||||
|
*/
|
||||||
|
function guidePathToId(filePath: string): string {
|
||||||
|
return filePath.replace("/src/guides/", "").replace(".md", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all the guides sorted by the publishing date
|
||||||
|
* @returns Promisifed guide files
|
||||||
|
*/
|
||||||
|
export async function getAllGuides(): Promise<GuideFileType[]> {
|
||||||
|
const guides = await import.meta.glob<GuideFileType>("/src/guides/*.md", {
|
||||||
|
eager: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const guideFiles = Object.values(guides);
|
||||||
|
const enrichedGuides = guideFiles.map((guideFile) => ({
|
||||||
|
...guideFile,
|
||||||
|
id: guidePathToId(guideFile.file),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return enrichedGuides.sort(
|
||||||
|
(a, b) =>
|
||||||
|
new Date(b.frontmatter.date).valueOf() -
|
||||||
|
new Date(a.frontmatter.date).valueOf()
|
||||||
|
);
|
||||||
|
}
|
@@ -1,8 +1,5 @@
|
|||||||
import type { MarkdownFileType } from "./File";
|
import type { MarkdownFileType } from "./file";
|
||||||
|
|
||||||
export type RoadmapFileType = MarkdownFileType<RoadmapFrontmatter> & {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface RoadmapFrontmatter {
|
export interface RoadmapFrontmatter {
|
||||||
jsonUrl: string;
|
jsonUrl: string;
|
||||||
@@ -32,6 +29,10 @@ export interface RoadmapFrontmatter {
|
|||||||
tags: string[];
|
tags: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RoadmapFileType = MarkdownFileType<RoadmapFrontmatter> & {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
function roadmapPathToId(filePath: string):string {
|
function roadmapPathToId(filePath: string):string {
|
||||||
const fileName = filePath.split("/").pop() || "";
|
const fileName = filePath.split("/").pop() || "";
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ function roadmapPathToId(filePath: string):string {
|
|||||||
* @returns string[] Array of roadmap IDs
|
* @returns string[] Array of roadmap IDs
|
||||||
*/
|
*/
|
||||||
export async function getRoadmapIds() {
|
export async function getRoadmapIds() {
|
||||||
const roadmapFiles = await import.meta.glob<string>("/src/roadmaps/*/*.md", {
|
const roadmapFiles = await import.meta.glob<RoadmapFileType>("/src/roadmaps/*/*.md", {
|
||||||
eager: true,
|
eager: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import type { MarkdownFileType } from './File';
|
import type { MarkdownFileType } from './file';
|
||||||
import type { RoadmapFrontmatter } from './roadmap';
|
import type { RoadmapFrontmatter } from './roadmap';
|
||||||
|
|
||||||
// Generates URL from the topic file path e.g.
|
// Generates URL from the topic file path e.g.
|
||||||
|
23
src/pages/guides.astro
Normal file
23
src/pages/guides.astro
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
import GuideListItem from "../components/GuideListItem.astro";
|
||||||
|
import SimplePageHeader from "../components/SimplePageHeader.astro";
|
||||||
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
import { getAllGuides } from "../lib/guide";
|
||||||
|
|
||||||
|
const guides = await getAllGuides();
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout title="Guides">
|
||||||
|
<SimplePageHeader
|
||||||
|
title="Guides"
|
||||||
|
description="Succinct graphical explanations to engineering topics."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="pb-20 pt-2 bg-gray-50">
|
||||||
|
<div class="container">
|
||||||
|
<div class="mt-3 sm:my-5">
|
||||||
|
{guides.map((guide) => <GuideListItem guide={guide} />)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
Reference in New Issue
Block a user