parent
37ffc2cc62
commit
d958a29862
49 changed files with 381 additions and 300 deletions
@ -0,0 +1,11 @@ |
|||||||
|
--- |
||||||
|
name: 'Fernando Doglio' |
||||||
|
imageUrl: '/authors/fernando.jpeg' |
||||||
|
social: |
||||||
|
twitter: 'https://twitter.com/deleteman123' |
||||||
|
linkedin: 'https://www.linkedin.com/in/fernandodoglio' |
||||||
|
--- |
||||||
|
|
||||||
|
With two decades of experience in Software Development, Fernando Doglio excels in diverse languages like Ruby, Perl, PHP, Python, and JavaScript. He's led teams in crafting scalable architectures for both in-house and cloud infrastructures. |
||||||
|
|
||||||
|
An author of 8 technical books and over 250 articles, Fernando's current role as a Dev Advocate allows him to blend his passion for coding with content creation, enhancing developer experiences with products through engaging outreach. |
@ -0,0 +1,13 @@ |
|||||||
|
--- |
||||||
|
name: 'Kamran Ahmed' |
||||||
|
imageUrl: '/authors/kamran.jpeg' |
||||||
|
social: |
||||||
|
linkedin: 'https://www.linkedin.com/in/kamrify' |
||||||
|
twitter: 'https://twitter.com/kamrify' |
||||||
|
github: 'https://github.com/kamranahmedse' |
||||||
|
website: 'https://kamranahmed.info' |
||||||
|
--- |
||||||
|
|
||||||
|
Kamran is the founder of **roadmap.sh**. He has a decade long experience working mostly with startups and scale-ups. Over the years, he has worked with a variety of technologies in a variety of domains and have worn several different hats. He is working full time on roadmap.sh at the moment. |
||||||
|
|
||||||
|
He is also a Google Developer Expert and a GitHub Star. He is a huge proponent of open-source, and has authored several popular open-source projects. He is [the second most starred developer](https://twitter.com/kamrify/status/1750345095587754382) on GitHub globally. |
Before Width: | Height: | Size: 941 B After Width: | Height: | Size: 946 B |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 611 B |
@ -0,0 +1,73 @@ |
|||||||
|
import type { MarkdownFileType } from './file'; |
||||||
|
|
||||||
|
export interface AuthorFrontmatter { |
||||||
|
name: string; |
||||||
|
imageUrl: string; |
||||||
|
social: { |
||||||
|
twitter: string; |
||||||
|
github: string; |
||||||
|
linkedin: string; |
||||||
|
website: string; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export type AuthorFileType = MarkdownFileType<AuthorFrontmatter> & { |
||||||
|
id: string; |
||||||
|
}; |
||||||
|
|
||||||
|
function authorPathToId(filePath: string): string { |
||||||
|
const fileName = filePath.split('/').pop() || ''; |
||||||
|
|
||||||
|
return fileName.replace('.md', ''); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the IDs of all the authors available on the website |
||||||
|
* |
||||||
|
* @returns string[] Array of author IDs |
||||||
|
*/ |
||||||
|
export async function getAuthorIds() { |
||||||
|
const authorFiles = import.meta.glob<AuthorFileType>( |
||||||
|
'/src/data/authors/*.md', |
||||||
|
{ |
||||||
|
eager: true, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
console.log(Object.keys(authorFiles)); |
||||||
|
return Object.keys(authorFiles).map(authorPathToId); |
||||||
|
} |
||||||
|
|
||||||
|
export async function getAllAuthors(): Promise<AuthorFileType[]> { |
||||||
|
const authorFilesMap: Record<string, AuthorFileType> = |
||||||
|
import.meta.glob<AuthorFileType>('/src/data/authors/*.md', { |
||||||
|
eager: true, |
||||||
|
}); |
||||||
|
|
||||||
|
const authorFiles = Object.values(authorFilesMap); |
||||||
|
|
||||||
|
return authorFiles.map((authorFile) => ({ |
||||||
|
...authorFile, |
||||||
|
id: authorPathToId(authorFile.file), |
||||||
|
})); |
||||||
|
} |
||||||
|
|
||||||
|
export async function getAuthorById(id: string): Promise<AuthorFileType> { |
||||||
|
const authorFilesMap: Record<string, AuthorFileType> = |
||||||
|
import.meta.glob<AuthorFileType>('/src/data/authors/*.md', { |
||||||
|
eager: true, |
||||||
|
}); |
||||||
|
|
||||||
|
const authorFile = Object.values(authorFilesMap).find((authorFile) => { |
||||||
|
return authorPathToId(authorFile.file) === id; |
||||||
|
}); |
||||||
|
|
||||||
|
if (!authorFile) { |
||||||
|
throw new Error(`Author with ID ${id} not found`); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
...authorFile, |
||||||
|
id: authorPathToId(authorFile.file), |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,110 @@ |
|||||||
|
--- |
||||||
|
import BaseLayout from '../../layouts/BaseLayout.astro'; |
||||||
|
import AstroIcon from '../../components/AstroIcon.astro'; |
||||||
|
import { getGuidesByAuthor } from '../../lib/guide'; |
||||||
|
import { getAllVideos } from '../../lib/video'; |
||||||
|
import GuideListItem from '../../components/GuideListItem.astro'; |
||||||
|
import { getAuthorById, getAuthorIds } from '../../lib/author'; |
||||||
|
|
||||||
|
interface Params extends Record<string, string | undefined> {} |
||||||
|
|
||||||
|
export async function getStaticPaths() { |
||||||
|
const authorIds = await getAuthorIds(); |
||||||
|
|
||||||
|
return authorIds.map((authorId) => ({ |
||||||
|
params: { authorId }, |
||||||
|
})); |
||||||
|
} |
||||||
|
|
||||||
|
const { authorId } = Astro.params; |
||||||
|
|
||||||
|
const author = await getAuthorById(authorId); |
||||||
|
|
||||||
|
const guides = await getGuidesByAuthor(authorId); |
||||||
|
const videos = await getAllVideos(); |
||||||
|
--- |
||||||
|
|
||||||
|
<BaseLayout |
||||||
|
permalink={`/author/${author.id}`} |
||||||
|
title={`${author.frontmatter.name} - Author at roadmap.sh`} |
||||||
|
briefTitle={author.frontmatter.name} |
||||||
|
ogImageUrl={`https://roadmap.sh/${author.frontmatter.imageUrl}`} |
||||||
|
description={`${author.frontmatter.name} has written ${guides.length} articles on roadmap.sh on a variety of topics.`} |
||||||
|
noIndex={false} |
||||||
|
> |
||||||
|
<div class='container pt-4 pb-0 md:pb-16 md:pt-8'> |
||||||
|
<div class=''> |
||||||
|
<div class='mb-5 flex items-center gap-8 rounded-3xl py-0 md:py-8'> |
||||||
|
<div> |
||||||
|
<h1 class='text-2xl md:text-3xl font-bold'>{author.frontmatter.name}</h1> |
||||||
|
<div |
||||||
|
class='mt-1 mb-4 md:mt-4 md:mb-6 flex flex-col gap-3 text-gray-800 [&>p>a]:font-semibold [&>p>a]:underline leading-normal' |
||||||
|
> |
||||||
|
<author.Content /> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class='flex items-center justify-between'> |
||||||
|
<div class='flex items-center gap-1.5'> |
||||||
|
{ |
||||||
|
author.frontmatter.social?.github && ( |
||||||
|
<a |
||||||
|
href={author.frontmatter.social.github} |
||||||
|
target='_blank' |
||||||
|
class='text-gray-500 transition-colors hover:text-gray-800' |
||||||
|
> |
||||||
|
<AstroIcon icon='github' class='h-[20px]' /> |
||||||
|
</a> |
||||||
|
) |
||||||
|
} |
||||||
|
{ |
||||||
|
author.frontmatter.social.twitter && ( |
||||||
|
<a |
||||||
|
href={author.frontmatter.social.twitter} |
||||||
|
target='_blank' |
||||||
|
class='text-gray-500 transition-colors hover:text-gray-800' |
||||||
|
> |
||||||
|
<AstroIcon icon='twitter' class='h-[20px]' /> |
||||||
|
</a> |
||||||
|
) |
||||||
|
} |
||||||
|
{ |
||||||
|
author.frontmatter.social.linkedin && ( |
||||||
|
<a |
||||||
|
href={author.frontmatter.social.linkedin} |
||||||
|
target='_blank' |
||||||
|
class='text-gray-500 transition-colors hover:text-gray-800' |
||||||
|
> |
||||||
|
<AstroIcon icon='linkedin-2' class='h-[20px]' /> |
||||||
|
</a> |
||||||
|
) |
||||||
|
} |
||||||
|
{ |
||||||
|
author.frontmatter.social.website && ( |
||||||
|
<a |
||||||
|
href={author.frontmatter.social.website} |
||||||
|
target='_blank' |
||||||
|
class='text-gray-500 transition-colors hover:text-gray-800' |
||||||
|
> |
||||||
|
<AstroIcon icon='globe' class='h-[20px]' /> |
||||||
|
</a> |
||||||
|
) |
||||||
|
} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class='hidden flex-shrink-0 flex-col md:flex'> |
||||||
|
<img |
||||||
|
alt="Kamran Ahmed's profile picture" |
||||||
|
class='block h-[175px] w-[175px] rounded-full bg-gray-100' |
||||||
|
src={author.frontmatter.imageUrl} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div |
||||||
|
class='rounded-t-xl bg-gradient-to-b from-gray-100 to-white px-3 py-2 md:px-6 md:py-3 [&>*:last-child]:border-b-0' |
||||||
|
> |
||||||
|
{guides.map((guide) => <GuideListItem guide={guide} />)} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</BaseLayout> |
Loading…
Reference in new issue