diff --git a/src/assets/styles/Project.module.scss b/src/assets/styles/Project.module.scss
index e4b1c75..d66485a 100644
--- a/src/assets/styles/Project.module.scss
+++ b/src/assets/styles/Project.module.scss
@@ -42,7 +42,7 @@
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
-.cardImg-top {
+.cardImgTop {
height: 200px;
object-fit: cover;
}
@@ -68,10 +68,18 @@
}
/* Category badges */
-.categoryBadge {
+.categoryBadgeContainer {
position: absolute;
top: 1rem;
right: 1rem;
+
+ display: flex;
+ flex-direction: row-reverse;
+ gap: 0.5rem;
+ z-index: 1;
+}
+
+.categoryBadge {
background-color: rgba(139, 0, 0, 0.9);
color: $text-light;
padding: 0.25rem 0.75rem;
@@ -80,7 +88,6 @@
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
- z-index: 1;
}
.featuredBadge {
diff --git a/src/components/ProjectCard.tsx b/src/components/ProjectCard.tsx
index fc339ca..4d92450 100644
--- a/src/components/ProjectCard.tsx
+++ b/src/components/ProjectCard.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react'
+import React, { useEffect, useState } from 'react'
import style from '@styles/Project.module.scss'
@@ -12,7 +12,7 @@ export interface ProjectProps {
title: string;
description: string;
image: string;
- category: string[];
+ category: Category[];
featured?: boolean;
technologies: ProjectTech[];
links: {
@@ -21,71 +21,146 @@ export interface ProjectProps {
};
}
+export interface Category {
+ [key: string]: string;
+ fullName: string;
+ shortName: string;
+ icon: string;
+}
+
interface ProjectCardProps {
project: ProjectProps;
onOpenDetails: (id: string) => void;
}
+const ProjectCardTech: React.FC<{ technology: ProjectTech[] }> = ({ technology }) => (
+
+
// TECHNOLOGIES_USED
+
+ {technology.map((tech, index) => (
+
+
+ {tech.name}
+
+ ))}
+
+
+)
+
+const CategoryBadge: React.FC<{ category: Category[] }> = ({ category }) => (
+ < div className={style.categoryBadgeContainer} >
+ {
+ category.map((cat, index) => (
+
+ {cat.shortName}
+
+ ))}
+
+)
+
+const ProjectLinks: React.FC<{ links: { github?: string; demo?: string }, id: string, onOpenDetails: (id: string) => void }> = ({ links, id, onOpenDetails }) => (
+
+)
+
export const ProjectCard: React.FC = ({ project, onOpenDetails }) => {
const [, setHovered] = useState(false);
-
- return (
- setHovered(true)}
- onMouseLeave={() => setHovered(false)}
- >
-
-

-
{project.category[0]}
- {project.featured && (
-
-
- Featured
-
- )}
-
-
-
{project.title}
-
- {project.description}
-
-
-
// TECHNOLOGIES_USED
-
- {project.technologies.map((tech, index) => (
-
-
- {tech.name}
-
- ))}
+
+ useEffect(() => {
+ const createEmbers = () => {
+ const embersContainer = document.querySelectorAll(`.${style.cardImgTop}`);
+
+ embersContainer.forEach((container) => {
+ for (let i = 0; i < 5; i++) {
+ const ember = document.createElement('div');
+ ember.className = style.emberParticle;
+
+ ember.style.left = `${Math.random() * 100}%`;
+ ember.style.bottom = '0';
+ ember.style.animationDuration = `${Math.random() * 2 + 3}s`;
+ ember.style.animationDelay = `${Math.random() * 3}s`;
+ container.appendChild(ember);
+ }
+ });
+ };
+ createEmbers();
+ }, []);
+
+ if (project.featured) {
+ return (
+
+
setHovered(true)}
+ onMouseLeave={() => setHovered(false)}
+ >
+
+
+

+
+
+ Featured
+
+
+
+
+
+
{project.title}
+
+ {project.description}
+
+
+
+
+
-
-
-
- {project.links.github && (
-
-
- View Code
-
- )}
-
- {project.links.demo && (
-
-
- Live Demo
-
- )}
+
+ )
+ }
+
+
+ return (
+
+
setHovered(true)}
+ onMouseLeave={() => setHovered(false)}>
+
+

+
+
+
+
{project.title}
+
+ {project.description}
+
+
+
)
-}
\ No newline at end of file
+};
diff --git a/src/data/projectsData.ts b/src/data/projectsData.ts
index 4132324..aa6a8ae 100644
--- a/src/data/projectsData.ts
+++ b/src/data/projectsData.ts
@@ -1,4 +1,33 @@
-import type { ProjectProps } from "../components/ProjectCard";
+import type { ProjectProps, Category } from "../components/ProjectCard";
+
+type CategoryMap = Record
+export const Categories: CategoryMap = {
+ Embedded: {
+ fullName: "Embedded Systems",
+ shortName: "Embedded",
+ icon: "fas fa-microchip",
+ },
+ Web: {
+ fullName: "Web Development",
+ shortName: "Web",
+ icon: "fas fa-code",
+ },
+ ML: {
+ fullName: "Machine Learning",
+ shortName: "ML",
+ icon: "fas fa-robot",
+ },
+ IoT: {
+ fullName: "Internet of Things",
+ shortName: "IoT",
+ icon: "fas fa-network-wired",
+ },
+ Sec: {
+ fullName: "Cybersecurity",
+ shortName: "Security",
+ icon: "fas fa-shield-alt",
+ },
+}
export const projectsData: ProjectProps[] = [
{
@@ -6,7 +35,7 @@ export const projectsData: ProjectProps[] = [
title: "ShadowGuardian: Secure IoT Monitoring System",
description: "A comprehensive security monitoring system built on STM32 microcontrollers with end-to-end encryption and low-power operation. Integrates with home automation systems and provides real-time alerts through a mobile application.",
image: "/images/projects/shadowguardian.jpg",
- category: ["iot", "embedded"],
+ category: [Categories["IoT"], Categories["Embedded"]],
featured: true,
technologies: [
{ name: "C++", icon: "devicon-cplusplus-plain tech-icon" },
@@ -25,7 +54,7 @@ export const projectsData: ProjectProps[] = [
title: "PyroSight: Thermal Anomaly Detection",
description: "An intelligent system that uses computer vision and thermal imaging to detect anomalies in industrial equipment. Employs PyTorch for deep learning to predict potential failures before they occur.",
image: "/images/projects/pyrosight.jpg",
- category: ["ml", "iot"],
+ category: [Categories["ML"], Categories["IoT"]],
technologies: [
{ name: "Python", icon: "devicon-python-plain tech-icon" },
{ name: "PyTorch", icon: "devicon-pytorch-original tech-icon" },
@@ -41,7 +70,7 @@ export const projectsData: ProjectProps[] = [
title: "NexusFlow: DevOps Dashboard",
description: "A real-time monitoring dashboard for DevOps teams that visualizes CI/CD pipelines, infrastructure metrics, and deployment statuses. Built with Blazor for seamless updates without page refreshes.",
image: "/images/projects/nexusflow.jpg",
- category: ["web"],
+ category: [Categories["Web"]],
technologies: [
{ name: "C#", icon: "devicon-csharp-plain tech-icon" },
{ name: "Blazor", icon: "devicon-dotnetcore-plain tech-icon" },
@@ -58,7 +87,7 @@ export const projectsData: ProjectProps[] = [
title: "EtherWhisper: Secure Communication Node",
description: "A low-power, long-range communication node for IoT networks with military-grade encryption and mesh capabilities. Perfect for remote sensing applications in harsh environments.",
image: "/images/projects/etherwhisper.jpg",
- category: ["embedded", "iot"],
+ category: [Categories["Embedded"], Categories["IoT"]],
technologies: [
{ name: "C", icon: "devicon-c-plain tech-icon" },
{ name: "STM32", icon: "fas fa-memory tech-icon" },
@@ -74,7 +103,7 @@ export const projectsData: ProjectProps[] = [
title: "DataForge: Advanced Analytics Platform",
description: "A comprehensive data analytics platform with interactive visualization tools and automated reporting features. Integrates with various data sources and provides ML-powered insights.",
image: "/images/projects/dataforge.jpg",
- category: ["web"],
+ category: [Categories["Web"]],
technologies: [
{ name: "Python", icon: "devicon-python-plain tech-icon" },
{ name: "Django", icon: "devicon-django-plain tech-icon" },
@@ -91,7 +120,7 @@ export const projectsData: ProjectProps[] = [
title: "VisionKeeper: Object Recognition System",
description: "A real-time object recognition system optimized for edge devices. Capable of identifying and tracking objects with high accuracy even in challenging lighting conditions.",
image: "/images/projects/visionkeeper.jpg",
- category: ["ml"],
+ category: [Categories["ML"]],
technologies: [
{ name: "Python", icon: "devicon-python-plain tech-icon" },
{ name: "PyTorch", icon: "devicon-pytorch-original tech-icon" },
diff --git a/src/pages/Projects.tsx b/src/pages/Projects.tsx
index faf2fa0..d59354f 100644
--- a/src/pages/Projects.tsx
+++ b/src/pages/Projects.tsx
@@ -2,88 +2,78 @@ import { useState } from 'react';
import { PageHeader } from '../components/PageHeader';
import { ProjectCard, type ProjectProps } from '../components/ProjectCard';
import { ProjectModal } from '../components/ProjectModal';
-import { projectsData } from '../data/projectsData';
+import { projectsData, Categories } from '../data/projectsData';
+import { styleText } from 'util';
+
+import style from '@styles/Project.module.scss'
+
export const Projects = () => {
const [activeFilter, setActiveFilter] = useState('all');
const [selectedProject, setSelectedProject] = useState(null);
-
- const filteredProjects = activeFilter === 'all'
- ? projectsData
- : projectsData.filter(project => project.category.includes(activeFilter));
-
+
+ const filteredProjects = activeFilter === 'all'
+ ? projectsData
+ : projectsData.filter(project => project.category.some(cat => cat.shortName.toLowerCase() === activeFilter));
+
+ const categories = Categories;
+
const featuredProject = projectsData.find(project => project.featured);
-
+
const regularProjects = filteredProjects.filter(project => !project.featured);
-
+
const handleFilterClick = (filter: string) => {
setActiveFilter(filter);
};
-
+
const handleOpenDetails = (id: string) => {
const project = projectsData.find(p => p.id === id);
if (project) {
setSelectedProject(project);
}
};
-
+
const handleCloseDetails = () => {
setSelectedProject(null);
};
-
+
return (
<>
-
-
+
{/* Projects Filter */}
-
-
- {featuredProject && (activeFilter === 'all' || featuredProject.category.includes(activeFilter)) && (
+ {featuredProject && (activeFilter === 'all' || featuredProject.category.some(cat => cat.shortName.toLowerCase() === activeFilter)) && (
)}
{regularProjects.map(project => (
-
+
+
))}
@@ -99,7 +89,7 @@ export const Projects = () => {
-
+
{selectedProject && (
)}