From c4c28944ee9ce0faf15644cda77eb6a4a321b422 Mon Sep 17 00:00:00 2001 From: Arik Chakma Date: Thu, 28 Aug 2025 13:36:23 +0600 Subject: [PATCH] chore: replace roadmap listing --- .../EditorRoadmap/EditorRoadmap.tsx | 7 +- src/components/GridItem.astro | 4 +- src/lib/project.ts | 9 +- src/lib/roadmap.ts | 105 ------------------ src/pages/404.astro | 27 +++-- src/pages/[roadmapId]/courses.astro | 4 +- src/pages/[roadmapId]/projects.astro | 37 +++--- src/pages/[roadmapId]/svg.astro | 52 ++++----- src/pages/dashboard.astro | 77 ++++++------- src/pages/index.astro | 50 +++++---- src/pages/pages.json.ts | 58 +++++----- src/pages/projects/index.astro | 16 ++- src/pages/roadmaps.astro | 3 - src/queries/official-roadmap.ts | 9 ++ 14 files changed, 191 insertions(+), 267 deletions(-) diff --git a/src/components/EditorRoadmap/EditorRoadmap.tsx b/src/components/EditorRoadmap/EditorRoadmap.tsx index 997a78362..dc5defaf0 100644 --- a/src/components/EditorRoadmap/EditorRoadmap.tsx +++ b/src/components/EditorRoadmap/EditorRoadmap.tsx @@ -23,7 +23,12 @@ type EditorRoadmapProps = { }; export function EditorRoadmap(props: EditorRoadmapProps) { - const { resourceId, resourceType = 'roadmap', dimensions, hasChat = true } = props; + const { + resourceId, + resourceType = 'roadmap', + dimensions, + hasChat = true, + } = props; const [hasSwitchedRoadmap, setHasSwitchedRoadmap] = useState(false); const [isLoading, setIsLoading] = useState(true); diff --git a/src/components/GridItem.astro b/src/components/GridItem.astro index 2b80746b4..7a327cf1c 100644 --- a/src/components/GridItem.astro +++ b/src/components/GridItem.astro @@ -1,6 +1,4 @@ --- -import type { RoadmapFileType } from '../lib/roadmap'; - export interface Props { url: string; title: string; @@ -27,7 +25,7 @@ const { url, title, description, isNew } = Astro.props; { isNew && ( - + diff --git a/src/lib/project.ts b/src/lib/project.ts index 41cef4841..1a16aea66 100644 --- a/src/lib/project.ts +++ b/src/lib/project.ts @@ -1,5 +1,8 @@ +import { + officialRoadmapDetails, + type OfficialRoadmapDocument, +} from '../queries/official-roadmap'; import type { MarkdownFileType } from './file'; -import { getRoadmapById, type RoadmapFileType } from './roadmap'; export const projectDifficulties = [ 'beginner', @@ -28,7 +31,7 @@ export interface ProjectFrontmatter { export type ProjectFileType = MarkdownFileType & { id: string; - roadmaps: RoadmapFileType[]; + roadmaps: OfficialRoadmapDocument[]; }; /** @@ -85,7 +88,7 @@ export async function getProjectById( const project = await import(`../data/projects/${groupId}.md`); const roadmapIds = project.frontmatter.roadmapIds || []; const roadmaps = await Promise.all( - roadmapIds.map((roadmapId: string) => getRoadmapById(roadmapId)), + roadmapIds.map((roadmapId: string) => officialRoadmapDetails(roadmapId)), ); return { diff --git a/src/lib/roadmap.ts b/src/lib/roadmap.ts index c83d9c41c..0beb7a18f 100644 --- a/src/lib/roadmap.ts +++ b/src/lib/roadmap.ts @@ -1,19 +1,7 @@ import type { PageType } from '../components/CommandMenu/CommandMenu'; -import type { MarkdownFileType } from './file'; import { httpGet } from './http'; 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 interface RoadmapFrontmatter { @@ -76,99 +64,6 @@ export interface RoadmapFrontmatter { renderer?: AllowedRoadmapRenderer; } -export type RoadmapFileType = MarkdownFileType & { - 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( - '/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 { - const roadmapFilesMap = import.meta.glob( - '/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 { - const roadmapFilesMap: Record = - import.meta.glob('/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 { - if (!ids?.length) { - return []; - } - - return Promise.all(ids.map((id) => getRoadmapById(id))); -} - -export async function getRoadmapFaqsById(roadmapId: string): Promise { - const { faqs } = await import( - `../data/roadmaps/${roadmapId}/faqs.astro` - ).catch(() => ({})); - - return faqs || []; -} - export async function getResourceMeta( resourceType: ResourceType, resourceId: string, diff --git a/src/pages/404.astro b/src/pages/404.astro index 9e4261b86..553778cce 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -1,10 +1,13 @@ --- import Icon from '../components/AstroIcon.astro'; import BaseLayout from '../layouts/BaseLayout.astro'; -import { getRoadmapIds } from '../lib/roadmap'; +import { listOfficialRoadmaps } from '../queries/official-roadmap'; -const roadmapIds = await getRoadmapIds(); -const legacyRoadmapUrls = [...roadmapIds.map((id) => `/${id}/`), '/roadmaps/']; +const roadmapIds = await listOfficialRoadmaps(); +const legacyRoadmapUrls = [ + ...roadmapIds.map((roadmap) => `/${roadmap.slug}/`), + '/roadmaps/', +]; --- @@ -18,20 +21,26 @@ const legacyRoadmapUrls = [...roadmapIds.map((id) => `/${id}/`), '/roadmaps/'];
-
+
diff --git a/src/pages/[roadmapId]/courses.astro b/src/pages/[roadmapId]/courses.astro index 001f62eb7..17b4514a4 100644 --- a/src/pages/[roadmapId]/courses.astro +++ b/src/pages/[roadmapId]/courses.astro @@ -1,4 +1,4 @@ ---- + diff --git a/src/pages/[roadmapId]/projects.astro b/src/pages/[roadmapId]/projects.astro index bde9f23e2..8c00615e6 100644 --- a/src/pages/[roadmapId]/projects.astro +++ b/src/pages/[roadmapId]/projects.astro @@ -5,16 +5,19 @@ import { ProjectsList } from '../../components/Projects/ProjectsList'; import BaseLayout from '../../layouts/BaseLayout.astro'; import { getProjectsByRoadmapId } from '../../lib/project'; import { getOpenGraphImageUrl } from '../../lib/open-graph'; -import { type RoadmapFrontmatter, getRoadmapIds } from '../../lib/roadmap'; import { projectApi } from '../../api/project'; +import { + listOfficialRoadmaps, + officialRoadmapDetails, +} from '../../queries/official-roadmap'; export const prerender = true; export async function getStaticPaths() { - const roadmapIds = await getRoadmapIds(); + const roadmapIds = await listOfficialRoadmaps(); - return roadmapIds.map((roadmapId) => ({ - params: { roadmapId }, + return roadmapIds.map((roadmap) => ({ + params: { roadmapId: roadmap.slug }, })); } @@ -23,15 +26,14 @@ interface Params extends Record { } const { roadmapId } = Astro.params as Params; -const roadmapFile = await import( - `../../data/roadmaps/${roadmapId}/${roadmapId}.md` -); - -const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter; +const roadmapData = await officialRoadmapDetails(roadmapId); +if (!roadmapData) { + return Astro.rewrite('/404'); +} // update og for projects const ogImageUrl = - roadmapData?.seo?.ogImageUrl || + roadmapData?.openGraph?.image || getOpenGraphImageUrl({ group: 'roadmap', resourceId: roadmapId, @@ -44,13 +46,13 @@ const descriptionNoun: Record = { 'Product Manager': 'Product Management', }; -const title = `${roadmapData.briefTitle} Projects`; -const description = `Project ideas to take you from beginner to advanced in ${descriptionNoun[roadmapData.briefTitle] || roadmapData.briefTitle}`; +const title = `${roadmapData.title.card} Projects`; +const description = `Project ideas to take you from beginner to advanced in ${descriptionNoun[roadmapData.title.card] || roadmapData.title.card}`; // `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.briefTitle} Projects`; +const seoTitle = `${roadmapData.title.card} Projects`; const nounTitle = - descriptionNoun[roadmapData?.briefTitle] || roadmapData.briefTitle; + descriptionNoun[roadmapData?.title.card] || roadmapData.title.card; 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); @@ -65,7 +67,7 @@ const { response: userCounts } = permalink={`/${roadmapId}/projects`} title={seoTitle} description={seoDescription} - briefTitle={roadmapData.briefTitle} + briefTitle={roadmapData.title.card} ogImageUrl={ogImageUrl} keywords={roadmapData.seo.keywords} noIndex={projects.length === 0} @@ -73,15 +75,16 @@ const { response: userCounts } = resourceType='roadmap' >
+ diff --git a/src/pages/[roadmapId]/svg.astro b/src/pages/[roadmapId]/svg.astro index 5b508c215..d353d723e 100644 --- a/src/pages/[roadmapId]/svg.astro +++ b/src/pages/[roadmapId]/svg.astro @@ -1,17 +1,19 @@ --- import { EditorRoadmap } from '../../components/EditorRoadmap/EditorRoadmap'; -import FrameRenderer from '../../components/FrameRenderer/FrameRenderer.astro'; import SkeletonLayout from '../../layouts/SkeletonLayout.astro'; import { getOpenGraphImageUrl } from '../../lib/open-graph'; -import { type RoadmapFrontmatter, getRoadmapIds } from '../../lib/roadmap'; +import { + listOfficialRoadmaps, + officialRoadmapDetails, +} from '../../queries/official-roadmap'; export const prerender = true; export async function getStaticPaths() { - const roadmapIds = await getRoadmapIds(); + const roadmapIds = await listOfficialRoadmaps(); - return roadmapIds.map((roadmapId) => ({ - params: { roadmapId }, + return roadmapIds.map((roadmap) => ({ + params: { roadmapId: roadmap.slug }, })); } @@ -20,13 +22,13 @@ interface Params extends Record { } const { roadmapId } = Astro.params as Params; -const roadmapFile = await import( - `../../data/roadmaps/${roadmapId}/${roadmapId}.md` -); -const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter; +const roadmapData = await officialRoadmapDetails(roadmapId); +if (!roadmapData) { + return Astro.rewrite('/404'); +} const ogImageUrl = - roadmapData?.seo?.ogImageUrl || + roadmapData?.openGraph?.image || getOpenGraphImageUrl({ group: 'roadmap', resourceId: roadmapId, @@ -35,8 +37,8 @@ const ogImageUrl = -
- { - roadmapData?.renderer === 'editor' ? ( - - ) : ( - - ) - } +
+
diff --git a/src/pages/dashboard.astro b/src/pages/dashboard.astro index 08fdffa52..88af1687a 100644 --- a/src/pages/dashboard.astro +++ b/src/pages/dashboard.astro @@ -1,56 +1,53 @@ --- +import { DateTime } from 'luxon'; import { DashboardPage } from '../components/Dashboard/DashboardPage'; import BaseLayout from '../layouts/BaseLayout.astro'; import { getAllBestPractices } from '../lib/best-practice'; import { getAllQuestionGroups } from '../lib/question-group'; -import { getRoadmapsByTag } from '../lib/roadmap'; import { getAllVideos } from '../lib/video'; import { listOfficialGuides } from '../queries/official-guide'; +import { + isNewRoadmap, + listOfficialRoadmaps, +} from '../queries/official-roadmap'; +import type { BuiltInRoadmap } from '../components/Dashboard/PersonalDashboard'; -const roleRoadmaps = await getRoadmapsByTag('role-roadmap'); -const skillRoadmaps = await getRoadmapsByTag('skill-roadmap'); +const roadmaps = await listOfficialRoadmaps(); +const roleRoadmaps = roadmaps.filter((roadmap) => roadmap.type === 'role'); +const skillRoadmaps = roadmaps.filter((roadmap) => roadmap.type === 'skill'); const bestPractices = await getAllBestPractices(); const questionGroups = await getAllQuestionGroups(); const guides = await listOfficialGuides(); const videos = await getAllVideos(); -const enrichedRoleRoadmaps = roleRoadmaps - .filter((roadmapItem) => !roadmapItem.frontmatter.isHidden) - .map((roadmap) => { - const { frontmatter } = roadmap; - - return { - id: roadmap.id, - url: `/${roadmap.id}`, - title: frontmatter.briefTitle, - description: frontmatter.briefDescription, - relatedRoadmapIds: frontmatter.relatedRoadmaps, - renderer: frontmatter.renderer, - isNew: frontmatter.isNew, - metadata: { - tags: frontmatter.tags, - }, - }; - }); -const enrichedSkillRoadmaps = skillRoadmaps - .filter((roadmapItem) => !roadmapItem.frontmatter.isHidden) - .map((roadmap) => { - const { frontmatter } = roadmap; - - return { - 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 enrichedRoleRoadmaps: BuiltInRoadmap[] = roleRoadmaps.map((roadmap) => { + return { + id: roadmap.slug, + url: `/${roadmap.slug}`, + title: roadmap.title.card, + description: roadmap.description, + relatedRoadmapIds: roadmap.relatedRoadmaps, + renderer: 'editor', + isNew: isNewRoadmap(roadmap.createdAt), + metadata: { + tags: ['role-roadmap'], + }, + }; +}); +const enrichedSkillRoadmaps: BuiltInRoadmap[] = skillRoadmaps.map((roadmap) => { + return { + id: roadmap.slug, + url: `/${roadmap.slug}`, + title: roadmap.title.card === 'Go' ? 'Go Roadmap' : roadmap.title.card, + description: roadmap.description, + relatedRoadmapIds: roadmap.relatedRoadmaps, + renderer: 'editor', + isNew: isNewRoadmap(roadmap.createdAt), + metadata: { + tags: ['skill-roadmap'], + }, + }; +}); const enrichedBestPractices = bestPractices.map((bestPractice) => { const { frontmatter } = bestPractice; diff --git a/src/pages/index.astro b/src/pages/index.astro index bbbd2aab8..658bd2677 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,4 +1,5 @@ --- +import { DateTime } from 'luxon'; import ChangelogBanner from '../components/ChangelogBanner.astro'; import { FeaturedGuideList } from '../components/FeaturedGuides/FeaturedGuideList'; import FeaturedItems from '../components/FeaturedItems/FeaturedItems.astro'; @@ -6,12 +7,16 @@ import { FeaturedVideoList } from '../components/FeaturedVideos/FeaturedVideoLis import HeroSection from '../components/HeroSection/HeroSection.astro'; import BaseLayout from '../layouts/BaseLayout.astro'; import { getAllBestPractices } from '../lib/best-practice'; -import { getRoadmapsByTag } from '../lib/roadmap'; import { getAllVideos } from '../lib/video'; import { listOfficialGuides } from '../queries/official-guide'; +import { + isNewRoadmap, + listOfficialRoadmaps, +} from '../queries/official-roadmap'; -const roleRoadmaps = await getRoadmapsByTag('role-roadmap'); -const skillRoadmaps = await getRoadmapsByTag('skill-roadmap'); +const roadmaps = await listOfficialRoadmaps(); +const roleRoadmaps = roadmaps.filter((roadmap) => roadmap.type === 'role'); +const skillRoadmaps = roadmaps.filter((roadmap) => roadmap.type === 'skill'); const bestPractices = await getAllBestPractices(); export const projectGroups = [ @@ -47,33 +52,32 @@ const videos = await getAllVideos(); !roadmapItem.frontmatter.isHidden) - .map((roadmapItem) => ({ - text: roadmapItem.frontmatter.briefTitle, - url: `/${roadmapItem.id}`, - isNew: roadmapItem.frontmatter.isNew, - isUpcoming: roadmapItem.frontmatter.isUpcoming, - }))} + featuredItems={roleRoadmaps.map((roadmapItem) => { + const isNew = isNewRoadmap(roadmapItem.createdAt); + + return { + text: roadmapItem.title.card, + url: `/${roadmapItem.slug}`, + isNew, + }; + })} showCreateRoadmap={true} /> !roadmapItem.frontmatter.isHidden) - .map((roadmapItem) => ({ + featuredItems={skillRoadmaps.map((roadmapItem) => { + const isNew = isNewRoadmap(roadmapItem.createdAt); + + return { text: - roadmapItem.frontmatter.briefTitle === 'Go' + roadmapItem.title.card === 'Go' ? 'Go Roadmap' - : roadmapItem.frontmatter.briefTitle.replace( - 'Software Design', - 'Design', - ), - url: `/${roadmapItem.id}`, - isNew: roadmapItem.frontmatter.isNew, - isUpcoming: roadmapItem.frontmatter.isUpcoming, - }))} + : roadmapItem.title.card.replace('Software Design', 'Design'), + url: `/${roadmapItem.slug}`, + isNew, + }; + })} showCreateRoadmap={true} /> diff --git a/src/pages/pages.json.ts b/src/pages/pages.json.ts index 1819f45b5..eec165088 100644 --- a/src/pages/pages.json.ts +++ b/src/pages/pages.json.ts @@ -1,5 +1,4 @@ import { getAllBestPractices } from '../lib/best-practice'; -import { getRoadmapsByTag } from '../lib/roadmap'; import { getAllVideos } from '../lib/video'; import { getAllQuestionGroups } from '../lib/question-group'; import { getAllProjects } from '../lib/project'; @@ -7,6 +6,7 @@ import { listOfficialAuthors, listOfficialGuides, } from '../queries/official-guide'; +import { listOfficialRoadmaps } from '../queries/official-roadmap'; // Add utility to fetch beginner roadmap file IDs function getBeginnerRoadmapIds() { @@ -25,40 +25,49 @@ export async function GET() { const authors = await listOfficialAuthors(); const videos = await getAllVideos(); const questionGroups = await getAllQuestionGroups(); - const roadmaps = await getRoadmapsByTag('roadmap'); + const roadmaps = await listOfficialRoadmaps(); + const bestPractices = await getAllBestPractices(); const projects = await getAllProjects(); // Transform main roadmaps into page objects first so that we can reuse their meta for beginner variants - const roadmapPages = roadmaps.map((roadmap) => ({ - id: roadmap.id, - url: `/${roadmap.id}`, - title: roadmap.frontmatter.briefTitle, - shortTitle: roadmap.frontmatter.title, - description: roadmap.frontmatter.briefDescription, - group: 'Roadmaps', - metadata: { - tags: roadmap.frontmatter.tags, - }, - renderer: roadmap?.frontmatter?.renderer || 'balsamiq', - })); + const roadmapPages = roadmaps + .map((roadmap) => { + const isBeginner = roadmap.slug.endsWith('-beginner'); + if (!isBeginner) { + return { + id: roadmap.slug, + url: `/${roadmap.slug}`, + title: roadmap.title.card, + shortTitle: roadmap.title.card, + description: roadmap.description, + group: 'Roadmaps', + metadata: { + tags: + roadmap.type === 'role' ? ['role-roadmap'] : ['skill-roadmap'], + }, + renderer: 'editor', + }; + } - // Generate beginner roadmap page objects - const beginnerRoadmapPages = getBeginnerRoadmapIds() - .map((beginnerId) => { - const parentId = beginnerId.replace('-beginner', ''); - const parentMeta = roadmapPages.find((page) => page.id === parentId); + const parentSlug = roadmap.slug.replace('-beginner', ''); + const parentMeta = roadmaps.find((r) => r.slug === parentSlug); if (!parentMeta) { return null; } return { - ...parentMeta, - id: beginnerId, - url: `/${parentId}?r=${beginnerId}`, - title: `${parentMeta.title} Beginner`, - shortTitle: `${parentMeta.shortTitle} Beginner`, + id: roadmap.slug, + url: `/${parentSlug}?r=${roadmap.slug}`, + title: `${parentMeta.title.page} Beginner`, + shortTitle: `${parentMeta.title.page} Beginner`, + description: parentMeta.description, + group: 'Roadmaps', + metadata: { + tags: ['beginner-roadmap'], + }, + renderer: 'editor', }; }) .filter(Boolean); @@ -66,7 +75,6 @@ export async function GET() { return new Response( JSON.stringify([ ...roadmapPages, - ...beginnerRoadmapPages, ...bestPractices.map((bestPractice) => ({ id: bestPractice.id, url: `/best-practices/${bestPractice.id}`, diff --git a/src/pages/projects/index.astro b/src/pages/projects/index.astro index 83dc0042f..68f08a2a4 100644 --- a/src/pages/projects/index.astro +++ b/src/pages/projects/index.astro @@ -1,23 +1,27 @@ --- import BaseLayout from '../../layouts/BaseLayout.astro'; import { getRoadmapsProjects } from '../../lib/project'; -import { getRoadmapsByIds } from '../../lib/roadmap'; import { ProjectsPageHeader } from '../../components/Projects/ProjectsPageHeader'; import { ProjectsPage } from '../../components/Projects/ProjectsPage'; import { projectApi } from '../../api/project'; +import { listOfficialRoadmaps } from '../../queries/official-roadmap'; const roadmapProjects = await getRoadmapsProjects(); const allRoadmapIds = Object.keys(roadmapProjects); -const allRoadmaps = await getRoadmapsByIds(allRoadmapIds); +const roadmaps = await listOfficialRoadmaps(); +const allRoadmaps = roadmaps.filter((roadmap) => + allRoadmapIds.includes(roadmap.slug), +); + const enrichedRoadmaps = allRoadmaps.map((roadmap) => { - const projects = (roadmapProjects[roadmap.id] || []).sort((a, b) => { + const projects = (roadmapProjects[roadmap.slug] || []).sort((a, b) => { return a.frontmatter.sort - b.frontmatter.sort; }); return { - id: roadmap.id, - title: roadmap.frontmatter.briefTitle, + id: roadmap.slug, + title: roadmap.title.card, projects, }; }); @@ -42,5 +46,5 @@ const { response: userCounts } = userCounts={userCounts || {}} client:load /> -
+
diff --git a/src/pages/roadmaps.astro b/src/pages/roadmaps.astro index c0f4db6be..a5b202f95 100644 --- a/src/pages/roadmaps.astro +++ b/src/pages/roadmaps.astro @@ -1,10 +1,7 @@ --- import { RoadmapsPage } from '../components/Roadmaps/RoadmapsPage'; 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 { getRoadmapsByTag } from '../lib/roadmap'; import ChangelogBanner from '../components/ChangelogBanner.astro'; --- diff --git a/src/queries/official-roadmap.ts b/src/queries/official-roadmap.ts index cbdc4f193..89a2138c1 100644 --- a/src/queries/official-roadmap.ts +++ b/src/queries/official-roadmap.ts @@ -1,6 +1,7 @@ import { queryOptions } from '@tanstack/react-query'; import { FetchError, httpGet } from '../lib/query-http'; import type { Node, Edge } from '@roadmapsh/editor'; +import { DateTime } from 'luxon'; export const allowedOfficialRoadmapType = ['skill', 'role'] as const; export type AllowedOfficialRoadmapType = @@ -103,3 +104,11 @@ export async function listOfficialRoadmaps() { throw error; } } + +export function isNewRoadmap(createdAt: Date) { + return ( + createdAt && + DateTime.now().diff(DateTime.fromJSDate(new Date(createdAt)), 'days').days < + 45 + ); +}