feat: add projects in public page

feat/dashboard
Arik Chakma 3 months ago
parent 7e0dd7dd51
commit 7fab5018a0
  1. 28
      src/api/roadmap.ts
  2. 2
      src/api/user.ts
  3. 16
      src/components/UserPublicProfile/UserPublicProfilePage.tsx
  4. 57
      src/components/UserPublicProfile/UserPublicProjects.tsx
  5. 12
      src/pages/u/[username].astro

@ -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;
}

@ -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;
};

@ -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 (
<div className="bg-gray-200/40 min-h-full flex-grow pt-10 pb-36">
<div className="min-h-full flex-grow bg-gray-200/40 pb-36 pt-10">
<div className="container flex flex-col gap-8">
<PrivateProfileBanner
isOwnProfile={isOwnProfile}
@ -27,12 +32,19 @@ export function UserPublicProfilePage(props: UserPublicProfilePageProps) {
<UserPublicProfileHeader userDetails={props!} />
<UserActivityHeatmap joinedAt={createdAt} activity={activity!} />
<div>
<UserPublicProgresses
username={username!}
userId={userId!}
roadmaps={props.roadmaps}
publicConfig={props.publicConfig}
/>
<UserPublicProjects
userId={userId!}
projects={props.projects}
projectDetails={projectDetails}
/>
</div>
</div>
</div>
);

@ -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 (
<div className="mt-5">
<h2 className="mb-2 text-xs uppercase tracking-wide text-gray-400">
Projects I have worked on
</h2>
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2 md:grid-cols-3">
{enrichedProjects.map((project) => (
<ProjectProgress
key={project._id}
projectStatus={project}
showActions={false}
/>
))}
</div>
</div>
);
}

@ -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 && <UserPublicProfilePage {...userDetails!} client:load />}
{
!errorMessage && (
<UserPublicProfilePage
{...userDetails!}
projectDetails={projectDetails}
client:load
/>
)
}
{
errorMessage && (
<div class='container my-24 flex flex-col'>

Loading…
Cancel
Save