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>
+  );
+}