v0.12.4 Update Project card to filter with array categories, and polish project card presentation
This commit is contained in:
parent
7875d92b4c
commit
9ccb1d5a1d
5 changed files with 201 additions and 37 deletions
|
|
@ -2,52 +2,147 @@
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"title": "PaperKeypad",
|
"title": "PaperKeypad",
|
||||||
"category": "Misc",
|
"category": ["Misc"],
|
||||||
"images": ["/assets/projects/keypad0.jpg"],
|
"images": ["/assets/projects/keypad0.jpg"],
|
||||||
"description": "Ever need to use a keyboard, but you got only your phone and a printer, PaperKeypad is a keypad that is made of paper.",
|
"description": "Ever need to use a keyboard, but you got only your phone and a printer, PaperKeypad is a keypad that is made of paper.",
|
||||||
"technologies": ["Java", "JavaFX", "Android Studio"],
|
"technologies": ["Java", "JavaFX", "Android Studio"],
|
||||||
"features": ["Mobile sensor manipulation", "Responsive design"],
|
"features": ["Mobile sensor manipulation", "Responsive design"],
|
||||||
"githubLink": "https://github.com/betato/PaperKeypad"
|
"githubLink": "https://github.com/betato/PaperKeypad",
|
||||||
|
"date": 2019
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"title": "StonkBot",
|
"title": "StonkBot",
|
||||||
"category": "Misc",
|
"category": ["Misc"],
|
||||||
"images": ["/assets/projects/stonkbot0.jpg"],
|
"images": ["/assets/projects/stonkbot0.jpg"],
|
||||||
"description": "The fear of losing money is common among first-time and seasoned investors alike. This inspired the creation of Stonk Bot, a fantasy trading platform that can be implemented in Discord.",
|
"description": "The fear of losing money is common among first-time and seasoned investors alike. This inspired the creation of Stonk Bot, a fantasy trading platform that can be implemented in Discord.",
|
||||||
"technologies": ["Python", "VS Code", "Matplotlib", "Financial Modeling Prep API", "Discord API"],
|
"technologies": ["Python", "VS Code", "Matplotlib", "Financial Modeling Prep API", "Discord API"],
|
||||||
"features": ["Buy shares", "Sell shares", "View stock information", "View personal portfolio", "View leaderboard"],
|
"features": ["Buy shares", "Sell shares", "View stock information", "View personal portfolio", "View leaderboard"],
|
||||||
"githubLink": "https://github.com/aidanbruneel/stonkbot",
|
"githubLink": "https://github.com/aidanbruneel/stonkbot",
|
||||||
"liveLink": "https://discord.com/invite/tQNkk7v7R8"
|
"liveLink": "https://discord.com/invite/tQNkk7v7R8",
|
||||||
|
"date": 2022
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"title": "Car Model Classification",
|
"title": "Car Model Classification",
|
||||||
"category": "Machine Learning",
|
"category": ["Machine Learning"],
|
||||||
"images": ["/assets/projects/carmodelclass0.png"],
|
"images": ["/assets/projects/carmodelclass0.png"],
|
||||||
"description": "Developing a computer vision application to identify a vehicle model from a given image is an interesting and challenging problem to solve. Challenge of this problem is that different vehicle models can appear very similar and the same vehicle can look different and hard to identify depending on lighting conditions, angle and many other factors. In this project, I decided to train a Convolutional Neural Network(CNN) to generate a model that can identify a given vehicle model.",
|
"description": "Developing a computer vision application to identify a vehicle model from a given image is an interesting and challenging problem to solve. Challenge of this problem is that different vehicle models can appear very similar and the same vehicle can look different and hard to identify depending on lighting conditions, angle and many other factors. In this project, I decided to train a Convolutional Neural Network(CNN) to generate a model that can identify a given vehicle model.",
|
||||||
"technologies": ["Python", "Tensorflow", "CNN", "Deep learning", "ResNet", "EfficientNet", "Stanford Cars Dataset"],
|
"technologies": ["Python", "Tensorflow", "CNN", "Deep learning", "ResNet", "EfficientNet", "Stanford Cars Dataset"],
|
||||||
"features": ["Buy shares", "Sell shares", "View stock information", "View personal portfolio", "View leaderboard"],
|
"features": ["Buy shares", "Sell shares", "View stock information", "View personal portfolio", "View leaderboard"],
|
||||||
"githubLink": "https://github.com/mnisyif/carClassificationModel"
|
"githubLink": "https://github.com/mnisyif/carClassificationModel",
|
||||||
|
"date": 2022
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"title": "Memory Allocation Simulations",
|
"title": "Memory Allocation Simulations",
|
||||||
"category": "Misc",
|
"category": ["Misc"],
|
||||||
"images": ["/assets/projects/memallc0.png"],
|
"images": ["/assets/projects/memallc0.png"],
|
||||||
"description": "This implementation uses doubly linked list to simulate memory allocation given 4 different memory management algorithms",
|
"description": "This implementation uses doubly linked list to simulate memory allocation given 4 different memory management algorithms",
|
||||||
"technologies": ["C", "CMake", "Data structures"],
|
"technologies": ["C", "CMake", "Data structures"],
|
||||||
"features": ["First fit", "Best fit", "Next fit", "Worst fit"],
|
"features": ["First fit", "Best fit", "Next fit", "Worst fit"],
|
||||||
"githubLink": "https://github.com/mnisyif/MemoryAllocationAlgorithm/tree/main"
|
"githubLink": "https://github.com/mnisyif/MemoryAllocationAlgorithm/tree/main",
|
||||||
|
"date": 2022
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": 5,
|
||||||
"title": "Portfolio Website",
|
"title": "Transformer-based Semantic Transcoding",
|
||||||
"category": "Web Development",
|
"category": ["Machine Learning"],
|
||||||
"images": ["/assets/projects/memallc0.png"],
|
"images": ["/assets/projects/semantic01.png"],
|
||||||
"description": "This implementation uses doubly linked list to simulate memory allocation given 4 different memory management algorithms",
|
"description": "Developed PyTorch models for E2E semantic transcoding, deployed on Xilinx SoC boards using Vitis AI™",
|
||||||
"technologies": ["C", "CMake", "Data structures"],
|
"technologies": ["PyTorch", "Vitis AI", "Xilinx SoC", "Machine Learning", "C++"],
|
||||||
"features": ["First fit", "Best fit", "Next fit", "Worst fit"],
|
"features": ["E2E semantic transcoding", "Hardware acceleration", "SoC deployment", "C++ deployment"],
|
||||||
"githubLink": "https://github.com/mnisyif/MemoryAllocationAlgorithm/tree/main"
|
"githubLink": "https://github.com/mnisyif/masters-research",
|
||||||
}
|
"date": 2024
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"title": "Clean Architecture C# Backend",
|
||||||
|
"category": ["Web Development"],
|
||||||
|
"images": ["/assets/projects/clean_architecture_backend.png"],
|
||||||
|
"description": "Engineered a scalable portfolio website backend using C#, adhering to Clean Architecture principles and implementing CI/CD pipeline for efficient deployment",
|
||||||
|
"technologies": ["C#", "Clean Architecture", "CI/CD", "REST Api"],
|
||||||
|
"features": ["Scalable backend", "Clean Architecture implementation", "Automated deployment", "RESTful"],
|
||||||
|
"githubLink": "https://github.com/mnisyif/portfolio-backend",
|
||||||
|
"date": 2024
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"title": "DevOps Homelab Maestro",
|
||||||
|
"category": ["DevOps"],
|
||||||
|
"images": ["/assets/projects/homelab_maestro.png"],
|
||||||
|
"description": "Orchestrating a robust homelab environment with Docker containers, Kubernetes clusters, Ceph distributed storage, and CI/CD pipelines for seamless application deployment",
|
||||||
|
"technologies": ["Docker", "Kubernetes", "Ceph", "CI/CD"],
|
||||||
|
"features": ["Containerized applications", "Orchestration", "Distributed storage", "Automated deployment"],
|
||||||
|
"date": 2023
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"title": "RL Dynamic Noise Cancelling",
|
||||||
|
"category": ["Machine Learning"],
|
||||||
|
"images": ["/assets/projects/noise_cancelling.png"],
|
||||||
|
"description": "Implemented real-time Automatic Noise Filtering using Reinforcement Learning and Dynamic Sparse Training in PyTorch",
|
||||||
|
"technologies": ["PyTorch", "Reinforcement Learning", "Dynamic Sparse Training", "Jupyter Notebooks"],
|
||||||
|
"features": ["Real-time filtering", "Automatic noise cancellation", "Sparse training", "Interactive development"],
|
||||||
|
"githubLink": "https://github.com/mnisyif/rl-noise-cancelling",
|
||||||
|
"date": 2023
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"title": "Real-Time Text-to-Braille",
|
||||||
|
"category": ["Embedded Systems"],
|
||||||
|
"images": ["/assets/projects/braille01.jpg","/assets/projects/braille02.jpg"],
|
||||||
|
"description": "Built a Raspberry Pi device for real-time image-to-Braille conversion, enhancing accessibility for the deaf-blind community",
|
||||||
|
"technologies": ["Raspberry Pi", "Image Processing", "OCR", "Python"],
|
||||||
|
"features": ["Real-time conversion", "Low-cost OCR algorithm", "Lookup table for Braille conversion", "Accessibility enhancement"],
|
||||||
|
"date": 2023
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"title": "ZAMAZ UTI Diagnosis",
|
||||||
|
"category": ["Embedded Systems"],
|
||||||
|
"images": ["/assets/projects/zamaz01.jpg","/assets/projects/zamaz02.jpg"],
|
||||||
|
"description": "Developed a Raspberry Pi-based system for automated urine test analysis, achieving 16x faster results than standard methods",
|
||||||
|
"technologies": ["Raspberry Pi", "Python", "Image Processing", "Healthcare Technology"],
|
||||||
|
"features": ["Automated analysis", "Pixel-based concentration calculation", "E. Coli and Staph bacteria detection", "Rapid results"],
|
||||||
|
"date": 2022
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"title": "HAM10K Image Classification with Deep Networks",
|
||||||
|
"category": ["Machine Learning"],
|
||||||
|
"images": ["/assets/projects/ham10k_classification.png"],
|
||||||
|
"description": "Developed and compared three deep learning models (MLP+PCA, DCNN, RegNetY-320) for skin cancer classification using the HAM10000 dataset, achieving 96.89% accuracy with RegNetY-320",
|
||||||
|
"technologies": ["PyTorch", "Deep Learning", "CNN", "RegNet", "PCA", "Python"],
|
||||||
|
"features": ["Multi-model comparison", "Data balancing and augmentation", "High accuracy classification", "Medical image analysis"],
|
||||||
|
"githubLink": "https://github.com/nithinprasad94/ENGG6600_DL_Final_Project",
|
||||||
|
"liveLink":"https://youtu.be/zHbRmIn7gPo",
|
||||||
|
"date": 2023
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"title": "Heart Disease Prediction Web App",
|
||||||
|
"category": ["Machine Learning", "Web Development"],
|
||||||
|
"images": ["/assets/projects/heartdis01.png"],
|
||||||
|
"description": "Developed a Flask-based web application that predicts the likelihood of heart disease using machine learning models. The app processes user input, applies feature encoding and scaling, and provides instant predictions.",
|
||||||
|
"technologies": [
|
||||||
|
"Python",
|
||||||
|
"Flask",
|
||||||
|
"NumPy",
|
||||||
|
"Scikit-learn",
|
||||||
|
"Pickle",
|
||||||
|
"HTML",
|
||||||
|
"Machine Learning"
|
||||||
|
],
|
||||||
|
"features": [
|
||||||
|
"User-friendly web interface for input",
|
||||||
|
"Real-time prediction using pre-trained model",
|
||||||
|
"Feature encoding and scaling",
|
||||||
|
"Integration of multiple ML preprocessing steps",
|
||||||
|
"Handling of both categorical and numerical inputs"
|
||||||
|
],
|
||||||
|
"githubLink": "https://github.com/zeyadghulam/engg6600-assignment3",
|
||||||
|
"date":2023
|
||||||
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,16 @@ function Projects({ title, data }) {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const categories = useMemo(() => {
|
const categories = useMemo(() => {
|
||||||
const cats = new Set(data.map((project) => project.category));
|
const cats = new Set(data.flatMap((project) => project.category));
|
||||||
return ["All", ...Array.from(cats)];
|
return ["All", ...Array.from(cats)];
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
const filteredProjects = useMemo(() => {
|
const sortedAndFilteredProjects = useMemo(() => {
|
||||||
if (activeFilter === "All") return data;
|
let filteredProjects = activeFilter === "All"
|
||||||
return data.filter((project) => project.category === activeFilter);
|
? data
|
||||||
|
: data.filter((project) => project.category.includes(activeFilter));
|
||||||
|
|
||||||
|
return filteredProjects.sort((a, b) => b.date - a.date);
|
||||||
}, [data, activeFilter]);
|
}, [data, activeFilter]);
|
||||||
|
|
||||||
const handleFilterClick = (category) => {
|
const handleFilterClick = (category) => {
|
||||||
|
|
@ -41,14 +44,23 @@ function Projects({ title, data }) {
|
||||||
<h2 className={styles.sectionTitle}>{title}</h2>
|
<h2 className={styles.sectionTitle}>{title}</h2>
|
||||||
<div className={styles.filterContainer}>
|
<div className={styles.filterContainer}>
|
||||||
{categories.map((category) => (
|
{categories.map((category) => (
|
||||||
<button key={category} className={`${styles.filterButton} ${activeFilter === category ? styles.active : ""}`} onClick={() => handleFilterClick(category)}>
|
<button
|
||||||
|
key={category}
|
||||||
|
className={`${styles.filterButton} ${activeFilter === category ? styles.active : ""}`}
|
||||||
|
onClick={() => handleFilterClick(category)}
|
||||||
|
>
|
||||||
{category}
|
{category}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.projectGrid}>
|
<div className={styles.projectGrid}>
|
||||||
{filteredProjects.map((project) => (
|
{sortedAndFilteredProjects.map((project) => (
|
||||||
<ProjectCard key={project.id} project={project} onClick={openModal} className={animatingOut ? styles.fadeOut : styles.fadeIn} />
|
<ProjectCard
|
||||||
|
key={project.id}
|
||||||
|
project={project}
|
||||||
|
onClick={openModal}
|
||||||
|
className={animatingOut ? styles.fadeOut : styles.fadeIn}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{selectedProject && <ProjectModal project={selectedProject} onClose={closeModal} />}
|
{selectedProject && <ProjectModal project={selectedProject} onClose={closeModal} />}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,44 @@
|
||||||
import React from "react";
|
import React, { useRef, useEffect, useState } from "react";
|
||||||
import styles from "./ProjectCard.module.css";
|
import styles from "./ProjectCard.module.css";
|
||||||
|
|
||||||
function ProjectCard({ project, onClick, className }) {
|
function ProjectCard({ project, onClick, className }) {
|
||||||
|
const [truncatedDescription, setTruncatedDescription] = useState(project.description);
|
||||||
|
const descriptionRef = useRef(null);
|
||||||
|
|
||||||
|
const formatList = (items) => {
|
||||||
|
return items.map((item, index, arr) => (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
{item}
|
||||||
|
{index < arr.length - 1 && <span className={styles.separator}>, </span>}
|
||||||
|
</React.Fragment>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const truncateDescription = () => {
|
||||||
|
const element = descriptionRef.current;
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
const maxHeight = parseInt(window.getComputedStyle(element).lineHeight) * 4; // 4 lines
|
||||||
|
let text = project.description;
|
||||||
|
element.textContent = text;
|
||||||
|
|
||||||
|
while (element.scrollHeight > maxHeight && text.length > 0) {
|
||||||
|
text = text.slice(0, -1);
|
||||||
|
element.textContent = text + '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
setTruncatedDescription(element.textContent);
|
||||||
|
};
|
||||||
|
|
||||||
|
truncateDescription();
|
||||||
|
window.addEventListener('resize', truncateDescription);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', truncateDescription);
|
||||||
|
};
|
||||||
|
}, [project.description]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.card} ${className}`} onClick={() => onClick(project)}>
|
<div className={`${styles.card} ${className}`} onClick={() => onClick(project)}>
|
||||||
<div className={styles.imageSlider}>
|
<div className={styles.imageSlider}>
|
||||||
|
|
@ -9,8 +46,14 @@ function ProjectCard({ project, onClick, className }) {
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<h3 className={styles.title}>{project.title}</h3>
|
<h3 className={styles.title}>{project.title}</h3>
|
||||||
<p className={styles.category}>{project.category}</p>
|
<div className={styles.categories}>
|
||||||
<p className={styles.description}>{project.description}</p>
|
{formatList(project.category)}
|
||||||
|
</div>
|
||||||
|
<p ref={descriptionRef} className={styles.description}>{truncatedDescription}</p>
|
||||||
|
<div className={styles.technologies}>
|
||||||
|
{formatList(project.technologies)}
|
||||||
|
</div>
|
||||||
|
{/* <p className={styles.date}>{project.date}</p> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: transform 0.3s ease;
|
transition: transform 0.3s ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 450px; /* Fixed height for the card */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
height: auto;
|
||||||
|
min-height: 450px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover {
|
.card:hover {
|
||||||
|
|
@ -32,7 +33,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category {
|
.categories {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: #666;
|
color: #666;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
|
|
@ -51,10 +51,24 @@
|
||||||
.description {
|
.description {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
color: #666;
|
color: #666;
|
||||||
flex-grow: 1;
|
margin-bottom: 0.5rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: -webkit-box;
|
line-height: 1.4;
|
||||||
-webkit-line-clamp: 4; /* Adjust this number to show more or fewer lines */
|
max-height: calc(1.4em * 4); /* 4 lines of text */
|
||||||
-webkit-box-orient: vertical;
|
}
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
.technologies {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #0066cc;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #999;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
@ -1 +1 @@
|
||||||
0.12.3
|
0.12.4
|
||||||
Loading…
Add table
Add a link
Reference in a new issue