mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-12 04:04:08 +02:00
feat: add project's user count (#6992)
* feat: add project user count * feat: add user count * fix: user count
This commit is contained in:
15
src/api/project.ts
Normal file
15
src/api/project.ts
Normal file
@@ -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<Record<string, number>>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-list-projects-user-count`,
|
||||
{
|
||||
projectIds,
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
@@ -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<ProjectDifficultyType, string> = {
|
||||
@@ -15,7 +17,7 @@ const badgeVariants: Record<ProjectDifficultyType, string> = {
|
||||
};
|
||||
|
||||
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) {
|
||||
</span>
|
||||
<span className="mb-1 mt-2.5 font-medium">{frontmatter.title}</span>
|
||||
<span className="text-sm text-gray-500">{frontmatter.description}</span>
|
||||
<span className="mt-2.5 flex items-center gap-2 text-xs text-gray-500">
|
||||
<Users className="inline-block size-3.5" />
|
||||
{userCount > 0 ? <>{userCount} Started</> : <>Be the first to solve!</>}
|
||||
</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@@ -40,10 +40,11 @@ function DifficultyButton(props: DifficultyButtonProps) {
|
||||
|
||||
type ProjectsListProps = {
|
||||
projects: ProjectFileType[];
|
||||
userCounts: Record<string, number>;
|
||||
};
|
||||
|
||||
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) => (
|
||||
<ProjectCard project={matchingProject} />
|
||||
))}
|
||||
.map((matchingProject) => {
|
||||
const count = userCounts[matchingProject?.id] || 0;
|
||||
return <ProjectCard project={matchingProject} userCount={count} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -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<string, string> = {
|
||||
'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);
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
@@ -95,7 +87,15 @@ const projects = await getProjectsByRoadmapId(roadmapId);
|
||||
|
||||
<div class='container'>
|
||||
{projects.length === 0 && <EmptyProjects client:load />}
|
||||
{projects.length > 0 && <ProjectsList projects={projects} client:load />}
|
||||
{
|
||||
projects.length > 0 && (
|
||||
<ProjectsList
|
||||
projects={projects}
|
||||
userCounts={userCounts || {}}
|
||||
client:load
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
Reference in New Issue
Block a user