-
+
+
-
+
{Math.floor(+progressPercentage)}%
-
-
- {isCustomResource ? (
- <>Last updated {getRelativeTimeString(updatedAt)}>
- ) : (
- <>Last practiced {getRelativeTimeString(updatedAt)}>
- )}
-
);
}
diff --git a/src/components/Dashboard/DashboardProjectCard.tsx b/src/components/Dashboard/DashboardProjectCard.tsx
new file mode 100644
index 000000000..14a033bb0
--- /dev/null
+++ b/src/components/Dashboard/DashboardProjectCard.tsx
@@ -0,0 +1,34 @@
+import { CircleCheck, CircleDashed } from 'lucide-react';
+import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
+
+type DashboardProjectCardProps = {
+ project: ProjectStatusDocument & {
+ title: string;
+ };
+};
+
+export function DashboardProjectCard(props: DashboardProjectCardProps) {
+ const { project } = props;
+
+ const { title, projectId, submittedAt, repositoryUrl } = project;
+
+ const url = `/projects/${projectId}`;
+ const status = submittedAt && repositoryUrl ? 'submitted' : 'started';
+
+ return (
+
+ {status === 'submitted' ? (
+
+ ) : (
+
+ )}
+
+ {title}
+
+
+ );
+}
diff --git a/src/components/Dashboard/ListDashboardCustomProgress.tsx b/src/components/Dashboard/ListDashboardCustomProgress.tsx
index 4915f3b7f..0f52e7518 100644
--- a/src/components/Dashboard/ListDashboardCustomProgress.tsx
+++ b/src/components/Dashboard/ListDashboardCustomProgress.tsx
@@ -1,6 +1,5 @@
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
-import { DashboardProgressCard } from './DashboardProgressCard';
-import { DashboardProgressCardSkeleton } from './ListDashboardProgress';
+import { DashboardCustomProgressCard } from './DashboardCustomProgressCard';
import { DashboardCardLink } from './DashboardCardLink';
import { useState } from 'react';
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal';
@@ -67,13 +66,13 @@ export function ListDashboardCustomProgress(
{isLoading ? (
<>
{Array.from({ length: 8 }).map((_, index) => (
-
+
))}
>
) : (
<>
{progresses.map((progress) => (
-
@@ -97,3 +96,13 @@ export function ListDashboardCustomProgress(
>
);
}
+
+type CustomProgressCardSkeletonProps = {};
+
+export function CustomProgressCardSkeleton(
+ props: CustomProgressCardSkeletonProps,
+) {
+ return (
+
+ );
+}
diff --git a/src/components/Dashboard/ListDashboardProgress.tsx b/src/components/Dashboard/ListDashboardProgress.tsx
deleted file mode 100644
index 4fd9609f2..000000000
--- a/src/components/Dashboard/ListDashboardProgress.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { getPercentage } from '../../helper/number';
-import { getUser } from '../../lib/jwt';
-import type { UserProgress } from '../TeamProgress/TeamProgressPage';
-import { DashboardProgressCard } from './DashboardProgressCard';
-
-type ListDashboardProgressProps = {
- progresses: UserProgress[];
- isLoading?: boolean;
- isCustomResources?: boolean;
-};
-
-export function ListDashboardProgress(props: ListDashboardProgressProps) {
- const { progresses, isLoading = false } = props;
-
- if (!isLoading && progresses.length === 0) {
- return null;
- }
-
- return (
- <>
-
- Progress and Bookmarks
-
-
-
- {isLoading ? (
- <>
- {Array.from({ length: 8 }).map((_, index) => (
-
- ))}
- >
- ) : (
- <>
- {progresses.map((progress) => (
-
- ))}
- >
- )}
-
- >
- );
-}
-
-type DashboardProgressCardSkeletonProps = {};
-
-export function DashboardProgressCardSkeleton(
- props: DashboardProgressCardSkeletonProps,
-) {
- return (
-
- );
-}
diff --git a/src/components/Dashboard/PersonalDashboard.tsx b/src/components/Dashboard/PersonalDashboard.tsx
index a3399906a..7b948e161 100644
--- a/src/components/Dashboard/PersonalDashboard.tsx
+++ b/src/components/Dashboard/PersonalDashboard.tsx
@@ -2,19 +2,12 @@ import { useEffect, useState, type ReactNode } from 'react';
import { httpGet } from '../../lib/http';
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
-import { ResourceProgress } from '../Activity/ResourceProgress';
-import { ProjectProgress } from '../Activity/ProjectProgress';
import type { PageType } from '../CommandMenu/CommandMenu';
import { useToast } from '../../hooks/use-toast';
-import { LoadingProgress } from './LoadingProgress';
-import { ArrowUpRight, Pencil, Plus } from 'lucide-react';
-import { MarkFavorite } from '../FeaturedItems/MarkFavorite';
-import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal';
import { getCurrentPeriod } from '../../lib/date';
-import { ListDashboardProgress } from './ListDashboardProgress';
import { ListDashboardCustomProgress } from './ListDashboardCustomProgress';
-import { DashboardCardLink } from './DashboardCardLink';
import { RecommendedRoadmaps } from './RecommendedRoadmaps';
+import { ProgressStack } from './ProgressStack';
type UserDashboardResponse = {
name: string;
@@ -48,9 +41,11 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
builtInSkillRoadmaps = [],
} = props;
+ const toast = useToast();
const [isLoading, setIsLoading] = useState(true);
const [personalDashboardDetails, setPersonalDashboardDetails] =
useState
();
+ const [projectDetails, setProjectDetails] = useState([]);
async function loadProgress() {
const { response: progressList, error } =
@@ -76,8 +71,26 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
setPersonalDashboardDetails(progressList);
}
+ async function loadAllProjectDetails() {
+ const { error, response } = await httpGet(`/pages.json`);
+
+ if (error) {
+ toast.error(error.message || 'Something went wrong');
+ return;
+ }
+
+ if (!response) {
+ return [];
+ }
+
+ const allProjects = response.filter((page) => page.group === 'Projects');
+ setProjectDetails(allProjects);
+ }
+
useEffect(() => {
- loadProgress().finally(() => setIsLoading(false));
+ Promise.allSettled([loadProgress(), loadAllProjectDetails()]).finally(() =>
+ setIsLoading(false),
+ );
}, []);
useEffect(() => {
@@ -152,6 +165,29 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
recommendedRoadmapIds.has(roadmap.id),
);
+ const enrichedProjects = personalDashboardDetails?.projects
+ .map((project) => {
+ const projectDetail = projectDetails.find(
+ (page) => page.id === project.projectId,
+ );
+
+ return {
+ ...project,
+ title: projectDetail?.title || 'N/A',
+ };
+ })
+ .sort((a, b) => {
+ if (a.repositoryUrl && !b.repositoryUrl) {
+ return 1;
+ }
+
+ if (!a.repositoryUrl && b.repositoryUrl) {
+ return -1;
+ }
+
+ return 0;
+ });
+
return (
{isLoading ? (
@@ -212,13 +248,9 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
)}
-
-
-
@@ -231,6 +263,11 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
isLoading={isLoading}
isAIGeneratedRoadmaps={true}
/>
+
+
);
}
diff --git a/src/components/Dashboard/ProgressStack.tsx b/src/components/Dashboard/ProgressStack.tsx
new file mode 100644
index 000000000..818725cc9
--- /dev/null
+++ b/src/components/Dashboard/ProgressStack.tsx
@@ -0,0 +1,231 @@
+import {
+ ArrowUpRight,
+ Bookmark,
+ Check,
+ CheckCircle,
+ CheckIcon,
+ CircleCheck,
+ CircleDashed,
+} from 'lucide-react';
+import { ResourceProgress } from '../Activity/ResourceProgress';
+import type { UserProgress } from '../TeamProgress/TeamProgressPage';
+import { getPercentage } from '../../helper/number';
+import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
+import { DashboardBookmarkCard } from './DashboardBookmarkCard';
+import { DashboardProjectCard } from './DashboardProjectCard';
+import { useState } from 'react';
+import { cn } from '../../lib/classname';
+import { DashboardProgressCard } from './DashboardProgressCard';
+
+type ProgressStackProps = {
+ progresses: UserProgress[];
+ projects: (ProjectStatusDocument & {
+ title: string;
+ })[];
+ isLoading: boolean;
+};
+
+const MAX_PROGRESS_TO_SHOW = 5;
+const MAX_PROJECTS_TO_SHOW = 8;
+const MAX_BOOKMARKS_TO_SHOW = 8;
+
+export function ProgressStack(props: ProgressStackProps) {
+ const { progresses, projects, isLoading } = props;
+
+ const bookmarkedProgresses = progresses.filter(
+ (progress) =>
+ progress?.isFavorite &&
+ progress?.done === 0 &&
+ progress?.learning === 0 &&
+ progress?.skipped === 0,
+ );
+
+ const userProgresses = progresses.filter((progress) => !progress?.isFavorite);
+
+ const [showAllProgresses, setShowAllProgresses] = useState(false);
+ const userProgressesToShow = showAllProgresses
+ ? userProgresses
+ : userProgresses.slice(0, MAX_PROGRESS_TO_SHOW);
+
+ const [showAllProjects, setShowAllProjects] = useState(false);
+ const projectsToShow = showAllProjects
+ ? projects
+ : projects.slice(0, MAX_PROJECTS_TO_SHOW);
+
+ const [showAllBookmarks, setShowAllBookmarks] = useState(false);
+ const bookmarksToShow = showAllBookmarks
+ ? bookmarkedProgresses
+ : bookmarkedProgresses.slice(0, MAX_BOOKMARKS_TO_SHOW);
+
+ return (
+
+
+
Your Progress
+
+
+ {isLoading ? (
+ <>
+
+
+
+
+
+ >
+ ) : (
+ <>
+ {userProgressesToShow.map((progress) => {
+ return (
+
+ );
+ })}
+ >
+ )}
+
+
+ {userProgresses.length > MAX_PROGRESS_TO_SHOW && (
+
+ )}
+
+
+
+
Projects
+
+
+ {isLoading ? (
+ <>
+
+
+
+
+
+
+
+
+ >
+ ) : (
+ <>
+ {projectsToShow.map((project) => {
+ return (
+
+ );
+ })}
+ >
+ )}
+
+
+ {projects.length > MAX_PROJECTS_TO_SHOW && (
+
+ )}
+
+
+
+
+
+
+ {isLoading ? (
+ <>
+
+
+
+
+
+
+
+
+ >
+ ) : (
+ <>
+ {bookmarksToShow.map((progress) => {
+ return (
+
+ );
+ })}
+ >
+ )}
+
+
+ {bookmarkedProgresses.length > MAX_BOOKMARKS_TO_SHOW && (
+
+ )}
+
+
+ );
+}
+
+type ShowAllButtonProps = {
+ showAll: boolean;
+ setShowAll: (showAll: boolean) => void;
+ count: number;
+ maxCount: number;
+ className?: string;
+};
+
+function ShowAllButton(props: ShowAllButtonProps) {
+ const { showAll, setShowAll, count, maxCount, className } = props;
+
+ return (
+
+ );
+}
+
+type CardSkeletonProps = {
+ className?: string;
+};
+
+function CardSkeleton(props: CardSkeletonProps) {
+ const { className } = props;
+
+ return (
+
+ );
+}
diff --git a/src/components/Dashboard/RecommendedRoadmaps.tsx b/src/components/Dashboard/RecommendedRoadmaps.tsx
index 1c18338e5..73c380171 100644
--- a/src/components/Dashboard/RecommendedRoadmaps.tsx
+++ b/src/components/Dashboard/RecommendedRoadmaps.tsx
@@ -11,9 +11,19 @@ export function RecommendedRoadmaps(props: RecommendedRoadmapsProps) {
return (
<>
-
- Recommended Roadmaps
-
+
{isLoading ? (