|
|
@ -1,27 +1,20 @@ |
|
|
|
import { |
|
|
|
import { ArrowUpRight } from 'lucide-react'; |
|
|
|
ArrowUpRight, |
|
|
|
|
|
|
|
Bookmark, |
|
|
|
|
|
|
|
Check, |
|
|
|
|
|
|
|
CheckCircle, |
|
|
|
|
|
|
|
CheckIcon, |
|
|
|
|
|
|
|
CircleCheck, |
|
|
|
|
|
|
|
CircleDashed, |
|
|
|
|
|
|
|
} from 'lucide-react'; |
|
|
|
|
|
|
|
import { ResourceProgress } from '../Activity/ResourceProgress'; |
|
|
|
|
|
|
|
import type { UserProgress } from '../TeamProgress/TeamProgressPage'; |
|
|
|
import type { UserProgress } from '../TeamProgress/TeamProgressPage'; |
|
|
|
import { getPercentage } from '../../helper/number'; |
|
|
|
|
|
|
|
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions'; |
|
|
|
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions'; |
|
|
|
import { DashboardBookmarkCard } from './DashboardBookmarkCard'; |
|
|
|
import { DashboardBookmarkCard } from './DashboardBookmarkCard'; |
|
|
|
import { DashboardProjectCard } from './DashboardProjectCard'; |
|
|
|
import { DashboardProjectCard } from './DashboardProjectCard'; |
|
|
|
import { useState } from 'react'; |
|
|
|
import { useState } from 'react'; |
|
|
|
import { cn } from '../../lib/classname'; |
|
|
|
import { cn } from '../../lib/classname'; |
|
|
|
import { DashboardProgressCard } from './DashboardProgressCard'; |
|
|
|
import { DashboardProgressCard } from './DashboardProgressCard'; |
|
|
|
|
|
|
|
import { useStore } from '@nanostores/react'; |
|
|
|
|
|
|
|
import { $accountStreak, type StreakResponse } from '../../stores/streak'; |
|
|
|
|
|
|
|
|
|
|
|
type ProgressStackProps = { |
|
|
|
type ProgressStackProps = { |
|
|
|
progresses: UserProgress[]; |
|
|
|
progresses: UserProgress[]; |
|
|
|
projects: (ProjectStatusDocument & { |
|
|
|
projects: (ProjectStatusDocument & { |
|
|
|
title: string; |
|
|
|
title: string; |
|
|
|
})[]; |
|
|
|
})[]; |
|
|
|
|
|
|
|
accountStreak?: StreakResponse; |
|
|
|
isLoading: boolean; |
|
|
|
isLoading: boolean; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -30,7 +23,7 @@ const MAX_PROJECTS_TO_SHOW = 8; |
|
|
|
const MAX_BOOKMARKS_TO_SHOW = 8; |
|
|
|
const MAX_BOOKMARKS_TO_SHOW = 8; |
|
|
|
|
|
|
|
|
|
|
|
export function ProgressStack(props: ProgressStackProps) { |
|
|
|
export function ProgressStack(props: ProgressStackProps) { |
|
|
|
const { progresses, projects, isLoading } = props; |
|
|
|
const { progresses, projects, isLoading, accountStreak } = props; |
|
|
|
|
|
|
|
|
|
|
|
const bookmarkedProgresses = progresses.filter( |
|
|
|
const bookmarkedProgresses = progresses.filter( |
|
|
|
(progress) => |
|
|
|
(progress) => |
|
|
@ -57,135 +50,154 @@ export function ProgressStack(props: ProgressStackProps) { |
|
|
|
? bookmarkedProgresses |
|
|
|
? bookmarkedProgresses |
|
|
|
: bookmarkedProgresses.slice(0, MAX_BOOKMARKS_TO_SHOW); |
|
|
|
: bookmarkedProgresses.slice(0, MAX_BOOKMARKS_TO_SHOW); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const totalProjectFinished = projects.filter( |
|
|
|
|
|
|
|
(project) => project.repositoryUrl, |
|
|
|
|
|
|
|
).length; |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<div className="mt-2 grid min-h-[330px] grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3"> |
|
|
|
<> |
|
|
|
<div className="h-full rounded-md border bg-white p-4 shadow-sm"> |
|
|
|
<div className="mt-2 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3"> |
|
|
|
<h3 className="text-xs uppercase text-gray-500">Your Progress</h3> |
|
|
|
<StatsCard |
|
|
|
|
|
|
|
title="Current Streak" |
|
|
|
<div className="mt-4 flex flex-col gap-2"> |
|
|
|
value={accountStreak?.count || 0} |
|
|
|
{isLoading ? ( |
|
|
|
isLoading={isLoading} |
|
|
|
<> |
|
|
|
/> |
|
|
|
<CardSkeleton /> |
|
|
|
<StatsCard |
|
|
|
<CardSkeleton /> |
|
|
|
title="Projects Finished" |
|
|
|
<CardSkeleton /> |
|
|
|
value={totalProjectFinished} |
|
|
|
<CardSkeleton /> |
|
|
|
isLoading={isLoading} |
|
|
|
<CardSkeleton /> |
|
|
|
/> |
|
|
|
</> |
|
|
|
</div> |
|
|
|
) : ( |
|
|
|
|
|
|
|
<> |
|
|
|
<div className="mt-2 grid min-h-[330px] grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3"> |
|
|
|
{userProgressesToShow.map((progress) => { |
|
|
|
<div className="h-full rounded-md border bg-white p-4 shadow-sm"> |
|
|
|
return ( |
|
|
|
<h3 className="text-xs uppercase text-gray-500">Your Progress</h3> |
|
|
|
<DashboardProgressCard |
|
|
|
|
|
|
|
key={progress.resourceId} |
|
|
|
<div className="mt-4 flex flex-col gap-2"> |
|
|
|
progress={progress} |
|
|
|
{isLoading ? ( |
|
|
|
/> |
|
|
|
<> |
|
|
|
); |
|
|
|
<CardSkeleton /> |
|
|
|
})} |
|
|
|
<CardSkeleton /> |
|
|
|
</> |
|
|
|
<CardSkeleton /> |
|
|
|
|
|
|
|
<CardSkeleton /> |
|
|
|
|
|
|
|
<CardSkeleton /> |
|
|
|
|
|
|
|
</> |
|
|
|
|
|
|
|
) : ( |
|
|
|
|
|
|
|
<> |
|
|
|
|
|
|
|
{userProgressesToShow.map((progress) => { |
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
|
|
<DashboardProgressCard |
|
|
|
|
|
|
|
key={progress.resourceId} |
|
|
|
|
|
|
|
progress={progress} |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
})} |
|
|
|
|
|
|
|
</> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{userProgresses.length > MAX_PROGRESS_TO_SHOW && ( |
|
|
|
|
|
|
|
<ShowAllButton |
|
|
|
|
|
|
|
showAll={showAllProgresses} |
|
|
|
|
|
|
|
setShowAll={setShowAllProgresses} |
|
|
|
|
|
|
|
count={userProgresses.length} |
|
|
|
|
|
|
|
maxCount={MAX_PROGRESS_TO_SHOW} |
|
|
|
|
|
|
|
className="mt-3" |
|
|
|
|
|
|
|
/> |
|
|
|
)} |
|
|
|
)} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{userProgresses.length > MAX_PROGRESS_TO_SHOW && ( |
|
|
|
<div className="h-full rounded-md border bg-white p-4 shadow-sm"> |
|
|
|
<ShowAllButton |
|
|
|
<h3 className="text-xs uppercase text-gray-500">Projects</h3> |
|
|
|
showAll={showAllProgresses} |
|
|
|
|
|
|
|
setShowAll={setShowAllProgresses} |
|
|
|
|
|
|
|
count={userProgresses.length} |
|
|
|
|
|
|
|
maxCount={MAX_PROGRESS_TO_SHOW} |
|
|
|
|
|
|
|
className="mt-3" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div className="h-full rounded-md border bg-white p-4 shadow-sm"> |
|
|
|
<div className="mt-4 flex flex-col gap-2.5"> |
|
|
|
<h3 className="text-xs uppercase text-gray-500">Projects</h3> |
|
|
|
{isLoading ? ( |
|
|
|
|
|
|
|
<> |
|
|
|
<div className="mt-4 flex flex-col gap-2.5"> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
{isLoading ? ( |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
</> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
) : ( |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<> |
|
|
|
</> |
|
|
|
{projectsToShow.map((project) => { |
|
|
|
) : ( |
|
|
|
return ( |
|
|
|
<> |
|
|
|
<DashboardProjectCard |
|
|
|
{projectsToShow.map((project) => { |
|
|
|
key={project.projectId} |
|
|
|
return ( |
|
|
|
project={project} |
|
|
|
<DashboardProjectCard |
|
|
|
/> |
|
|
|
key={project.projectId} |
|
|
|
); |
|
|
|
project={project} |
|
|
|
})} |
|
|
|
/> |
|
|
|
</> |
|
|
|
); |
|
|
|
)} |
|
|
|
})} |
|
|
|
</div> |
|
|
|
</> |
|
|
|
|
|
|
|
|
|
|
|
{projects.length > MAX_PROJECTS_TO_SHOW && ( |
|
|
|
|
|
|
|
<ShowAllButton |
|
|
|
|
|
|
|
showAll={showAllProjects} |
|
|
|
|
|
|
|
setShowAll={setShowAllProjects} |
|
|
|
|
|
|
|
count={projects.length} |
|
|
|
|
|
|
|
maxCount={MAX_PROJECTS_TO_SHOW} |
|
|
|
|
|
|
|
className="mt-3" |
|
|
|
|
|
|
|
/> |
|
|
|
)} |
|
|
|
)} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{projects.length > MAX_PROJECTS_TO_SHOW && ( |
|
|
|
<div className="h-full rounded-md border bg-white p-4 shadow-sm"> |
|
|
|
<ShowAllButton |
|
|
|
<div className="flex items-center justify-between gap-2"> |
|
|
|
showAll={showAllProjects} |
|
|
|
<h3 className="text-xs uppercase text-gray-500">Bookmarks</h3> |
|
|
|
setShowAll={setShowAllProjects} |
|
|
|
|
|
|
|
count={projects.length} |
|
|
|
|
|
|
|
maxCount={MAX_PROJECTS_TO_SHOW} |
|
|
|
|
|
|
|
className="mt-3" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div className="h-full rounded-md border bg-white p-4 shadow-sm"> |
|
|
|
<a |
|
|
|
<div className="flex items-center justify-between gap-2"> |
|
|
|
href="/roadmaps" |
|
|
|
<h3 className="text-xs uppercase text-gray-500">Bookmarks</h3> |
|
|
|
className="flex items-center gap-1 text-xs text-gray-500" |
|
|
|
|
|
|
|
> |
|
|
|
<a |
|
|
|
<ArrowUpRight size={12} /> |
|
|
|
href="/roadmaps" |
|
|
|
Explore |
|
|
|
className="flex items-center gap-1 text-xs text-gray-500" |
|
|
|
</a> |
|
|
|
> |
|
|
|
</div> |
|
|
|
<ArrowUpRight size={12} /> |
|
|
|
|
|
|
|
Explore |
|
|
|
|
|
|
|
</a> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div className="mt-4 flex flex-col gap-2.5"> |
|
|
|
<div className="mt-4 flex flex-col gap-2.5"> |
|
|
|
{isLoading ? ( |
|
|
|
{isLoading ? ( |
|
|
|
<> |
|
|
|
<> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
<CardSkeleton className="h-5" /> |
|
|
|
</> |
|
|
|
</> |
|
|
|
) : ( |
|
|
|
) : ( |
|
|
|
<> |
|
|
|
<> |
|
|
|
{bookmarksToShow.map((progress) => { |
|
|
|
{bookmarksToShow.map((progress) => { |
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<DashboardBookmarkCard |
|
|
|
<DashboardBookmarkCard |
|
|
|
key={progress.resourceId} |
|
|
|
key={progress.resourceId} |
|
|
|
bookmark={progress} |
|
|
|
bookmark={progress} |
|
|
|
/> |
|
|
|
/> |
|
|
|
); |
|
|
|
); |
|
|
|
})} |
|
|
|
})} |
|
|
|
</> |
|
|
|
</> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{bookmarkedProgresses.length > MAX_BOOKMARKS_TO_SHOW && ( |
|
|
|
|
|
|
|
<ShowAllButton |
|
|
|
|
|
|
|
showAll={showAllBookmarks} |
|
|
|
|
|
|
|
setShowAll={setShowAllBookmarks} |
|
|
|
|
|
|
|
count={bookmarkedProgresses.length} |
|
|
|
|
|
|
|
maxCount={MAX_BOOKMARKS_TO_SHOW} |
|
|
|
|
|
|
|
className="mt-3" |
|
|
|
|
|
|
|
/> |
|
|
|
)} |
|
|
|
)} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{bookmarkedProgresses.length > MAX_BOOKMARKS_TO_SHOW && ( |
|
|
|
|
|
|
|
<ShowAllButton |
|
|
|
|
|
|
|
showAll={showAllBookmarks} |
|
|
|
|
|
|
|
setShowAll={setShowAllBookmarks} |
|
|
|
|
|
|
|
count={bookmarkedProgresses.length} |
|
|
|
|
|
|
|
maxCount={MAX_BOOKMARKS_TO_SHOW} |
|
|
|
|
|
|
|
className="mt-3" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</> |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -229,3 +241,24 @@ function CardSkeleton(props: CardSkeletonProps) { |
|
|
|
/> |
|
|
|
/> |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type StatsCardProps = { |
|
|
|
|
|
|
|
title: string; |
|
|
|
|
|
|
|
value: number; |
|
|
|
|
|
|
|
isLoading?: boolean; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function StatsCard(props: StatsCardProps) { |
|
|
|
|
|
|
|
const { title, value, isLoading = false } = props; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
|
|
<div className="flex flex-col gap-1 rounded-md border bg-white p-4 shadow-sm"> |
|
|
|
|
|
|
|
<h3 className="text-xs uppercase text-gray-500">{title}</h3> |
|
|
|
|
|
|
|
{isLoading ? ( |
|
|
|
|
|
|
|
<CardSkeleton className="h-8" /> |
|
|
|
|
|
|
|
) : ( |
|
|
|
|
|
|
|
<span className="text-2xl font-medium text-black">{value}</span> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|