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