Refactor progress stack

feat/dashboard
Kamran Ahmed 3 months ago
parent 10e493de99
commit f51a85ac21
  1. 37
      src/components/Dashboard/PersonalDashboard.tsx
  2. 147
      src/components/Dashboard/ProgressStack.tsx

@ -162,8 +162,6 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${avatar}` ? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${avatar}`
: '/images/default-avatar.png'; : '/images/default-avatar.png';
const currentPeriod = getCurrentPeriod();
const allRoadmapsAndBestPractices = [ const allRoadmapsAndBestPractices = [
...builtInRoleRoadmaps, ...builtInRoleRoadmaps,
...builtInSkillRoadmaps, ...builtInSkillRoadmaps,
@ -221,7 +219,7 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
<div className="h-7 w-1/4 animate-pulse rounded-lg bg-gray-200"></div> <div className="h-7 w-1/4 animate-pulse rounded-lg bg-gray-200"></div>
) : ( ) : (
<h2 className="text-lg font-medium"> <h2 className="text-lg font-medium">
Hi {name}, good {currentPeriod}! Hi {name}, good {getCurrentPeriod()}!
</h2> </h2>
)} )}
@ -235,23 +233,12 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
</> </>
) : ( ) : (
<> <>
<a <DashboardCard
className="overflow-hidden rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50" imgUrl={avatarLink}
title={name!}
description="Setup your profile"
href="/account/update-profile" href="/account/update-profile"
>
<div className="px-4 pb-1.5 pt-3.5">
<img
src={avatarLink}
alt={name}
className="size-8 rounded-full"
/> />
</div>
<div className="flex flex-col gap-0.5 p-4">
<h3 className="truncate font-medium">{name}</h3>
<p className="text-xs">Setup your profile</p>
</div>
</a>
<DashboardCard <DashboardCard
icon={BookEmoji} icon={BookEmoji}
@ -259,6 +246,7 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
description="Visit our Roadmaps" description="Visit our Roadmaps"
href="/roadmaps" href="/roadmaps"
/> />
<DashboardCard <DashboardCard
icon={ConstructionEmoji} icon={ConstructionEmoji}
title="Practice your skills" title="Practice your skills"
@ -302,14 +290,15 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
} }
type DashboardCardProps = { type DashboardCardProps = {
icon: JSXElementConstructor<any>; icon?: JSXElementConstructor<any>;
imgUrl?: string;
title: string; title: string;
description: string; description: string;
href: string; href: string;
}; };
function DashboardCard(props: DashboardCardProps) { function DashboardCard(props: DashboardCardProps) {
const { icon: Icon, title, description, href } = props; const { icon: Icon, imgUrl, title, description, href } = props;
return ( return (
<a <a
@ -317,9 +306,17 @@ function DashboardCard(props: DashboardCardProps) {
target="_blank" target="_blank"
className="flex flex-col overflow-hidden rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50" className="flex flex-col overflow-hidden rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50"
> >
{Icon && (
<div className="px-4 pb-3 pt-4"> <div className="px-4 pb-3 pt-4">
<Icon className="size-6" /> <Icon className="size-6" />
</div> </div>
)}
{imgUrl && (
<div className="px-4 pb-1.5 pt-3.5">
<img src={imgUrl} alt={title} className="size-8 rounded-full" />
</div>
)}
<div className="flex grow flex-col justify-center gap-0.5 p-4"> <div className="flex grow flex-col justify-center gap-0.5 p-4">
<h3 className="truncate font-medium text-black">{title}</h3> <h3 className="truncate font-medium text-black">{title}</h3>

@ -23,6 +23,57 @@ const MAX_PROGRESS_TO_SHOW = 5;
const MAX_PROJECTS_TO_SHOW = 8; const MAX_PROJECTS_TO_SHOW = 8;
const MAX_BOOKMARKS_TO_SHOW = 8; const MAX_BOOKMARKS_TO_SHOW = 8;
type ProgressLaneProps = {
title: string;
linkText?: string;
linkHref?: string;
isLoading?: boolean;
loadingSkeletonCount?: number;
loadingSkeletonClassName?: string;
children: React.ReactNode;
};
function ProgressLane(props: ProgressLaneProps) {
const {
title,
linkText,
linkHref,
isLoading = false,
loadingSkeletonCount = 4,
loadingSkeletonClassName = '',
children,
} = props;
return (
<div className="h-full rounded-md border bg-white p-4 shadow-sm">
<div className="flex items-center justify-between gap-2">
<h3 className="text-xs uppercase text-gray-500">{title}</h3>
{linkText && linkHref && (
<a
href={linkHref}
className="flex items-center gap-1 text-xs text-gray-500"
>
<ArrowUpRight size={12} />
{linkText}
</a>
)}
</div>
<div className="mt-4 flex flex-col gap-2.5">
{isLoading && (
<>
{Array.from({ length: loadingSkeletonCount }).map((_, index) => (
<CardSkeleton key={index} className={loadingSkeletonClassName} />
))}
</>
)}
{!isLoading && children}
</div>
</div>
);
}
export function ProgressStack(props: ProgressStackProps) { export function ProgressStack(props: ProgressStackProps) {
const { progresses, projects, isLoading, accountStreak, topicDoneToday } = const { progresses, projects, isLoading, accountStreak, topicDoneToday } =
props; props;
@ -77,19 +128,12 @@ export function ProgressStack(props: ProgressStackProps) {
</div> </div>
<div className="mt-2 grid min-h-[330px] grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3"> <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"> <ProgressLane
<h3 className="text-xs uppercase text-gray-500">Your Progress</h3> title={'Your Expertise'}
isLoading={isLoading}
<div className="mt-4 flex flex-col gap-2"> loadingSkeletonCount={5}
{isLoading ? ( >
<> {userProgressesToShow.length > 0 && (
<CardSkeleton />
<CardSkeleton />
<CardSkeleton />
<CardSkeleton />
<CardSkeleton />
</>
) : (
<> <>
{userProgressesToShow.map((progress) => { {userProgressesToShow.map((progress) => {
return ( return (
@ -101,7 +145,6 @@ export function ProgressStack(props: ProgressStackProps) {
})} })}
</> </>
)} )}
</div>
{userProgresses.length > MAX_PROGRESS_TO_SHOW && ( {userProgresses.length > MAX_PROGRESS_TO_SHOW && (
<ShowAllButton <ShowAllButton
@ -112,36 +155,19 @@ export function ProgressStack(props: ProgressStackProps) {
className="mt-3" className="mt-3"
/> />
)} )}
</div> </ProgressLane>
<div className="h-full rounded-md border bg-white p-4 shadow-sm"> <ProgressLane
<h3 className="text-xs uppercase text-gray-500">Projects</h3> title={'Projects'}
isLoading={isLoading}
<div className="mt-4 flex flex-col gap-2.5"> loadingSkeletonClassName={'h-5'}
{isLoading ? ( loadingSkeletonCount={8}
<> >
<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) => { {projectsToShow.map((project) => {
return ( return (
<DashboardProjectCard <DashboardProjectCard key={project.projectId} project={project} />
key={project.projectId}
project={project}
/>
); );
})} })}
</>
)}
</div>
{projects.length > MAX_PROJECTS_TO_SHOW && ( {projects.length > MAX_PROJECTS_TO_SHOW && (
<ShowAllButton <ShowAllButton
@ -152,35 +178,16 @@ export function ProgressStack(props: ProgressStackProps) {
className="mt-3" className="mt-3"
/> />
)} )}
</div> </ProgressLane>
<div className="h-full rounded-md border bg-white p-4 shadow-sm"> <ProgressLane
<div className="flex items-center justify-between gap-2"> title={'Bookmarks'}
<h3 className="text-xs uppercase text-gray-500">Bookmarks</h3> isLoading={isLoading}
loadingSkeletonClassName={'h-5'}
<a loadingSkeletonCount={8}
href="/roadmaps" linkHref={'/roadmaps'}
className="flex items-center gap-1 text-xs text-gray-500" linkText={'Explore'}
> >
<ArrowUpRight size={12} />
Explore
</a>
</div>
<div className="mt-4 flex flex-col gap-2.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" />
</>
) : (
<>
{bookmarksToShow.map((progress) => { {bookmarksToShow.map((progress) => {
return ( return (
<DashboardBookmarkCard <DashboardBookmarkCard
@ -189,10 +196,6 @@ export function ProgressStack(props: ProgressStackProps) {
/> />
); );
})} })}
</>
)}
</div>
{bookmarkedProgresses.length > MAX_BOOKMARKS_TO_SHOW && ( {bookmarkedProgresses.length > MAX_BOOKMARKS_TO_SHOW && (
<ShowAllButton <ShowAllButton
showAll={showAllBookmarks} showAll={showAllBookmarks}
@ -202,7 +205,7 @@ export function ProgressStack(props: ProgressStackProps) {
className="mt-3" className="mt-3"
/> />
)} )}
</div> </ProgressLane>
</div> </div>
</> </>
); );
@ -260,7 +263,7 @@ function StatsCard(props: StatsCardProps) {
return ( return (
<div className="flex flex-col gap-1 rounded-md border bg-white p-4 shadow-sm"> <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> <h3 className="mb-1 text-xs uppercase text-gray-500">{title}</h3>
{isLoading ? ( {isLoading ? (
<CardSkeleton className="h-8" /> <CardSkeleton className="h-8" />
) : ( ) : (

Loading…
Cancel
Save