From 19e300ff891c4c1d8c02c6ae31e9acc51df0ef4a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 23 May 2026 21:00:20 +0000 Subject: [PATCH] Refactor duplicated UI components to address Sonar scan failures - Extract shared `IconLink` component from `ProjectView` and `PublicationView`. - Extract shared `SectionItemCard` component for `CertificationsSection` and `EducationSection`. - Clean up unused imports and verify consistent UI rendering. --- src/components/icon-link.tsx | 31 ++++++++++++++++ src/components/project.tsx | 33 ++--------------- src/components/publication.tsx | 33 ++--------------- src/components/section-item-card.tsx | 41 ++++++++++++++++++++++ src/components/sections/certifications.tsx | 26 +++++--------- src/components/sections/education.tsx | 37 +++++++------------ 6 files changed, 97 insertions(+), 104 deletions(-) create mode 100644 src/components/icon-link.tsx create mode 100644 src/components/section-item-card.tsx diff --git a/src/components/icon-link.tsx b/src/components/icon-link.tsx new file mode 100644 index 0000000..fec5e6c --- /dev/null +++ b/src/components/icon-link.tsx @@ -0,0 +1,31 @@ +"use client"; + +import Link from "next/link"; +import React from "react"; +import { Tooltip } from "@heroui/react"; + +interface IconLinkProps { + href: string; + title: string; + children: React.ReactNode; +} + +export const IconLink = ({ href, title, children }: IconLinkProps) => ( + + + + {children} + + + + + {title} + + +); diff --git a/src/components/project.tsx b/src/components/project.tsx index 028e566..0e1cd6a 100644 --- a/src/components/project.tsx +++ b/src/components/project.tsx @@ -1,7 +1,5 @@ "use client"; -import Link from "next/link"; - import React from "react"; import { FaGithub, @@ -13,39 +11,12 @@ import { FaPersonChalkboard, } from "react-icons/fa6"; -import { Tooltip, Card } from "@heroui/react"; +import { Card } from "@heroui/react"; +import { IconLink } from "@/components/icon-link"; import { Areas, Tools } from "@/components/skills"; import { Project, ProjectLinks } from "@/types"; -const IconLink = ({ - href, - title, - children, -}: { - href: string; - title: string; - children: React.ReactNode; -}) => ( - - - - {children} - - - - - {title} - - -); - const Links = ({ links }: { links: ProjectLinks }) => { const githubLink = links.github ? `https://github.com/${links.github}` : null; diff --git a/src/components/publication.tsx b/src/components/publication.tsx index 97e306e..3e259a0 100644 --- a/src/components/publication.tsx +++ b/src/components/publication.tsx @@ -1,7 +1,5 @@ "use client"; -import Link from "next/link"; - import React from "react"; import { FaFileLines, @@ -11,39 +9,12 @@ import { } from "react-icons/fa6"; import { SiScopus } from "react-icons/si"; -import { Tooltip, Popover, Button, Card } from "@heroui/react"; +import { Popover, Button, Card } from "@heroui/react"; +import { IconLink } from "@/components/icon-link"; import { Areas, Tools } from "@/components/skills"; import { Publication, PublicationLinks } from "@/types"; -const IconLink = ({ - href, - title, - children, -}: { - href: string; - title: string; - children: React.ReactNode; -}) => ( - - - - {children} - - - - - {title} - - -); - const CiteButton = ({ publication }: { publication: Publication }) => { const author = publication.authors[0]; const paperTitle = publication.title; diff --git a/src/components/section-item-card.tsx b/src/components/section-item-card.tsx new file mode 100644 index 0000000..cb05947 --- /dev/null +++ b/src/components/section-item-card.tsx @@ -0,0 +1,41 @@ +"use client"; + +import Image from "next/image"; +import React from "react"; + +interface SectionItemCardProps { + href: string; + image: { + src: string; + alt: string; + }; + title: string; + subtitle: string; + footer: string; +} + +export const SectionItemCard = ({ + href, + image, + title, + subtitle, + footer, +}: SectionItemCardProps) => { + return ( +
+ +
+ {image.alt} +

{title}

+

{subtitle}

+

{footer}

+
+
+
+ ); +}; diff --git a/src/components/sections/certifications.tsx b/src/components/sections/certifications.tsx index 66e9fbc..42ca99b 100644 --- a/src/components/sections/certifications.tsx +++ b/src/components/sections/certifications.tsx @@ -1,9 +1,8 @@ "use client"; -import Image from "next/image"; - import certificationsData from "@/data/certifications"; +import { SectionItemCard } from "../section-item-card"; import { FilterableSection } from "../filterable-section"; export const CertificationsSection = () => { @@ -13,21 +12,14 @@ export const CertificationsSection = () => { title="Certifications" data={certificationsData} renderItem={(certificate) => ( -
- -
- {`Badge -

{certificate.title}

-

{certificate.organization.name}

-

{certificate.date}

-
-
-
+ )} gridClassName="section-body" /> diff --git a/src/components/sections/education.tsx b/src/components/sections/education.tsx index 23e13e8..a8dee91 100644 --- a/src/components/sections/education.tsx +++ b/src/components/sections/education.tsx @@ -1,12 +1,11 @@ "use client"; -import Image from "next/image"; - import { memo } from "react"; import degreesData from "@/data/degrees"; import { Section } from "../section"; +import { SectionItemCard } from "../section-item-card"; // ⚡ Optimization: EducationSection is memoized to prevent unnecessary re-renders. // Since it only depends on static data (degreesData) and doesn't consume filter/search contexts, @@ -15,29 +14,17 @@ export const EducationSection = memo(() => { return (
{degreesData.map((degree) => ( -
- -
- {`${degree.university.name} -

- {degree.title} -

-

- {degree.university.name} -

-

{degree.duration}

-
-
-
+ ))}
);