mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-01 05:21:43 +02:00
Add best practices page
This commit is contained in:
@@ -4,6 +4,8 @@ pdfUrl: "/pdfs/best-practices/frontend-performance.pdf"
|
||||
order: 1
|
||||
featuredTitle: "Frontend Performance"
|
||||
featuredDescription: "Detailed list of best practices to improve your frontend performance"
|
||||
isNew: true
|
||||
isUpcoming: false
|
||||
title: "Frontend Performance"
|
||||
description: "Detailed list of best practices to improve your frontend performance"
|
||||
dimensions:
|
||||
|
@@ -9,11 +9,9 @@ export interface Props {
|
||||
const { featuredItems, heading } = Astro.props;
|
||||
---
|
||||
|
||||
<div class='py-4 sm:py-14 border-b border-b-slate-900 relative'>
|
||||
<div class='py-4 sm:py-14 border-b border-b-[#1e293c] relative'>
|
||||
<div class='container'>
|
||||
<h2
|
||||
class='hidden sm:flex absolute rounded-lg -top-[17px] left-1/2 -translate-x-1/2 bg-slate-900 py-1 px-3 border border-slate-900 text-md text-slate-400 font-regular'
|
||||
>
|
||||
<h2 class='hidden sm:flex absolute rounded-lg -top-[17px] left-1/2 -translate-x-1/2 bg-slate-900 py-1 px-3 border border-[#1e293c] text-md text-slate-400 font-regular'>
|
||||
{heading}
|
||||
</h2>
|
||||
|
||||
|
32
src/components/GridItem.astro
Normal file
32
src/components/GridItem.astro
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
import type { RoadmapFileType } from '../lib/roadmap';
|
||||
|
||||
export interface Props {
|
||||
url: string;
|
||||
title: string;
|
||||
description: string;
|
||||
isNew: boolean;
|
||||
}
|
||||
|
||||
const { url, title, description, isNew } = Astro.props;
|
||||
---
|
||||
|
||||
<a
|
||||
href={url}
|
||||
class='bg-gradient-to-r from-slate-900 to-amber-900 hover:from-stone-900 hover:to-stone-900 hover:bg-gray-100 flex flex-col p-2.5 sm:p-5 rounded-md sm:rounded-lg border border-gray-200 relative h-full'
|
||||
>
|
||||
<span
|
||||
class='font-regular sm:font-medium text-md sm:text-xl hover:text-gray-50 text-gray-200 sm:text-gray-100 mb-0 sm:mb-1.5'
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
<span class='text-sm leading-normal text-gray-400 hidden sm:block'>{description}</span>
|
||||
|
||||
{
|
||||
isNew && (
|
||||
<span class='absolute bottom-1 right-1 bg-yellow-300 text-yellow-900 text-xs font-medium px-1 sm:px-1.5 py-0.5 rounded-sm uppercase'>
|
||||
New
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</a>
|
@@ -1,31 +0,0 @@
|
||||
---
|
||||
import type { RoadmapFileType } from '../lib/roadmap';
|
||||
|
||||
export interface Props {
|
||||
roadmap: RoadmapFileType;
|
||||
}
|
||||
|
||||
const { roadmap } = Astro.props;
|
||||
const frontmatter = roadmap.frontmatter;
|
||||
---
|
||||
|
||||
<a
|
||||
href={`/${roadmap.id}`}
|
||||
class="bg-gradient-to-r from-slate-900 to-amber-900 hover:from-stone-900 hover:to-stone-900 hover:bg-gray-100 flex flex-col p-2.5 sm:p-5 rounded-md sm:rounded-lg border border-gray-200 relative h-full"
|
||||
>
|
||||
<span
|
||||
class="font-regular sm:font-medium text-md sm:text-xl hover:text-gray-50 text-gray-200 sm:text-gray-100 mb-0 sm:mb-1.5"
|
||||
>{frontmatter.title}</span
|
||||
>
|
||||
<span class="text-sm leading-normal text-gray-400 hidden sm:block"
|
||||
>{frontmatter.description}</span
|
||||
>
|
||||
|
||||
{
|
||||
frontmatter.isNew && (
|
||||
<span class="absolute bottom-1 right-1 bg-yellow-300 text-yellow-900 text-xs font-medium px-1 sm:px-1.5 py-0.5 rounded-sm uppercase">
|
||||
New
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</a>
|
@@ -14,6 +14,9 @@ import Icon from './Icon.astro';
|
||||
<li>
|
||||
<a href='/roadmaps' class='text-gray-400 hover:text-white'>Roadmaps</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='/best-practices' class='text-gray-400 hover:text-white'>Best Practices</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='/guides' class='text-gray-400 hover:text-white'>Guides</a>
|
||||
</li>
|
||||
@@ -48,6 +51,9 @@ import Icon from './Icon.astro';
|
||||
<li>
|
||||
<a href='/roadmaps' class='text-xl md:text-lg hover:text-blue-300'>Roadmaps</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='/best-practices' class='text-xl md:text-lg hover:text-blue-300'>Best Practices</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='/guides' class='text-xl md:text-lg hover:text-blue-300'>Guides</a>
|
||||
</li>
|
||||
|
74
src/lib/best-pratice.ts
Normal file
74
src/lib/best-pratice.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import type { MarkdownFileType } from './file';
|
||||
import type { SponsorType } from '../components/Sponsor/Sponsor.astro';
|
||||
|
||||
export interface BestPracticeFrontmatter {
|
||||
jsonUrl: string;
|
||||
pdfUrl: string;
|
||||
order: number;
|
||||
featuredTitle: string;
|
||||
featuredDescription: string;
|
||||
title: string;
|
||||
description: string;
|
||||
isNew: boolean;
|
||||
isUpcoming: boolean;
|
||||
dimensions?: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
sponsor?: SponsorType;
|
||||
seo: {
|
||||
title: string;
|
||||
description: string;
|
||||
keywords: string[];
|
||||
};
|
||||
schema?: {
|
||||
headline: string;
|
||||
description: string;
|
||||
datePublished: string;
|
||||
dateModified: string;
|
||||
imageUrl: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type BestPracticeFileType = MarkdownFileType<BestPracticeFrontmatter> & {
|
||||
id: string;
|
||||
};
|
||||
|
||||
function bestPracticePathToId(filePath: string): string {
|
||||
const fileName = filePath.split('/').pop() || '';
|
||||
|
||||
return fileName.replace('.md', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the IDs of all the best practices available on the website
|
||||
*
|
||||
* @returns string[] Array of best practices file IDs
|
||||
*/
|
||||
export async function getBestPracticeIds() {
|
||||
const bestPracticeFiles = await import.meta.glob<BestPracticeFileType>('/src/best-practices/*/*.md', {
|
||||
eager: true,
|
||||
});
|
||||
|
||||
return Object.keys(bestPracticeFiles).map(bestPracticePathToId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the best practice files
|
||||
*
|
||||
* @param tag Tag assigned to best practice
|
||||
* @returns Promisified BestPracticeFileType[]
|
||||
*/
|
||||
export async function getAllBestPractices(): Promise<BestPracticeFileType[]> {
|
||||
const bestPracticeFilesMap = await import.meta.glob<BestPracticeFileType>('/src/best-practices/*/*.md', {
|
||||
eager: true,
|
||||
});
|
||||
|
||||
const bestPracticeFiles = Object.values(bestPracticeFilesMap);
|
||||
const bestPracticeItems = bestPracticeFiles.map((bestPracticeFile) => ({
|
||||
...bestPracticeFile,
|
||||
id: bestPracticePathToId(bestPracticeFile.file),
|
||||
}));
|
||||
|
||||
return bestPracticeItems.sort((a, b) => a.frontmatter.order - b.frontmatter.order);
|
||||
}
|
37
src/pages/best-practices/index.astro
Normal file
37
src/pages/best-practices/index.astro
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
import GridItem from '../../components/GridItem.astro';
|
||||
import SimplePageHeader from '../../components/SimplePageHeader.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { getAllBestPractices } from '../../lib/best-pratice';
|
||||
|
||||
const bestPractices = await getAllBestPractices();
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title='Best Practices'
|
||||
description={'Step by step guides and paths to learn different tools or technologies'}
|
||||
permalink={'/best-practices'}
|
||||
>
|
||||
<SimplePageHeader
|
||||
title='Best Practices'
|
||||
description='Step by step guides and paths to learn different tools or technologies'
|
||||
showYouTubeAlert={true}
|
||||
/>
|
||||
|
||||
<div class='bg-gray-100 pt-4 pb-14 sm:pt-8 sm:pb-16'>
|
||||
<div class='container'>
|
||||
<div class='grid grid-cols-1 sm:grid-cols-2 gap-0.5 sm:gap-3'>
|
||||
{
|
||||
bestPractices.map((bestPractice) => (
|
||||
<GridItem
|
||||
url={`/best-practices/${bestPractice.id}`}
|
||||
isNew={bestPractice.frontmatter.isNew}
|
||||
title={bestPractice.frontmatter.title}
|
||||
description={bestPractice.frontmatter.description}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
@@ -3,12 +3,14 @@ import FeaturedGuides from '../components/FeaturedGuides.astro';
|
||||
import FeaturedItems from '../components/FeaturedItems/FeaturedItems.astro';
|
||||
import FeaturedVideos from '../components/FeaturedVideos.astro';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { getAllBestPractices } from '../lib/best-pratice';
|
||||
import { getAllGuides } from '../lib/guide';
|
||||
import { getRoadmapsByTag } from '../lib/roadmap';
|
||||
import { getAllVideos } from '../lib/video';
|
||||
|
||||
const roleRoadmaps = await getRoadmapsByTag('role-roadmap');
|
||||
const skillRoadmaps = await getRoadmapsByTag('skill-roadmap');
|
||||
const bestPractices = await getAllBestPractices();
|
||||
|
||||
const guides = await getAllGuides();
|
||||
const videos = await getAllVideos();
|
||||
@@ -20,7 +22,7 @@ const videos = await getAllVideos();
|
||||
permalink={'/'}
|
||||
>
|
||||
<div class='bg-gradient-to-b from-slate-900 to-black'>
|
||||
<div class='border-b border-b-slate-900'>
|
||||
<div class='border-b border-b-[#1e293c]'>
|
||||
<div class='container text-left sm:text-center py-6 sm:py-20 px-6 sm:px-0'>
|
||||
<h1
|
||||
class='text-2xl sm:text-5xl mb-2 sm:mb-4 font-bold bg-gradient-to-b from-amber-50 to-purple-500 text-transparent bg-clip-text'
|
||||
@@ -59,6 +61,16 @@ const videos = await getAllVideos();
|
||||
}))}
|
||||
/>
|
||||
|
||||
<FeaturedItems
|
||||
heading='Best Practices'
|
||||
featuredItems={bestPractices.map((bestPractice) => ({
|
||||
text: bestPractice.frontmatter.featuredTitle,
|
||||
url: `/best-practices/${bestPractice.id}`,
|
||||
isNew: bestPractice.frontmatter.isNew,
|
||||
isUpcoming: bestPractice.frontmatter.isUpcoming,
|
||||
}))}
|
||||
/>
|
||||
|
||||
<div class='grid grid-cols-1 gap-7 sm:gap-16 bg-gray-50 py-7 sm:py-16'>
|
||||
<FeaturedGuides heading='Guides' guides={guides.slice(0, 7)} />
|
||||
<FeaturedVideos heading='Videos' videos={videos.slice(0, 7)} />
|
||||
|
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import GridRoadmapItem from '../components/GridRoadmapItem.astro';
|
||||
import GridItem from '../components/GridItem.astro';
|
||||
import SimplePageHeader from '../components/SimplePageHeader.astro';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { getRoadmapsByTag } from '../lib/roadmap';
|
||||
@@ -24,13 +24,23 @@ const skillRoadmaps = await getRoadmapsByTag('skill-roadmap');
|
||||
<div class='grid grid-cols-1 sm:grid-cols-2 gap-0.5 sm:gap-3'>
|
||||
{
|
||||
roleRoadmaps.map((roleRoadmap) => (
|
||||
<GridRoadmapItem roadmap={roleRoadmap} />
|
||||
<GridItem
|
||||
url={`/${roleRoadmap.id}`}
|
||||
isNew={roleRoadmap.frontmatter.isNew}
|
||||
title={roleRoadmap.frontmatter.title}
|
||||
description={roleRoadmap.frontmatter.description}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
||||
{
|
||||
skillRoadmaps.map((skillRoadmap) => (
|
||||
<GridRoadmapItem roadmap={skillRoadmap} />
|
||||
<GridItem
|
||||
url={`/${skillRoadmap.id}`}
|
||||
isNew={skillRoadmap.frontmatter.isNew}
|
||||
title={skillRoadmap.frontmatter.title}
|
||||
description={skillRoadmap.frontmatter.description}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user