diff --git a/src/api/roadmap.ts b/src/api/roadmap.ts index dab4e6fcb..2df832159 100644 --- a/src/api/roadmap.ts +++ b/src/api/roadmap.ts @@ -1,6 +1,7 @@ import { type APIContext } from 'astro'; import { api } from './api.ts'; import type { RoadmapDocument } from '../components/CustomRoadmap/CreateRoadmap/CreateRoadmapModal.tsx'; +import type { PageType } from '../components/CommandMenu/CommandMenu.tsx'; export type ListShowcaseRoadmapResponse = { data: Pick< @@ -37,3 +38,30 @@ export function roadmapApi(context: APIContext) { }, }; } + +export type ProjectPageType = { + id: string; + title: string; + url: string; +}; + +export async function getProjectList() { + const baseUrl = import.meta.env.DEV + ? 'http://localhost:3000' + : 'https://roadmap.sh'; + const pages = await fetch(`${baseUrl}/pages.json`).catch((err) => { + console.error(err); + return []; + }); + + const pagesJson = await (pages as any).json(); + const projects: ProjectPageType[] = pagesJson + .filter((page: any) => page?.group?.toLowerCase() === 'projects') + .map((page: any) => ({ + id: page.id, + title: page.title, + url: page.url, + })); + + return projects; +} diff --git a/src/api/user.ts b/src/api/user.ts index 819c68dbd..4e557e166 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -1,6 +1,7 @@ import { type APIContext } from 'astro'; import { api } from './api.ts'; import type { ResourceType } from '../lib/resource-progress.ts'; +import type { ProjectStatusDocument } from '../components/Projects/ListProjectSolutions.tsx'; export const allowedRoadmapVisibility = ['all', 'none', 'selected'] as const; export type AllowedRoadmapVisibility = @@ -99,6 +100,7 @@ export type GetPublicProfileResponse = Omit< > & { activity: UserActivityCount; roadmaps: ProgressResponse[]; + projects: ProjectStatusDocument[]; isOwnProfile: boolean; }; diff --git a/src/components/UserPublicProfile/UserPublicProfilePage.tsx b/src/components/UserPublicProfile/UserPublicProfilePage.tsx index d7c8342bb..47ecb8005 100644 --- a/src/components/UserPublicProfile/UserPublicProfilePage.tsx +++ b/src/components/UserPublicProfile/UserPublicProfilePage.tsx @@ -1,10 +1,14 @@ +import type { ProjectPageType } from '../../api/roadmap'; import type { GetPublicProfileResponse } from '../../api/user'; import { PrivateProfileBanner } from './PrivateProfileBanner'; import { UserActivityHeatmap } from './UserPublicActivityHeatmap'; import { UserPublicProfileHeader } from './UserPublicProfileHeader'; import { UserPublicProgresses } from './UserPublicProgresses'; +import { UserPublicProjects } from './UserPublicProjects'; -type UserPublicProfilePageProps = GetPublicProfileResponse; +type UserPublicProfilePageProps = GetPublicProfileResponse & { + projectDetails: ProjectPageType[]; +}; export function UserPublicProfilePage(props: UserPublicProfilePageProps) { const { @@ -14,10 +18,11 @@ export function UserPublicProfilePage(props: UserPublicProfilePageProps) { profileVisibility, _id: userId, createdAt, + projectDetails, } = props; return ( -
+
- +
+ + +
); diff --git a/src/components/UserPublicProfile/UserPublicProjects.tsx b/src/components/UserPublicProfile/UserPublicProjects.tsx new file mode 100644 index 000000000..a96a1a3eb --- /dev/null +++ b/src/components/UserPublicProfile/UserPublicProjects.tsx @@ -0,0 +1,57 @@ +import type { ProjectPageType } from '../../api/roadmap'; +import { ProjectProgress } from '../Activity/ProjectProgress'; +import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions'; + +type UserPublicProjectsProps = { + userId: string; + projects: ProjectStatusDocument[]; + projectDetails: ProjectPageType[]; +}; + +export function UserPublicProjects(props: UserPublicProjectsProps) { + const { projects, projectDetails } = props; + + const enrichedProjects = + projects + .map((project) => { + const projectDetail = projectDetails.find( + (projectDetail) => projectDetail.id === project.projectId, + ); + + return { + ...project, + title: projectDetail?.title || 'N/A', + }; + }) + ?.sort((a, b) => { + const isPendingA = !a.repositoryUrl && !a.submittedAt; + const isPendingB = !b.repositoryUrl && !b.submittedAt; + + if (isPendingA && !isPendingB) { + return -1; + } + + if (!isPendingA && isPendingB) { + return 1; + } + + return 0; + }) || []; + + return ( +
+

+ Projects I have worked on +

+
+ {enrichedProjects.map((project) => ( + + ))} +
+
+ ); +} diff --git a/src/pages/u/[username].astro b/src/pages/u/[username].astro index 2b8e0dbb1..c536240d0 100644 --- a/src/pages/u/[username].astro +++ b/src/pages/u/[username].astro @@ -1,4 +1,5 @@ --- +import { getProjectList } from '../../api/roadmap'; import { userApi } from '../../api/user'; import { UserPublicProfilePage } from '../../components/UserPublicProfile/UserPublicProfilePage'; import BaseLayout from '../../layouts/BaseLayout.astro'; @@ -23,6 +24,7 @@ if (error || !userDetails) { errorMessage = error?.message || 'User not found'; } +const projectDetails = await getProjectList(); const origin = Astro.url.origin; const ogImage = `${origin}/og/user/${username}`; --- @@ -32,7 +34,15 @@ const ogImage = `${origin}/og/user/${username}`; description='Check out my skill profile at roadmap.sh' ogImageUrl={ogImage} > - {!errorMessage && } + { + !errorMessage && ( + + ) + } { errorMessage && (