Fix-projects #8

Merged
seccodesmith merged 6 commits from Fix-projects into main 2025-05-26 20:40:51 +00:00
11 changed files with 834 additions and 226 deletions
+1 -1
View File
@@ -73,7 +73,7 @@
color: $accent-fire; color: $accent-fire;
font-size: 1rem; font-size: 1rem;
animation: fall linear infinite; animation: fall linear infinite;
opacity: 0.8; opacity: 0.9;
} }
@keyframes fall { @keyframes fall {
+378
View File
@@ -0,0 +1,378 @@
@use './variables' as *;
.filterContainer {
margin-bottom: 2rem;
}
.filterButton {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid $card-border;
color: $text-ash;
padding: 0.5rem 1rem;
margin: 0 0.25rem 0.5rem;
border-radius: 4px;
transition: all 0.3s ease;
}
.filterButton:hover,
.filterButton.active {
background-color: rgba(255, 100, 0, 0.1);
border-color: rgba(255, 100, 0, 0.3);
color: $accent-fire;
}
.filterButton.active {
border-color: $accent-fire;
}
/* Project Cards */
.card {
background-color: $card-bg;
border: 1px solid $card-border;
border-radius: 8px;
transition: all 0.3s ease;
margin-bottom: 1.5rem;
overflow: hidden;
height: 100%;
}
.card:hover {
transform: translateY(-10px);
border-color: rgba(255, 100, 0, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.cardImgTop {
height: 200px;
object-fit: cover;
}
.cardTitle {
font-size: 1.5rem;
margin-bottom: 0.75rem;
}
.cardText {
color: $text-ash;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
margin-bottom: 1.5rem;
}
.featuredCard {
position: relative;
}
.featuredCard .cardImgTop {
height: 300px;
}
/* Category badges */
.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;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
}
.featuredBadge {
position: absolute;
top: 1rem;
left: 1rem;
background: linear-gradient(45deg, $accent-dragon, $accent-fire);
color: $text-light;
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
display: flex;
align-items: center;
gap: 5px;
z-index: 1;
}
/* Tech stack */
.techTitle {
font-size: 0.9rem;
margin-bottom: 0.625rem;
color: $text-ash;
font-family: 'Fira Code', monospace;
}
.techStack {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 1.5rem;
}
.techItem {
display: inline-flex;
align-items: center;
gap: 6px;
background-color: rgba(255, 255, 255, 0.05);
padding: 5px 10px;
border-radius: 4px;
font-size: 0.8rem;
font-family: 'Fira Code', monospace;
color: $text-ash;
transition: all 0.3s ease;
}
.techItem:hover {
background-color: rgba(255, 100, 0, 0.1);
color: $accent-fire;
transform: translateY(-2px);
}
.techIcon {
font-size: 1rem;
color: $accent-fire;
}
/* Project links */
.btnOutlineSecondary {
background-color: rgba(255, 255, 255, 0.05);
border-color: $card-border;
color: $text-light;
}
.btnOutlineSecondary:hover {
background-color: rgba(255, 100, 0, 0.1);
border-color: $accent-fire;
color: $text-light;
}
.btnIcon {
margin-right: 0.5rem;
color: $accent-fire;
}
/* View All Button */
.btnPrimary {
background: linear-gradient(45deg, $accent-dragon, $accent-fire);
border: none;
padding: 0.75rem 2rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 4px 15px rgba(139, 0, 0, 0.3);
transition: all 0.3s ease;
}
.btnPrimary:hover {
transform: translateY(-3px);
box-shadow: 0 7px 25px rgba(139, 0, 0, 0.5);
background-color: $accent-fire;
background-image: none;
}
/* Modal Styling */
.modalContent {
background-color: $card-bg;
border: 1px solid $card-border;
color: $text-light;
}
.modalHeader {
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.modalTitle {
color: $text-light;
}
.modalFooter {
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.btnClose {
filter: invert(1) grayscale(100%) brightness(200%);
}
.featuresList {
padding-left: 1.5rem;
color: $text-ash;
}
.featuresList li {
margin-bottom: 0.5rem;
position: relative;
}
.featuresList li::before {
content: "";
color: $accent-fire;
position: absolute;
left: -1rem;
top: 0;
font-size: 1.2rem;
}
.techChip {
display: inline-flex;
align-items: center;
gap: 6px;
background-color: rgba(255, 255, 255, 0.05);
padding: 8px 15px;
border-radius: 20px;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
transition: all 0.3s ease;
}
.techChip:hover {
background-color: rgba(255, 100, 0, 0.1);
transform: translateY(-3px);
}
.techChip-icon {
color: $accent-fire;
font-size: 1.1rem;
}
.techChip-name {
color: $text-ash;
font-size: 0.9rem;
font-family: 'Fira Code', monospace;
}
.galleryItem {
height: 150px;
border-radius: 8px;
overflow: hidden;
transition: all 0.3s ease;
cursor: pointer;
margin-bottom: 15px;
}
.galleryItem:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.galleryImage {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Binary background effect */
.binaryLine {
position: absolute;
font-family: 'Fira Code', monospace;
color: $accent-fire;
font-size: 0.8rem;
opacity: 0.15;
pointer-events: none;
}
/* Ember particle effect */
.emberParticle {
position: absolute;
width: 3px;
height: 3px;
background-color: $accent-fire;
border-radius: 50%;
opacity: 0.6;
pointer-events: none;
z-index: 2;
box-shadow: 0 0 5px $accent-fire;
animation: float 3s linear infinite;
}
@keyframes float {
0% {
transform: translateY(0) rotate(0deg);
opacity: 0.6;
}
100% {
transform: translateY(-100px) rotate(360deg);
opacity: 0;
}
}
/* Footer */
.footer {
background-color: $primary-dark;
padding: 3rem 0;
border-top: 1px solid rgba(255, 100, 0, 0.2);
margin-top: 3rem;
}
.socialLinks {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 1.5rem;
}
.socialIcon {
font-size: 1.5rem;
color: $text-ash;
transition: all 0.3s ease;
}
.socialIcon:hover {
color: $accent-fire;
transform: translateY(-5px);
}
.copyright {
color: $text-ash;
font-size: 0.9rem;
text-align: center;
}
/* Responsive adjustments */
@media (max-width: 992px) {
.pageTitle {
font-size: 2.5rem;
}
.featuredCard .row {
flex-direction: column;
}
}
@media (max-width: 768px) {
.pageTitle {
font-size: 2rem;
}
.techStack {
justify-content: center;
}
}
@media (max-width: 576px) {
.pageTitle {
font-size: 1.8rem;
}
.projectLinks {
flex-direction: column;
}
.projectLinks .btn {
margin-bottom: 0.5rem;
}
}
+2 -16
View File
@@ -1,21 +1,7 @@
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import styles from '@styles/BlogCard.module.scss'; import styles from '@styles/BlogCard.module.scss';
export interface BlogPostProps { import type {BlogPostProps} from '../untils/BlogPostProps';
id: string;
slug: string;
title: string;
excerpt: string;
image: string;
category: string;
date: string;
author: string;
commentCount: number;
readTime: string;
featured?: boolean;
tags: string[];
content: string;
}
interface BlogCardProps { interface BlogCardProps {
post: BlogPostProps; post: BlogPostProps;
@@ -26,7 +12,7 @@ export const BlogCard: React.FC<BlogCardProps> = ({ post }) => {
<div className={`card ${styles.card} ${post.featured ? styles.featuredCard : ''}`}> <div className={`card ${styles.card} ${post.featured ? styles.featuredCard : ''}`}>
<div className="position-relative"> <div className="position-relative">
<img src={post.image} className={`card-img-top ${post.featured ? styles.featuredImg : ''}`} alt={post.title} /> <img src={post.image} className={`card-img-top ${post.featured ? styles.featuredImg : ''}`} alt={post.title} />
<div className="category-badge">{post.category}</div> <div className={styles.categoryBadge}>{post.category}</div>
{post.featured && ( {post.featured && (
<div className={`featured-badge ${styles.featuredBadge}`}> <div className={`featured-badge ${styles.featuredBadge}`}>
+113 -62
View File
@@ -1,90 +1,141 @@
import { useState } from 'react' import React, { useEffect, useState } from 'react'
export interface ProjectTech { import style from '@styles/Project.module.scss'
name: string; import type {ProjectTech, Category, ProjectProps } from '../untils/ProjectProps'
icon: string;
}
export interface ProjectProps {
id: string;
title: string;
description: string;
image: string;
category: string[];
featured?: boolean;
technologies: ProjectTech[];
links: {
github?: string;
demo?: string;
};
}
interface ProjectCardProps { interface ProjectCardProps {
project: ProjectProps; project: ProjectProps;
onOpenDetails: (id: string) => void; onOpenDetails: (id: string) => void;
} }
export const ProjectCard: React.FC<ProjectCardProps> = ({ project, onOpenDetails }) => { const ProjectCardTech: React.FC<{ technology: ProjectTech[] }> = ({ technology }) => (
const [, setHovered] = useState(false);
return (
<div
className={`card h-100 ${project.featured ? 'featured-card' : ''}`}
data-category={project.category.join(',')}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<div className="position-relative">
<img src={project.image} className="card-img-top" alt={project.title} />
<div className="category-badge">{project.category[0]}</div>
{project.featured && (
<div className="featured-badge">
<i className="fas fa-star"></i>
<span>Featured</span>
</div>
)}
</div>
<div className="card-body d-flex flex-column">
<h3 className="card-title">{project.title}</h3>
<p className="card-text">
{project.description}
</p>
<div> <div>
<h4 className="tech-title">// TECHNOLOGIES_USED</h4> <h4 className={style.techTitle}>// TECHNOLOGIES_USED</h4>
<div className="tech-stack"> <div className={style.techStack}>
{project.technologies.map((tech, index) => ( {technology.map((tech, index) => (
<span className="tech-item" key={index}> <span className={`${style.techItem}`} key={index}>
<i className={tech.icon}></i> <i className={`${tech.icon} ${style.techIcon}`}></i>
<span>{tech.name}</span> <span>{tech.name}</span>
</span> </span>
))} ))}
</div> </div>
</div> </div>
<div className="mt-auto d-flex gap-2 flex-wrap project-links"> )
const CategoryBadge: React.FC<{ category: Category[] }> = ({ category }) => (
< div className={style.categoryBadgeContainer} >
{
category.map((cat, index) => (
<span key={index} className={`${style.categoryBadge}`}>
{cat.shortName}
</span>
))}
</div>
)
const ProjectLinks: React.FC<{ links: { github?: string; demo?: string }, id: string, onOpenDetails: (id: string) => void }> = ({ links, id, onOpenDetails }) => (
<div className={`mt-auto d-flex gap-2 flex-wrap project-links ${style.projectLinks}`}>
<button <button
className="btn btn-outline-secondary" className={`btn btn-outline-secondary ${style.btnOutlineSecondary}`}
onClick={() => onOpenDetails(project.id)} onClick={() => onOpenDetails(id)}
> data-bs-toggle="modal" data-bs-target="#shadowguardianModal">
<i className="fas fa-info-circle me-2"></i> <i className={`fas fa-info-circle ${style.btnIcon}`} ></i>
Project Details Project Details
</button> </button>
{project.links.github && ( {links.github && (
<a href={project.links.github} className="btn btn-outline-secondary"> <a href={links.github} className={`btn btn-outline-secondary ${style.btnOutlineSecondary} `}>
<i className="fab fa-github me-2"></i> <i className={`fab fa-github ${style.btnIcon}`} ></i>
View Code View Code
</a> </a>
)} )}
{project.links.demo && ( {links.demo && (
<a href={project.links.demo} className="btn btn-outline-secondary"> <a href={links.demo} className={`btn btn-outline-secondary ${style.btnOutlineSecondary}`}>
<i className="fas fa-external-link-alt me-2"></i> <i className={`fas fa-external-link-alt ${style.btnIcon}`}></i>
Live Demo Live Demo
</a> </a>
)} )}
</div> </div>
)
export const ProjectCard: React.FC<ProjectCardProps> = ({ project, onOpenDetails }) => {
const [, setHovered] = useState(false);
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 (
<div className={`"col-12 mb-4`}>
<div
className={`card ${style.card} ${style.featuredCard}`}
data-category={project.category.join(',')}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<div className={`row g-0`}>
<div className={`col-md-6 position-relative`}>
<img src={project.image} className={`img-fluid rounded-start h-100 w-100 object-fit-cover`} alt={project.title} />
<div className={style.featuredBadge}>
<i className="fas fa-star"></i>
<span>Featured</span>
</div>
<CategoryBadge category={project.category} />
</div>
<div className={`col-md-6`}>
<div className={`card-body d-flex flex-column h-100 ${style.cardBody}`}>
<h3 className={`${style.cardTitle} card-title`}>{project.title}</h3>
<p className={`${style.cardText} card-text`}>
{project.description}
</p>
<ProjectCardTech technology={project.technologies} />
<ProjectLinks links={project.links} id={project.id} onOpenDetails={onOpenDetails} />
</div>
</div>
</div>
</div> </div>
</div> </div>
) )
} }
return (
<div className={`col-md-6 col-lg-4 mb-4`}>
<div className={`card ${style.card} h-100`}
data-category={project.category.join(',')}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}>
<div className={`position-relative`}>
<img src={project.image} className={style.cardImgTop} alt={project.title} />
<CategoryBadge category={project.category} />
</div>
<div className={`card-body d-flex flex-column ${style.cardBody}`}>
<h3 className={`${style.cardTitle} card-title`}>{project.title}</h3>
<p className={`${style.cardText} card-text`}>
{project.description}
</p>
<ProjectCardTech technology={project.technologies} />
<ProjectLinks links={project.links} id={project.id} onOpenDetails={onOpenDetails} />
</div>
</div>
</div>
)
};
+125 -23
View File
@@ -1,56 +1,158 @@
import type { ProjectProps } from './ProjectCard'; import type { ProjectProps } from '../untils/ProjectProps';
import style from '@styles/Project.module.scss'
interface ProjectModalProps { interface ProjectModalProps {
project: ProjectProps | null; project: ProjectProps | null;
onClose: () => void;
} }
export const ProjectModal: React.FC<ProjectModalProps> = ({ project, onClose }) => { export const ProjectModal: React.FC<ProjectModalProps> = ({ project }) => {
if (!project) return null; if (!project) return null;
return ( return (
<div className="modal fade show" style={{ display: 'block' }} tabIndex={-1}> <div className="modal fade" id="shadowguardianModal" tabIndex={-1} aria-labelledby="shadowguardianModalLabel" aria-hidden="true">
<div className="modal-dialog modal-xl modal-dialog-scrollable"> <div className="modal-dialog modal-xl modal-dialog-scrollable">
<div className="modal-content"> <div className={`modal-content ${style.modalContent}`}>
<div className="modal-header"> <div className={`modal-header ${style.modalHeader}`}>
<h5 className="modal-title">{project.title}</h5> <h5 className={style.modalHeader} id="shadowguardianModalLabel">
<button type="button" className="btn-close" onClick={onClose}></button> {project.title}
</h5>
<button type="button" className={`btn-close ${style.btnClose}`} data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div className="modal-body"> <div className={`modal-body ${style.modalBody}`}>
<div className="mb-4"> <div className="mb-4">
<img src={project.image} className="img-fluid rounded mb-3" alt={project.title} /> <img src={project.image} className="img-fluid rounded mb-3" alt={project.title} />
<div className={`row ${style.row}`}>
<div className="col-md-8">
{
project.projectDetails ? project.projectDetails.descriptions?.map((desc, index) => (
<p key={index} className="mb-3">{desc}</p>
)) :
<p>{project.description}</p>
}
</div>
<div className="col-md-4">
<div className={`card h-100 ${style.card}`}>
<div className={`card-header ${style.cardHeader}`}>
<h5 className="mb-0">Project Overview</h5>
</div>
<div className="card-body">
{project.projectDetails && project.projectDetails.startDate && project.projectDetails.endDate ? (
<p><strong>Duration:</strong> {new Date(project.projectDetails.startDate).toLocaleDateString()} - {new Date(project.projectDetails.endDate).toLocaleDateString()}</p>
) : (
<p><strong>Duration:</strong> Ongoing</p>
)
}
{
project.projectDetails && project.projectDetails.role ? (
<p><strong>Role:</strong> {project.projectDetails.role}</p>
) : (
<p><strong>Role:</strong> Developer</p>
)
}
{
project.projectDetails && project.projectDetails.client ? (
<p><strong>Client:</strong> {project.projectDetails.client}</p>
) : (
<p><strong>Client:</strong> Internal Project</p>
)
}
</div>
</div>
</div>
</div> </div>
<p>{project.description}</p>
{
project.projectDetails?.keyFeatures && project.projectDetails.keyFeatures.length > 0 && (
<>
<h4 className="mt-4 mb-3">Key Features</h4>
{
project.projectDetails?.keyFeatures.map((feature, index) => (
<p key={index} className="mb-2">
<i className="fas fa-check-circle text-success me-2"></i>
{feature}
</p>
))
}
</>
)
}
<h4 className="mt-4 mb-3">Technologies Used</h4> <h4 className="mt-4 mb-3">Technologies Used</h4>
<div className="mb-4"> <div className="mb-4"></div>
{project.technologies.map((tech, index) => ( {
<span className="tech-chip" key={index}> project.projectDetails?.fullTechStack && project.projectDetails?.fullTechStack?.length > 0 ? (
<i className={`${tech.icon} tech-chip-icon`}></i> <>
{project.projectDetails.fullTechStack.map((tech, index) => (
<span key={index} className={`tech-chip ${style.techChip}`}>
<i className={`${tech.icons} tech-chip-icon`}></i>
<span className="tech-chip-name">{tech.name}</span> <span className="tech-chip-name">{tech.name}</span>
</span> </span>
))} ))}
</>
) : (
<>
{project.technologies && project.technologies.length > 0 ? (
project.technologies.map((tech, index) => (
<span key={index} className={`tech-chip ${style.techChip}`}>
<i className={`${tech.icon} tech-chip-icon`}></i>
<span className="tech-chip-name">{tech.name}</span>
</span>
))
) : (
<p className="text-muted">No technologies listed.</p>
)
}
</>
)
}
</div>
{
project.projectDetails?.gallery && project.projectDetails?.gallery.length > 0 && (
<>
<h4 className="mt-4 mb-3">Gallery</h4>
<div className="row">
{
project.projectDetails.gallery.map((image, index) => (
<div className="col-md-4 col-6">
<div key={index} className={`gallery-item ${style.galleryItem}`}>
<img src={image} alt={`Gallery Image ${index + 1}`} className={`gallery-image ${style.galleryImage}`}></img>
</div> </div>
</div> </div>
<div className="modal-footer"> ))
}
</div>
</>
)
}
</div>
<div className={`modal-footer ${style.modalFooter}`}>
{project.links.github && ( {project.links.github && (
<a href={project.links.github} className="btn btn-outline-secondary"> <a href={project.links.github} className={`btn btn-outline-secondary ${style.btnOutlineSecondary}`} target="_blank" rel="noopener noreferrer">
<i className="fab fa-github me-2"></i>Source Code <i className="fab fa-github btn-icon"></i>Source Code
</a> </a>
)} )}
{project.links.demo && ( {project.links.demo && (
<a href={project.links.demo} className="btn btn-outline-secondary"> <a href={project.links.demo} className={`btn btn-outline-secondary ${style.btnOutlineSecondary}`} target="_blank" rel="noopener noreferrer">
<i className="fas fa-external-link-alt me-2"></i>Live Demo <i className="fas fa-external-link-alt btn-icon"></i>Live Demo
</a>
)}
{project.links.documentation && (
<a href={project.links.documentation} className={`btn btn-outline-secondary ${style.btnOutlineSecondary}`} target="_blank" rel="noopener noreferrer">
<i className="fas fa-book btn-icon"></i>Documentation
</a> </a>
)} )}
<button type="button" className="btn btn-secondary" onClick={onClose}>Close</button> <button type="button" className={`btn btn-secondary`} data-bs-dismiss="modal">Close</button>
</div> </div>
</div> </div>
</div> </div>
<div className="modal-backdrop fade show"></div>
</div> </div>
); );
} };
+1 -1
View File
@@ -1,4 +1,4 @@
import type { BlogPostProps } from "../components/BlogCard"; import type {BlogPostProps} from '../untils/BlogPostProps'
export const blogPostsData: BlogPostProps[] = [ export const blogPostsData: BlogPostProps[] = [
{ {
+67 -16
View File
@@ -1,23 +1,74 @@
import type { ProjectProps } from "../components/ProjectCard"; import type { ProjectProps, Category } from "../untils/ProjectProps";
type CategoryMap = Record<string, Category>
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[] = [ export const projectsData: ProjectProps[] = [
{ {
id: "shadowguardian", id: "SecCodeSmithFrontend",
title: "ShadowGuardian: Secure IoT Monitoring System", title: "SecCodeSmith Portfolio Page - Frontend",
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.", description: "A modern, responsive portfolio page showcasing my projects, skills, and blog posts. Built with React and styled-components for a sleek user interface.",
image: "/images/projects/shadowguardian.jpg", image: "/images/projects/shadowguardian.jpg",
category: ["iot", "embedded"], category: [Categories["Web"]],
featured: true, featured: true,
technologies: [ technologies: [
{ name: "C++", icon: "devicon-cplusplus-plain tech-icon" }, { name: "React", icon: "devicon-react-original tech-icon" },
{ name: "STM32", icon: "fas fa-microchip tech-icon" }, { name: "TypeScript", icon: "devicon-typescript-plain tech-icon" },
{ name: "ESP32", icon: "fas fa-wifi tech-icon" }, { name: "SCSS", icon: "devicon-sass-original tech-icon" },
{ name: "Encryption", icon: "fas fa-shield-alt tech-icon" }, { name: "Vite", icon: "devicon-vitejs-plain tech-icon" }
{ name: "React Native", icon: "devicon-react-original tech-icon" }
], ],
links: { links: {
github: "https://github.com/SecCodeSmith/shadowguardian", github: "https://github.com/SecCodeSmith/shadowguardian",
demo: "https://shadowguardian.demo.com" demo: "https://seccodesmith.pl"
},
projectDetails: {
descriptions: [
"This portfolio page is designed to highlight my skills and projects in a visually appealing way. It features a clean layout, smooth animations, and responsive design for optimal viewing on all devices.",
"The project utilizes React for building the user interface, TypeScript for type safety, and SCSS for styling. The use of Vite ensures fast development and build times."
],
startDate: "12-05-2025",
endDate: "",
dateFormatted: "DD MMMM YYYY",
role: "Frontend Developer",
status: "In Progress",
keyFeatures: [
"Responsive design with mobile-first approach",
"Dynamic project showcase with filtering options",
"Interactive blog section with latest posts"
],
gallery: ["/images/projects/shadowguardian1.jpg", "/images/projects/shadowguardian2.jpg"],
fullTechStack: [
{ name: "React", icons: "devicon-react-original" },
{ name: "TypeScript", icons: "devicon-typescript-plain" },
{ name: "SCSS", icons: "devicon-sass-original" },
{ name: "Vite", icons: "devicon-vitejs-plain" }
]
} }
}, },
{ {
@@ -25,7 +76,7 @@ export const projectsData: ProjectProps[] = [
title: "PyroSight: Thermal Anomaly Detection", 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.", 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", image: "/images/projects/pyrosight.jpg",
category: ["ml", "iot"], category: [Categories["ML"], Categories["IoT"]],
technologies: [ technologies: [
{ name: "Python", icon: "devicon-python-plain tech-icon" }, { name: "Python", icon: "devicon-python-plain tech-icon" },
{ name: "PyTorch", icon: "devicon-pytorch-original tech-icon" }, { name: "PyTorch", icon: "devicon-pytorch-original tech-icon" },
@@ -41,7 +92,7 @@ export const projectsData: ProjectProps[] = [
title: "NexusFlow: DevOps Dashboard", 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.", 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", image: "/images/projects/nexusflow.jpg",
category: ["web"], category: [Categories["Web"]],
technologies: [ technologies: [
{ name: "C#", icon: "devicon-csharp-plain tech-icon" }, { name: "C#", icon: "devicon-csharp-plain tech-icon" },
{ name: "Blazor", icon: "devicon-dotnetcore-plain tech-icon" }, { name: "Blazor", icon: "devicon-dotnetcore-plain tech-icon" },
@@ -58,7 +109,7 @@ export const projectsData: ProjectProps[] = [
title: "EtherWhisper: Secure Communication Node", 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.", 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", image: "/images/projects/etherwhisper.jpg",
category: ["embedded", "iot"], category: [Categories["Embedded"], Categories["IoT"]],
technologies: [ technologies: [
{ name: "C", icon: "devicon-c-plain tech-icon" }, { name: "C", icon: "devicon-c-plain tech-icon" },
{ name: "STM32", icon: "fas fa-memory tech-icon" }, { name: "STM32", icon: "fas fa-memory tech-icon" },
@@ -74,7 +125,7 @@ export const projectsData: ProjectProps[] = [
title: "DataForge: Advanced Analytics Platform", 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.", 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", image: "/images/projects/dataforge.jpg",
category: ["web"], category: [Categories["Web"]],
technologies: [ technologies: [
{ name: "Python", icon: "devicon-python-plain tech-icon" }, { name: "Python", icon: "devicon-python-plain tech-icon" },
{ name: "Django", icon: "devicon-django-plain tech-icon" }, { name: "Django", icon: "devicon-django-plain tech-icon" },
@@ -91,7 +142,7 @@ export const projectsData: ProjectProps[] = [
title: "VisionKeeper: Object Recognition System", 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.", 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", image: "/images/projects/visionkeeper.jpg",
category: ["ml"], category: [Categories["ML"]],
technologies: [ technologies: [
{ name: "Python", icon: "devicon-python-plain tech-icon" }, { name: "Python", icon: "devicon-python-plain tech-icon" },
{ name: "PyTorch", icon: "devicon-pytorch-original tech-icon" }, { name: "PyTorch", icon: "devicon-pytorch-original tech-icon" },
+1 -1
View File
@@ -7,7 +7,7 @@ import style from '@styles/Blog.module.scss';
export const Blog = () => { export const Blog = () => {
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const [currentPage, setCurrentPage] = useState(1); const [currentPage, /*setCurrentPage*/] = useState(1);
const filteredPosts = searchQuery const filteredPosts = searchQuery
? blogPostsData.filter(post => ? blogPostsData.filter(post =>
+26 -48
View File
@@ -1,30 +1,35 @@
import { useState } from 'react'; import { useState } from 'react';
import { PageHeader } from '../components/PageHeader'; import { PageHeader } from '../components/PageHeader';
import { ProjectCard, type ProjectProps } from '../components/ProjectCard'; import { ProjectCard } from '../components/ProjectCard';
import { ProjectModal } from '../components/ProjectModal'; import { ProjectModal } from '../components/ProjectModal';
import { projectsData } from '../data/projectsData'; import { projectsData, Categories } from '../data/projectsData';
import type {ProjectProps} from '../untils/ProjectProps';
import style from '@styles/Project.module.scss'
export const Projects = () => { export const Projects = () => {
const [activeFilter, setActiveFilter] = useState('all'); const [activeFilter, setActiveFilter] = useState('all');
const [selectedProject, setSelectedProject] = useState<ProjectProps | null>(null); const [selectedProject, setSelectedProject] = useState<ProjectProps | null>(null);
// Filter projects based on selected category
const filteredProjects = activeFilter === 'all' const filteredProjects = activeFilter === 'all'
? projectsData ? projectsData
: projectsData.filter(project => project.category.includes(activeFilter)); : projectsData.filter(project => project.category.some(cat => cat.shortName.toLowerCase() === activeFilter));
if (selectedProject == null && filteredProjects.length > 0)
setSelectedProject(filteredProjects[0]);
const categories = Categories;
// Get the featured project
const featuredProject = projectsData.find(project => project.featured); const featuredProject = projectsData.find(project => project.featured);
// Get regular projects (non-featured)
const regularProjects = filteredProjects.filter(project => !project.featured); const regularProjects = filteredProjects.filter(project => !project.featured);
// Handle project filter click
const handleFilterClick = (filter: string) => { const handleFilterClick = (filter: string) => {
setActiveFilter(filter); setActiveFilter(filter);
}; };
// Handle opening project details modal
const handleOpenDetails = (id: string) => { const handleOpenDetails = (id: string) => {
const project = projectsData.find(p => p.id === id); const project = projectsData.find(p => p.id === id);
if (project) { if (project) {
@@ -32,11 +37,6 @@ export const Projects = () => {
} }
}; };
// Handle closing project details modal
const handleCloseDetails = () => {
setSelectedProject(null);
};
return ( return (
<> <>
<PageHeader <PageHeader
@@ -46,74 +46,52 @@ export const Projects = () => {
<div className="container mt-5"> <div className="container mt-5">
{/* Projects Filter */} {/* Projects Filter */}
<div className="filter-container text-center"> <div className={`filter-container ${style.filterContainer} text-center`}>
<button <button
className={`filter-button ${activeFilter === 'all' ? 'active' : ''}`} className={`filter-button ${style.filterButton} ${activeFilter === 'all' ? style.active : ''}`}
onClick={() => handleFilterClick('all')} onClick={() => handleFilterClick('all')}
> >
All Artifacts All Artifacts
</button> </button>
{Object.values(categories).map((category) => (
<button <button
className={`filter-button ${activeFilter === 'embedded' ? 'active' : ''}`} key={category.shortName}
onClick={() => handleFilterClick('embedded')} className={`filter-button ${style.filterButton} ${activeFilter === category.shortName.toLowerCase() ? style.active : ''}`}
onClick={() => handleFilterClick(category.shortName.toLowerCase())}
> >
Embedded Systems {category.fullName}
</button>
<button
className={`filter-button ${activeFilter === 'web' ? 'active' : ''}`}
onClick={() => handleFilterClick('web')}
>
Web Development
</button>
<button
className={`filter-button ${activeFilter === 'ml' ? 'active' : ''}`}
onClick={() => handleFilterClick('ml')}
>
Machine Learning
</button>
<button
className={`filter-button ${activeFilter === 'iot' ? 'active' : ''}`}
onClick={() => handleFilterClick('iot')}
>
IoT
</button> </button>
))}
</div> </div>
{/* Projects Grid */}
<div className="row"> <div className="row">
{/* Featured Project */} {featuredProject && (activeFilter === 'all' || featuredProject.category.some(cat => cat.shortName.toLowerCase() === activeFilter)) && (
{featuredProject && (activeFilter === 'all' || featuredProject.category.includes(activeFilter)) && (
<div className="col-12 mb-4"> <div className="col-12 mb-4">
<ProjectCard project={featuredProject} onOpenDetails={handleOpenDetails} /> <ProjectCard project={featuredProject} onOpenDetails={handleOpenDetails} />
</div> </div>
)} )}
{/* Regular Projects */}
{regularProjects.map(project => ( {regularProjects.map(project => (
<div className="col-md-6 col-lg-4 mb-4" key={project.id}>
<ProjectCard project={project} onOpenDetails={handleOpenDetails} /> <ProjectCard project={project} onOpenDetails={handleOpenDetails} />
</div>
))} ))}
</div> </div>
{/* No Projects Message */}
{filteredProjects.length === 0 && ( {filteredProjects.length === 0 && (
<div className="text-center mt-5"> <div className="text-center mt-5">
<h3>No projects found for this category.</h3> <h3>No projects found for this category.</h3>
</div> </div>
)} )}
{/* View All Button */} <div className="text-center mt-4 mb-5">
<div className="text-center mt-4"> <a href="https://github.com/SecCodeSmith" className={`btn btn-primary ${style.btnPrimary}`}>
<a href="https://github.com/SecCodeSmith" className="btn btn-primary">
<i className="fab fa-github me-2"></i> View All Projects on GitHub <i className="fab fa-github me-2"></i> View All Projects on GitHub
</a> </a>
</div> </div>
</div> </div>
{/* Project Modal */}
{selectedProject && ( {selectedProject && (
<ProjectModal project={selectedProject} onClose={handleCloseDetails} /> <ProjectModal project={selectedProject} />
)} )}
</> </>
); );
+16
View File
@@ -0,0 +1,16 @@
export interface BlogPostProps {
id: string;
slug: string;
title: string;
excerpt: string;
image: string;
category: string;
date: string;
author: string;
commentCount: number;
readTime: string;
featured?: boolean;
tags: string[];
content: string;
}
+46
View File
@@ -0,0 +1,46 @@
export interface ProjectProps {
id: string;
title: string;
description: string;
projectDetails?: ProjectDetails;
image: string;
category: Category[];
featured?: boolean;
technologies: ProjectTech[];
links: {
github?: string;
demo?: string;
documentation?: string;
};
}
export interface ProjectDetails {
descriptions?: string[];
startDate?: string;
endDate?: string;
dateFormatted?: string;
role?: string;
status?: string;
client?: string;
keyFeatures?: string[];
gallery?: string[];
fullTechStack?: FullTechStack[];
}
export interface Category {
[key: string]: string;
fullName: string;
shortName: string;
icon: string;
}
export interface ProjectTech {
name: string;
icon: string;
}
export interface FullTechStack {
name: string;
icons: string;
}