diff --git a/src/api/project.ts b/src/api/project.ts new file mode 100644 index 000000000..18647eabd --- /dev/null +++ b/src/api/project.ts @@ -0,0 +1,15 @@ +import { type APIContext } from 'astro'; +import { api } from './api.ts'; + +export function projectApi(context: APIContext) { + return { + listProjectsUserCount: async function (projectIds: string[]) { + return api(context).post>( + `${import.meta.env.PUBLIC_API_URL}/v1-list-projects-user-count`, + { + projectIds, + }, + ); + }, + }; +} diff --git a/src/components/Projects/ProjectCard.tsx b/src/components/Projects/ProjectCard.tsx index 5f8179801..c66f085b3 100644 --- a/src/components/Projects/ProjectCard.tsx +++ b/src/components/Projects/ProjectCard.tsx @@ -3,9 +3,11 @@ import type { ProjectDifficultyType, ProjectFileType, } from '../../lib/project.ts'; +import { Users } from 'lucide-react'; type ProjectCardProps = { project: ProjectFileType; + userCount?: number; }; const badgeVariants: Record = { @@ -15,7 +17,7 @@ const badgeVariants: Record = { }; export function ProjectCard(props: ProjectCardProps) { - const { project } = props; + const { project, userCount = 0 } = props; const { frontmatter, id } = project; @@ -33,6 +35,10 @@ export function ProjectCard(props: ProjectCardProps) { {frontmatter.title} {frontmatter.description} + + + {userCount > 0 ? <>{userCount} Started : <>Be the first to solve!} + ); } diff --git a/src/components/Projects/ProjectsList.tsx b/src/components/Projects/ProjectsList.tsx index f6bfaed69..aa2bb8c3e 100644 --- a/src/components/Projects/ProjectsList.tsx +++ b/src/components/Projects/ProjectsList.tsx @@ -40,10 +40,11 @@ function DifficultyButton(props: DifficultyButtonProps) { type ProjectsListProps = { projects: ProjectFileType[]; + userCounts: Record; }; export function ProjectsList(props: ProjectsListProps) { - const { projects } = props; + const { projects, userCounts } = props; const { difficulty: urlDifficulty } = getUrlParams(); const [difficulty, setDifficulty] = useState< @@ -127,9 +128,10 @@ export function ProjectsList(props: ProjectsListProps) { .sort((a, b) => { return a.frontmatter.sort - b.frontmatter.sort; }) - .map((matchingProject) => ( - - ))} + .map((matchingProject) => { + const count = userCounts[matchingProject?.id] || 0; + return ; + })} ); diff --git a/src/pages/[roadmapId]/projects.astro b/src/pages/[roadmapId]/projects.astro index fc0046787..78ed00e9b 100644 --- a/src/pages/[roadmapId]/projects.astro +++ b/src/pages/[roadmapId]/projects.astro @@ -1,25 +1,12 @@ --- -import { EditorRoadmap } from '../../components/EditorRoadmap/EditorRoadmap'; -import FAQs, { type FAQType } from '../../components/FAQs/FAQs.astro'; -import FrameRenderer from '../../components/FrameRenderer/FrameRenderer.astro'; -import RelatedRoadmaps from '../../components/RelatedRoadmaps.astro'; import RoadmapHeader from '../../components/RoadmapHeader.astro'; -import { FolderKanbanIcon } from 'lucide-react'; import { EmptyProjects } from '../../components/Projects/EmptyProjects'; import { ProjectsList } from '../../components/Projects/ProjectsList'; -import ShareIcons from '../../components/ShareIcons/ShareIcons.astro'; -import { UserProgressModal } from '../../components/UserProgress/UserProgressModal'; import BaseLayout from '../../layouts/BaseLayout.astro'; import { getProjectsByRoadmapId } from '../../lib/project'; -import { - generateArticleSchema, - generateFAQSchema, -} from '../../lib/jsonld-schema'; import { getOpenGraphImageUrl } from '../../lib/open-graph'; import { type RoadmapFrontmatter, getRoadmapIds } from '../../lib/roadmap'; -import RoadmapNote from '../../components/RoadmapNote.astro'; -import { RoadmapTitleQuestion } from '../../components/RoadmapTitleQuestion'; -import ResourceProgressStats from '../../components/ResourceProgressStats.astro'; +import { projectApi } from '../../api/project'; export async function getStaticPaths() { const roadmapIds = await getRoadmapIds(); @@ -48,7 +35,7 @@ const ogImageUrl = resourceId: roadmapId, }); -const descriptionNoun = { +const descriptionNoun: Record = { 'AI and Data Scientist': 'AI and Data Science', 'Game Developer': 'Game Development', 'Technical Writer': 'Technical Writing', @@ -61,10 +48,15 @@ const description = `Project ideas to take you from beginner to advanced in ${de // `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 nounTitle = - descriptionNoun[roadmapData.briefTitle] || roadmapData.briefTitle; + 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 projects = await getProjectsByRoadmapId(roadmapId); +const projectIds = projects.map((project) => project.id); + +const projectApiClient = projectApi(Astro); +const { response: userCounts } = + await projectApiClient.listProjectsUserCount(projectIds); --- {projects.length === 0 && } - {projects.length > 0 && } + { + projects.length > 0 && ( + + ) + }