From 0bd4c0af03f87025234f155bfa4f097a9e59d568 Mon Sep 17 00:00:00 2001 From: Kamran Ahmed <kamranahmed.se@gmail.com> Date: Mon, 10 Feb 2025 23:33:43 +0000 Subject: [PATCH] Refactor hero items group --- .../Dashboard/PersonalDashboard.tsx | 6 - .../HeroSection/FavoriteRoadmaps.tsx | 254 +++++++----------- src/components/HeroSection/HeroItemsGroup.tsx | 54 ++++ 3 files changed, 156 insertions(+), 158 deletions(-) create mode 100644 src/components/HeroSection/HeroItemsGroup.tsx diff --git a/src/components/Dashboard/PersonalDashboard.tsx b/src/components/Dashboard/PersonalDashboard.tsx index a6ca134b2..2d8286682 100644 --- a/src/components/Dashboard/PersonalDashboard.tsx +++ b/src/components/Dashboard/PersonalDashboard.tsx @@ -274,12 +274,6 @@ export function PersonalDashboard(props: PersonalDashboardProps) { ? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${avatar}` : '/images/default-avatar.png'; - const allRoadmapsAndBestPractices = [ - ...builtInRoleRoadmaps, - ...builtInSkillRoadmaps, - ...builtInBestPractices, - ]; - const enrichedProjects = personalDashboardDetails?.projects .map((project) => { const projectDetail = projectDetails.find( diff --git a/src/components/HeroSection/FavoriteRoadmaps.tsx b/src/components/HeroSection/FavoriteRoadmaps.tsx index 95b9c631f..a824b458f 100644 --- a/src/components/HeroSection/FavoriteRoadmaps.tsx +++ b/src/components/HeroSection/FavoriteRoadmaps.tsx @@ -7,14 +7,13 @@ import { EyeOff, } from 'lucide-react'; import { useState } from 'react'; -import { cn } from '../../lib/classname.ts'; import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions.tsx'; import { CheckIcon } from '../ReactIcons/CheckIcon.tsx'; import type { UserProgress } from '../TeamProgress/TeamProgressPage.tsx'; import { HeroProject } from './HeroProject'; import { HeroRoadmap } from './HeroRoadmap'; -import { HeroTitle } from './HeroTitle'; import { CreateRoadmapButton } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapButton.tsx'; +import { HeroItemsGroup } from './HeroItemsGroup'; export type AIRoadmapType = { id: string; @@ -58,169 +57,120 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) { return ( <div className="flex flex-col"> - <div - className={cn('', { - 'border-b border-b-slate-800/70 pb-5 pt-5': !isAllCollapsed, - 'py-2': isAllCollapsed, - })} + <HeroItemsGroup + icon={<CheckIcon additionalClasses="mr-1.5 h-[14px] w-[14px]" />} + isLoading={isLoading} + title="Your progress and bookmarks" + isAllCollapsed={isAllCollapsed} > - <div className="container"> - <HeroTitle - icon={ - ( - <CheckIcon additionalClasses="mr-1.5 h-[14px] w-[14px]" /> - ) as any + {progress.map((resource) => ( + <HeroRoadmap + key={`${resource.resourceType}-${resource.resourceId}`} + resourceId={resource.resourceId} + resourceType={resource.resourceType} + resourceTitle={resource.resourceTitle} + isFavorite={resource.isFavorite} + percentageDone={ + ((resource.skipped + resource.done) / resource.total) * 100 + } + url={ + resource.resourceType === 'roadmap' + ? `/${resource.resourceId}` + : `/best-practices/${resource.resourceId}` } - isLoading={isLoading} - title="Your progress and bookmarks" /> - {!isLoading && progress.length > 0 && !isAllCollapsed && ( - <div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3"> - {progress.map((resource) => ( - <HeroRoadmap - key={`${resource.resourceType}-${resource.resourceId}`} - resourceId={resource.resourceId} - resourceType={resource.resourceType} - resourceTitle={resource.resourceTitle} - isFavorite={resource.isFavorite} - percentageDone={ - ((resource.skipped + resource.done) / resource.total) * 100 - } - url={ - resource.resourceType === 'roadmap' - ? `/${resource.resourceId}` - : `/best-practices/${resource.resourceId}` - } - /> - ))} - <CreateRoadmapButton /> - </div> - )} - </div> - </div> + ))} + <CreateRoadmapButton /> + </HeroItemsGroup> - <div - className={cn('', { - 'border-b border-b-slate-800/70 pb-5 pt-5': !isAllCollapsed, - 'py-2': isAllCollapsed, - })} + <HeroItemsGroup + icon={<MapIcon className="mr-1.5 h-[14px] w-[14px]" />} + isLoading={isLoading} + title="Your custom roadmaps" + isAllCollapsed={isAllCollapsed} > - <div className="container"> - <HeroTitle - icon={(<MapIcon className="mr-1.5 h-[14px] w-[14px]" />) as any} - isLoading={isLoading} - title="Your custom roadmaps" + {customRoadmaps.map((customRoadmap) => ( + <HeroRoadmap + key={customRoadmap.resourceId} + resourceId={customRoadmap.resourceId} + resourceType={'roadmap'} + resourceTitle={customRoadmap.resourceTitle} + percentageDone={ + ((customRoadmap.skipped + customRoadmap.done) / + customRoadmap.total) * + 100 + } + url={`/r/${customRoadmap?.roadmapSlug}`} + allowFavorite={false} /> - {!isLoading && customRoadmaps.length > 0 && !isAllCollapsed && ( - <div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3"> - {customRoadmaps.map((customRoadmap) => ( - <HeroRoadmap - key={customRoadmap.resourceId} - resourceId={customRoadmap.resourceId} - resourceType={'roadmap'} - resourceTitle={customRoadmap.resourceTitle} - percentageDone={ - ((customRoadmap.skipped + customRoadmap.done) / - customRoadmap.total) * - 100 - } - url={`/r/${customRoadmap?.roadmapSlug}`} - allowFavorite={false} - /> - ))} - <CreateRoadmapButton /> - </div> - )} - </div> - </div> + ))} + <CreateRoadmapButton /> + </HeroItemsGroup> - <div - className={cn('', { - 'border-b border-b-slate-800/70 pb-5 pt-5': !isAllCollapsed, - 'py-2': isAllCollapsed, - })} + <HeroItemsGroup + icon={<Sparkle className="mr-1.5 h-[14px] w-[14px]" />} + isLoading={isLoading} + title="Your AI roadmaps" + isAllCollapsed={isAllCollapsed} > - <div className="container"> - <HeroTitle - icon={(<Sparkle className="mr-1.5 h-[14px] w-[14px]" />) as any} - isLoading={isLoading} - title="Your AI roadmaps" + {aiRoadmaps.map((aiRoadmap) => ( + <HeroRoadmap + key={aiRoadmap.id} + resourceId={aiRoadmap.id} + resourceType={'roadmap'} + resourceTitle={aiRoadmap.title} + url={`/ai/${aiRoadmap.slug}`} + percentageDone={0} + allowFavorite={false} + isTrackable={false} /> - {!isLoading && aiRoadmaps.length > 0 && !isAllCollapsed && ( - <div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3"> - {aiRoadmaps.map((aiRoadmap) => ( - <HeroRoadmap - key={aiRoadmap.id} - resourceId={aiRoadmap.id} - resourceType={'roadmap'} - resourceTitle={aiRoadmap.title} - url={`/ai/${aiRoadmap.slug}`} - percentageDone={0} - allowFavorite={false} - isTrackable={false} - /> - ))} + ))} - <a - href="/ai" - className={ - 'flex h-full w-full items-center justify-center gap-1 overflow-hidden rounded-md border border-dashed border-gray-800 p-3 text-sm text-gray-400 hover:border-gray-600 hover:bg-gray-900 hover:text-gray-300' - } - > - <Plus size={16} /> - Generate New - </a> - </div> - )} - </div> - </div> + <a + href="/ai" + className={ + 'flex h-full w-full items-center justify-center gap-1 overflow-hidden rounded-md border border-dashed border-gray-800 p-3 text-sm text-gray-400 hover:border-gray-600 hover:bg-gray-900 hover:text-gray-300' + } + > + <Plus size={16} /> + Generate New + </a> + </HeroItemsGroup> - <div - className={cn('', { - 'border-b border-b-slate-800/70 pb-5 pt-5': !isAllCollapsed, - 'py-2': isAllCollapsed, - })} + <HeroItemsGroup + icon={<FolderKanban className="mr-1.5 h-[14px] w-[14px]" />} + isLoading={isLoading} + title="Your active projects" + isAllCollapsed={isAllCollapsed} + rightContent={ + completedProjects.length > 0 && ( + <button + onClick={() => setShowCompleted(!showCompleted)} + className="flex items-center gap-2 rounded-md px-2 py-1 text-xs text-slate-400 hover:text-slate-300" + > + {showCompleted ? ( + <EyeOff className="h-3.5 w-3.5" /> + ) : ( + <Eye className="h-3.5 w-3.5" /> + )} + {completedProjects.length} Completed + </button> + ) + } + className="border-b-0" > - <div className="container"> - <HeroTitle - icon={ - (<FolderKanban className="mr-1.5 h-[14px] w-[14px]" />) as any - } - isLoading={isLoading} - title="Your active projects" - rightContent={ - completedProjects.length > 0 && ( - <button - onClick={() => setShowCompleted(!showCompleted)} - className="flex items-center gap-2 rounded-md px-2 py-1 text-xs text-slate-400 hover:text-slate-300" - > - {showCompleted ? ( - <EyeOff className="h-3.5 w-3.5" /> - ) : ( - <Eye className="h-3.5 w-3.5" /> - )} - {completedProjects.length} Completed - </button> - ) - } - /> - {!isLoading && projectsToShow.length > 0 && !isAllCollapsed && ( - <div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3"> - {projectsToShow.map((project) => ( - <HeroProject key={project._id} project={project} /> - ))} + {projectsToShow.map((project) => ( + <HeroProject key={project._id} project={project} /> + ))} - <a - href="/projects" - className="flex min-h-[80px] items-center justify-center gap-2 rounded-md border border-dashed border-slate-800 p-4 text-sm text-slate-400 hover:border-slate-600 hover:bg-slate-900/50 hover:text-slate-300" - > - <Plus size={16} /> - Start a new project - </a> - </div> - )} - </div> - </div> + <a + href="/projects" + className="flex min-h-[80px] items-center justify-center gap-2 rounded-md border border-dashed border-slate-800 p-4 text-sm text-slate-400 hover:border-slate-600 hover:bg-slate-900/50 hover:text-slate-300" + > + <Plus size={16} /> + Start a new project + </a> + </HeroItemsGroup> </div> ); } diff --git a/src/components/HeroSection/HeroItemsGroup.tsx b/src/components/HeroSection/HeroItemsGroup.tsx new file mode 100644 index 000000000..45e27887d --- /dev/null +++ b/src/components/HeroSection/HeroItemsGroup.tsx @@ -0,0 +1,54 @@ +import type { ReactNode } from 'react'; +import { cn } from '../../lib/classname'; +import { HeroTitle } from './HeroTitle'; + +type HeroItemsGroupProps = { + icon: any; + isLoading?: boolean; + title: string | ReactNode; + rightContent?: ReactNode; + isAllCollapsed?: boolean; + children?: ReactNode; + className?: string; +}; + +export function HeroItemsGroup(props: HeroItemsGroupProps) { + const { + icon, + isLoading = false, + title, + rightContent, + isAllCollapsed = false, + children, + className, + } = props; + + const isCollapsed = isAllCollapsed || isLoading; + + return ( + <div + className={cn( + '', + { + 'border-b border-b-slate-800/70 pb-5 pt-5': !isCollapsed, + 'py-2': isCollapsed, + }, + className, + )} + > + <div className="container"> + <HeroTitle + icon={icon} + isLoading={isLoading} + title={title} + rightContent={rightContent} + /> + {!isLoading && !isAllCollapsed && ( + <div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3"> + {children} + </div> + )} + </div> + </div> + ); +}