diff --git a/src/api/user.ts b/src/api/user.ts
index dc07daeaa..8a677c744 100644
--- a/src/api/user.ts
+++ b/src/api/user.ts
@@ -38,6 +38,29 @@ export type UserActivityCount = {
totalActivityCount: number;
};
+type ProgressResponse = {
+ updatedAt: string;
+ title: string;
+ id: string;
+ learning: number;
+ skipped: number;
+ done: number;
+ total: number;
+ isCustomResource?: boolean;
+ roadmapSlug?: string;
+};
+
+export type UserResourceProgressStats = {
+ done: {
+ total: number;
+ };
+ learning: {
+ total: number;
+ roadmaps: ProgressResponse[];
+ bestPractices: ProgressResponse[];
+ };
+};
+
export type GetUserByUsernameResponse = Omit<
UserDocument,
| 'password'
@@ -45,9 +68,10 @@ export type GetUserByUsernameResponse = Omit<
| 'resetPasswordCode'
| 'resetPasswordCodeAt'
| 'email'
-> & {
- activity: UserActivityCount;
-};
+> &
+ UserResourceProgressStats & {
+ activity: UserActivityCount;
+ };
export function userApi(context: APIContext) {
return {
diff --git a/src/components/UserPublicAccount/UserPublicAccountPage.tsx b/src/components/UserPublicAccount/UserPublicAccountPage.tsx
new file mode 100644
index 000000000..17df977e4
--- /dev/null
+++ b/src/components/UserPublicAccount/UserPublicAccountPage.tsx
@@ -0,0 +1,74 @@
+import type { GetUserByUsernameResponse } from '../../api/user';
+import { UserActivityHeatmap } from './UserPublicActivityHeatmap';
+import { UserPublicDetails } from './UserPublicDetails';
+import { UserPublicProgressStats } from './UserPublicProgressStats';
+
+type UserPublicAccountPageProps = GetUserByUsernameResponse;
+
+export function UserPublicAccountPage(props: UserPublicAccountPageProps) {
+ const { activity, learning } = props;
+
+ const learningRoadmaps = learning?.roadmaps || [];
+ const learningBestPractices = learning?.bestPractices || [];
+
+ return (
+
+
+
+
+
+
+ {(learningRoadmaps.length > 0 || learningBestPractices.length > 0) && (
+ <>
+ Learning Progress
+
+ {learningRoadmaps
+ .sort((a, b) => {
+ const updatedAtA = new Date(a.updatedAt);
+ const updatedAtB = new Date(b.updatedAt);
+
+ return updatedAtB.getTime() - updatedAtA.getTime();
+ })
+ .map((roadmap) => (
+
+ ))}
+
+ {learningBestPractices
+ .sort((a, b) => {
+ const updatedAtA = new Date(a.updatedAt);
+ const updatedAtB = new Date(b.updatedAt);
+
+ return updatedAtB.getTime() - updatedAtA.getTime();
+ })
+ .map((bestPractice) => (
+
+ ))}
+
+ >
+ )}
+
+ );
+}
diff --git a/src/components/Account/UserActivityHeatmap.tsx b/src/components/UserPublicAccount/UserPublicActivityHeatmap.tsx
similarity index 100%
rename from src/components/Account/UserActivityHeatmap.tsx
rename to src/components/UserPublicAccount/UserPublicActivityHeatmap.tsx
diff --git a/src/components/Account/UserDetails.tsx b/src/components/UserPublicAccount/UserPublicDetails.tsx
similarity index 78%
rename from src/components/Account/UserDetails.tsx
rename to src/components/UserPublicAccount/UserPublicDetails.tsx
index 9038a1637..5a32b946d 100644
--- a/src/components/Account/UserDetails.tsx
+++ b/src/components/UserPublicAccount/UserPublicDetails.tsx
@@ -1,11 +1,11 @@
import { Github, Globe, LinkedinIcon, Twitter } from 'lucide-react';
import type { GetUserByUsernameResponse } from '../../api/user';
-type UserDetailsProps = {
+type UserPublicDetailsProps = {
userDetails: GetUserByUsernameResponse;
};
-export function UserDetails(props: UserDetailsProps) {
+export function UserPublicDetails(props: UserPublicDetailsProps) {
const { userDetails } = props;
const { name, username, links } = userDetails;
@@ -20,7 +20,11 @@ export function UserDetails(props: UserDetailsProps) {
{name}
-
@{username}
+
+ I'm a Frontend developer interested in filmmaking, content creation,
+ vlogging, and backend, currently living in Dhaka, Bangladesh. Right
+ now I'm writing html at @roadmapsh. Let's grab a coffee.
+
diff --git a/src/components/UserPublicAccount/UserPublicProgressStats.tsx b/src/components/UserPublicAccount/UserPublicProgressStats.tsx
new file mode 100644
index 000000000..b4b7db79c
--- /dev/null
+++ b/src/components/UserPublicAccount/UserPublicProgressStats.tsx
@@ -0,0 +1,84 @@
+import { getRelativeTimeString } from '../../lib/date';
+
+type UserPublicProgressStats = {
+ resourceType: 'roadmap' | 'best-practice';
+ resourceId: string;
+ title: string;
+ updatedAt: string;
+ totalCount: number;
+ doneCount: number;
+ learningCount: number;
+ skippedCount: number;
+ showClearButton?: boolean;
+ isCustomResource?: boolean;
+ roadmapSlug?: string;
+};
+
+export function UserPublicProgressStats(props: UserPublicProgressStats) {
+ const {
+ updatedAt,
+ resourceType,
+ resourceId,
+ title,
+ totalCount,
+ learningCount,
+ doneCount,
+ skippedCount,
+ roadmapSlug,
+ isCustomResource = false,
+ } = props;
+
+ let url =
+ resourceType === 'roadmap'
+ ? `/${resourceId}`
+ : `/best-practices/${resourceId}`;
+
+ if (isCustomResource) {
+ url = `/r/${roadmapSlug}`;
+ }
+
+ const totalMarked = doneCount + skippedCount;
+ const progressPercentage = Math.round((totalMarked / totalCount) * 100);
+
+ return (
+
+ );
+}
diff --git a/src/pages/u/[username]/index.astro b/src/pages/u/[username]/index.astro
index fa155226b..a036e709a 100644
--- a/src/pages/u/[username]/index.astro
+++ b/src/pages/u/[username]/index.astro
@@ -1,6 +1,5 @@
---
-import { UserActivityHeatmap } from '../../../components/Account/UserActivityHeatmap';
-import { UserDetails } from '../../../components/Account/UserDetails';
+import { UserPublicAccountPage } from '../../../components/UserPublicAccount/UserPublicAccountPage';
import { userApi } from '../../../api/user';
import AccountLayout from '../../../layouts/AccountLayout.astro';
@@ -19,10 +18,5 @@ if (error || !userDetails) {
---
-
+