feat: projects revamp

This commit is contained in:
2026-02-23 21:47:56 +00:00
parent 94ab70775f
commit 6abb75ccad
24 changed files with 568 additions and 318 deletions

View File

@@ -14,7 +14,6 @@
"security:outdated": "pnpm outdated"
},
"dependencies": {
"@splidejs/react-splide": "^0.7.12",
"animejs": "^3.2.2",
"aos": "^2.3.4",
"eslint": "8.57.1",

15
pnpm-lock.yaml generated
View File

@@ -11,9 +11,6 @@ importers:
.:
dependencies:
'@splidejs/react-splide':
specifier: ^0.7.12
version: 0.7.12
animejs:
specifier: ^3.2.2
version: 3.2.2
@@ -452,12 +449,6 @@ packages:
'@rushstack/eslint-patch@1.16.1':
resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==}
'@splidejs/react-splide@0.7.12':
resolution: {integrity: sha512-UfXH+j47jsMc4x5HA/aOwuuHPqn6y9+ZTNYPWDRD8iLKvIVMZlzq2unjUEvyDAU+TTVPZOXkG2Ojeoz0P4AkZw==}
'@splidejs/splide@4.1.4':
resolution: {integrity: sha512-5I30evTJcAJQXt6vJ26g2xEkG+l1nXcpEw4xpKh0/FWQ8ozmAeTbtniVtVmz2sH1Es3vgfC4SS8B2X4o5JMptA==}
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
@@ -2077,12 +2068,6 @@ snapshots:
'@rushstack/eslint-patch@1.16.1': {}
'@splidejs/react-splide@0.7.12':
dependencies:
'@splidejs/splide': 4.1.4
'@splidejs/splide@4.1.4': {}
'@swc/helpers@0.5.15':
dependencies:
tslib: 2.8.1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 KiB

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@@ -11,8 +11,8 @@ const MainPage = (): JSX.Element => {
<Intro/>
<CurrentWork/>
<Experience/>
<Projects/>
<SkillsAndLinks/>
<Projects/>
<Footer/>
</div>
);

View File

@@ -1,4 +1,4 @@
import { IProjectArguments, SkillEnum } from "@/src/portfolio/helpers/Project";
import { IProjectArguments, SkillEnum } from "@/src/portfolio/types/project";
const projectData : IProjectArguments[] = [
{
@@ -13,12 +13,12 @@ const projectData : IProjectArguments[] = [
],
github: "https://github.com/KuchtaVR6/Multi-LLM-Agent",
document: "/multiAgent.pdf",
title: "Multi-LLM Tool Use Modular Pipeline with Expert Adapters",
text: "In this work, I explore a practical and cost-effective approach to improving how AI models interact with external tools and APIs. Instead of relying on large, expensive models or complex zero-shot learning methods, I utilize a modular pipeline using smaller, specialized components (Planner, Caller, Summariser) trained separately. I introduce to it a hard routing agent system that assigns tasks to expert adapters based on API categories, the system achieves performance that surpasses much larger closed-source models on a key benchmark. This approach enables more efficient, decentralized training and has potential applications beyond the tool-use QA task."
title: "Efficient Agents with Hot-Swappable Adapters",
text: "Developed a modular multi-agent system using parameter-efficient fine-tuning to enhance tool use in small language models. Implemented hard routing with expert adapters, achieving performance surpassing larger closed-source models. Demonstrated skills in distributed training, model optimization, and API integration."
},
{
imagePath: "latviaEstimation.png",
title: "Deep Learning for Real Estate Valuation - Introducing a novel normalization technique",
title: "AI Property Valuation with Novel Normalization",
tech: [
SkillEnum.machineLearning,
SkillEnum.research,
@@ -28,43 +28,43 @@ const projectData : IProjectArguments[] = [
SkillEnum.computerVision
],
document: "/DNNpropertyEstimation.pdf",
text: "Conducted within a group of three, this project presents a novel deep learning approach to predicting apartment prices using both images and structured data. The model combines feed-forward and DenseNet convolutional networks, enhanced through transfer learning and advanced regularization techniques. To address regional and temporal variations in the housing market, we introduced a geo-temporally normalized loss function—an innovation tailored for real-world market dynamics. Uniquely, the study also incorporates transport and point-of-interest maps as part of the feature set. Evaluated on a partially self-collected Latvian real estate dataset, the system achieved a strong R² score of 0.7287, surpassing previous methods in the field."
text: "Built a deep learning model combining CNNs and structured data for property valuation, achieving R² of 0.7287. Introduced novel geo-temporal normalization for market dynamics. Applied transfer learning, advanced regularization, and multi-modal data fusion including transport and POI maps. Led data collection and preprocessing."
},
{
imagePath: "ipp.png",
title: "Research Proposal: Multi-LLM Tool Use Task Splits and Fine-Tuning Strategies",
title: "Research into Multi-Agent LLM Tool Use Strategies",
tech: [
SkillEnum.machineLearning,
SkillEnum.research
],
text: "This 2025 research proposal explores new ways to enhance tool use in small language models by distributing tasks across multiple fine-tuned agents. Building on recent advances in parameter-efficient fine-tuning (PEFT), the proposed study investigates novel task divisions and tuning strategies to improve the effectiveness of multi-agent LLM systems. While still in the proposal stage, this work aims to contribute to the growing field of tool-augmented AI by making small models more capable and cost-efficient.",
text: "Research proposal investigating parameter-efficient fine-tuning strategies for multi-agent LLM systems. Designed novel task division approaches to enhance tool use in small language models. Demonstrated research design, literature synthesis, and experimental methodology planning.",
document: "/ResearchReview.pdf"
},
{
imagePath: "researchReview.png",
title: "Research Review of Neural Techniques for low-resource language translation",
title: "Research Review: Neural Low-Resource Language Translation",
tech: [
SkillEnum.machineLearning,
SkillEnum.research
],
text: "As part of my Master's program, I had the opportunity to conduct an in-depth research review on \"Neural Techniques for Low-Resource Language Translation,\" which received excellent marks across all criteria. By critically evaluating the current state of the art in this field, I gained valuable insights into the potential of neural machine translation to break down language barriers and enable better communication across different cultures and communities. I am proud to showcase this project on my website and contribute to the ongoing efforts to improve low-resource language translation. This report was marked as 'excellent' for every criterion assessed in this course.",
text: "Conducted comprehensive research review of neural machine translation techniques for low-resource languages. Critically evaluated state-of-the-art approaches, identifying key challenges and opportunities. Demonstrated research synthesis, technical writing, and analytical evaluation skills. Received excellent marks across all criteria.",
document: "/ResearchReview.pdf"
},
{
imagePath: "naturalComputing.png",
title: "Natural Computing: Implementing and analysis of PSO, GA and GP",
title: "Analysis of PSO, GA and GP Algorithms",
tech: [
SkillEnum.python,
SkillEnum.numpy
],
text: "During my Master's program, I had the opportunity to take a course on Natural Computing, where I implemented and analyzed three major algorithms: Particle Swarm Optimization (PSO), Genetic Algorithms (GA), and Genetic Programming (GP). This coursework allowed me to gain hands-on experience with these powerful optimization techniques, which are inspired by natural phenomena such as swarm intelligence and evolution. Through this project, I developed a deep understanding of the underlying principles of natural computing and its potential applications in various fields, such as engineering, finance, and biology. I am excited to showcase my implementation and analysis of PSO, GA, and GP on my website and demonstrate my proficiency in natural computing techniques.",
text: "Implemented and analyzed three bio-inspired optimization algorithms: Particle Swarm Optimization, Genetic Algorithms, and Genetic Programming. Conducted comparative performance analysis across different problem domains. Demonstrated algorithm implementation, experimental design, and data analysis skills.",
github: "https://github.com/KuchtaVR6/nat_coursework",
document: "/PatrykKuchta_nat.pdf"
},
{
imagePath: "learnopedia.png",
title: "Undergraduate Dissertation Project",
text: "In this project, an online learning platform was created with the aim of diversifying and enriching online courses in all domains. The project focused on developing a learning platform, where courses were created collaboratively with a democratic system for approving suggestions. This allowed many people to contribute to creating courses. The details of the platform's implementation were worked out through academic research and an analysis of competing software, both of which were included in this report. Additionally, the report covered the details of the implementation, testing, and evaluation of the platform.",
title: "Learnopedia: Collaborative Learning Platform",
text: "Developed full-stack collaborative learning platform with democratic course creation system. Implemented user authentication, content management, and real-time collaboration features. Conducted competitive analysis and user testing. Demonstrated React, TypeScript, Express.js, and database design skills.",
tech: [
SkillEnum.typescript,
SkillEnum.react,
@@ -76,8 +76,8 @@ const projectData : IProjectArguments[] = [
},
{
imagePath: "cifar10.png",
title: "Image classification using the CIFAR-10 Dataset",
text: "In this project, I successfully implemented an image classification model using the CIFAR-10 dataset. Through the application of deep learning techniques and convolutional neural networks, I achieved an impressive final accuracy of 95.5%. The coursework assignment was a resounding success, as it showcased my ability to effectively train and fine-tune models for image recognition tasks, leading to a perfect score of 100%. The project not only demonstrated my proficiency in machine learning but also enhanced my understanding of image processing and model evaluation.",
title: "Deep Learning Image Classification (CIFAR-10)",
text: "Built CNN-based image classifier achieving 95.5% accuracy on CIFAR-10 dataset. Applied deep learning techniques including data augmentation, regularization, and hyperparameter tuning. Demonstrated model training, evaluation, and optimization skills. Scored 100% on coursework assignment.",
tech: [
SkillEnum.python,
SkillEnum.computerVision,
@@ -87,8 +87,8 @@ const projectData : IProjectArguments[] = [
},
{
imagePath: "learnopediaShowcase.png",
title: "Project Dissertation Showcase Video",
text: "The showcase video highlights the creation of an innovative online learning platform aimed at diversifying and enriching courses across various domains. It emphasizes the collaborative approach to course creation through a democratic system for approving suggestions. The video showcases the platform's user-friendly interface and unique features. I am proud to announce that the video received the \"Best EECS Undergraduate Project Showcase Video\" award, and is featured on the official QM EECS youtube channel.",
title: "Award-Winning Dissertation Video",
text: "Created award-winning showcase video demonstrating complex technical project. Applied video editing, storytelling, and visual communication skills to effectively present platform features and user experience. Won Best EECS Undergraduate Project Showcase Video award.",
tech: [
SkillEnum.photoshop,
SkillEnum.videoEditing
@@ -97,8 +97,8 @@ const projectData : IProjectArguments[] = [
},
{
imagePath: "cryptogram.png",
title: "Cryptocurrency wallet prototype",
text: "This is one of my academic projects, the prototype that you can see in the figure was created from the ground up starting with the domain analysis for our idea. We have worked as a group of 6, where I have taken the position of a manager assigning tasks, keeping track of deadlines and checking the quality of work of others. There were lots of interesting challenges creating the prototype itself, like learning how to create a RestAPI, but the biggest challenge was effective teamwork, in which I believe we have succeeded, having all of our group contributing a significant work and having only minor problems with code integration. This project has won the best project award.",
title: "Complete Prototype of a Cryptocurrency Wallet ",
text: "Led team of 6 to build cryptocurrency wallet prototype from domain analysis to deployment. Managed project timeline, task allocation, and code integration. Developed RESTful API and full-stack application using React and Express. Won best project award for technical execution and teamwork.",
tech: [
SkillEnum.typescript,
SkillEnum.react,
@@ -111,8 +111,8 @@ const projectData : IProjectArguments[] = [
},
{
imagePath: "photocast.png",
title: "Fully functional weather app",
text: "This is also one of my academic projects, where the goal was to create a fully functional weather application with one stakeholder in mind, we have chosen to create an application tailored for photographers. While developing this application I have learned about creating a very usable and minimalistic User Interface, along with working with APIs. Furthermore, I have gained experience working in a team, where I also became the manager of the project. ",
title: "PhotoCa.st: Weather App for Photographers",
text: "Developed photographer-focused weather application with minimalistic UI/UX design. Integrated external weather APIs and implemented responsive interface. Led team project, managing development workflow and stakeholder requirements. Demonstrated API integration, UI design, and project management skills.",
tech: [
SkillEnum.react,
SkillEnum.html,
@@ -123,8 +123,8 @@ const projectData : IProjectArguments[] = [
},
{
imagePath: "psychotherapist.png",
title: "Portfolio website for an Psychotherapist",
text: "I created a portfolio website for a psychotherapist, working closely with the client to develop a design that feels calm, professional, and welcoming. Using React, TypeScript, and CSS, I translated our collaborative vision into a fully responsive and accessible site. The layout and visual style were carefully crafted to reflect the therapists approach and values. I ensured seamless performance across devices and screen sizes, with attention to both aesthetics and usability.",
title: "Psychotherapist Portfolio Website",
text: "Built fully responsive portfolio website for psychotherapy practice using React and TypeScript. Collaborated with client to design calm, professional interface. Implemented accessibility standards and cross-device compatibility. Demonstrated client communication, responsive design, and modern web development skills.",
tech: [
SkillEnum.react,
SkillEnum.typescript,
@@ -134,8 +134,8 @@ const projectData : IProjectArguments[] = [
},
{
imagePath: "architect.png",
title: "Portfolio website for an Architect",
text: "Another professional website, that I have created is a portfolio website for an Architect. The design was a vital part of the whole experience as an Architect needs to exhibit their design language. The creation of this website involved using HTML, CSS and Javascript. Javascript is mainly used for the integrated gallery view of each project. Whilst I didn't come up with the design, I was tasked with translating sketches into code. Furthermore, Bootstrap was used to ensure that the website still looks stunning on a mobile device or a vertical screen.",
title: "Architect Portfolio Website",
text: "Developed portfolio website for architecture firm, translating design sketches into functional code. Implemented interactive project gallery with JavaScript and responsive layouts using Bootstrap. Demonstrated design-to-code translation, front-end development, and mobile-first design principles.",
tech: [
SkillEnum.javascript,
SkillEnum.html,
@@ -162,28 +162,28 @@ const projectData : IProjectArguments[] = [
// ],
// github: "https://github.com/KuchtaVR6/porfolio2021",
// },
{
imagePath: "proj2.png",
title: "A discord bot for colourful messages",
text: "To further expand my knowledge in python and APIs, I developed a fully functional bot that creates embedded messages. Although the task might seem not that hard, I gave myself a requirement that the system must have professional-grade exception catching and an interface that will make it very easy to use by someone less fluent in command based interaction. This made it a much bigger project with extensive testing and a steep learning curve. Even though it was my third discord bot this one was the most challenging and I have learned a lot from writing it.",
tech: [
SkillEnum.python,
SkillEnum.html,
SkillEnum.bootstrap
],
github: "https://github.com/KuchtaVR6/EmbederBot",
access: "https://discord.com/api/oauth2/authorize?client_id=819208892834644008&permissions=0&scope=bot"
},
{
imagePath: "proj1.png",
title: "DIY AndroidAuto",
text: "A project that I did during the first lockdown, was creating an AndroidAuto based infotainment system for my Dads car. This project gave me a chance to work with Linux, Python, RaspberryPi, 3D printing and design (in Blender), soldering, relays and electronics in general. It had all features of a full AndroidAuto experience including wake on Ignition, separate volume adjustment and a touchscreen. Because I was only using the most basic electronic components possible this allowed me to design and create electrical circuits. Furthermore, a lot of parts were 3D printed and I had to ensure that components that I created were shake and heat resistant so they can survive in a car environment.",
tech: [
SkillEnum.python,
SkillEnum.linux,
SkillEnum.design3d
]
}
// {
// imagePath: "proj2.png",
// title: "A discord bot for colourful messages",
// text: "To further expand my knowledge in python and APIs, I developed a fully functional bot that creates embedded messages. Although the task might seem not that hard, I gave myself a requirement that the system must have professional-grade exception catching and an interface that will make it very easy to use by someone less fluent in command based interaction. This made it a much bigger project with extensive testing and a steep learning curve. Even though it was my third discord bot this one was the most challenging and I have learned a lot from writing it.",
// tech: [
// SkillEnum.python,
// SkillEnum.html,
// SkillEnum.bootstrap
// ],
// github: "https://github.com/KuchtaVR6/EmbederBot",
// access: "https://discord.com/api/oauth2/authorize?client_id=819208892834644008&permissions=0&scope=bot"
// },
// {
// imagePath: "proj1.png",
// title: "DIY AndroidAuto",
// text: "A project that I did during the first lockdown, was creating an AndroidAuto based infotainment system for my Dads car. This project gave me a chance to work with Linux, Python, RaspberryPi, 3D printing and design (in Blender), soldering, relays and electronics in general. It had all features of a full AndroidAuto experience including wake on Ignition, separate volume adjustment and a touchscreen. Because I was only using the most basic electronic components possible this allowed me to design and create electrical circuits. Furthermore, a lot of parts were 3D printed and I had to ensure that components that I created were shake and heat resistant so they can survive in a car environment.",
// tech: [
// SkillEnum.python,
// SkillEnum.linux,
// SkillEnum.design3d
// ]
// }
// {
// imagePath: "port3.png",
// title: "My current portfolio website",

View File

@@ -1,4 +1,4 @@
import { SkillEnum } from "@/src/portfolio/helpers/Project";
import { SkillEnum } from "@/src/portfolio/types/project";
import { ProficiencyLevel } from "@/src/portfolio/helpers/SkillDisplay";
export const skillsInCategories = {

View File

@@ -1,95 +0,0 @@
import Image from "next/image";
import styles from "../styling/projects.module.scss";
import { VscGithub } from "react-icons/vsc";
import { BsGlobe2, BsFileEarmarkMedical } from "react-icons/bs";
export interface IProjectArguments {
imagePath : string,
title : string,
text : string,
tech : SkillEnum[],
github? : string,
access? : string,
document? : string,
}
export enum SkillEnum {
java = "java",
pytorch = "pytorch",
numpy = "numpy",
plt = "matplotlib",
tensorflow = "tensorflow",
webscrape = "web scraping",
graphql = "graphQL",
spaCy = "spaCy",
cpp = "c++",
php = "php",
typescript = "typescript",
react = "react",
html = "html",
css = "css",
express = "express.js",
javascript = "javascript",
bootstrap = "bootstrap",
python = "python",
linux = "linux",
design3d = "3d design",
videoEditing = "video-editing",
photoshop = "photo-editing",
machineLearning = "machine learning",
computerVision = "computer vision",
dataEngineering = "data engineering",
nlp = "natural language processing",
latex = "LATEX",
research = "research",
polish = "Polish",
german = "German",
english = "English",
}
const Project = ({ imagePath, title, text, github, access, document } : IProjectArguments): JSX.Element => {
return (
<div className={styles.projectDisplay}>
<div className={styles.text}>
<h2 data-aos={"fade-left"}>{title}</h2>
<p data-aos={"fade-left"}>
{text}
</p>
<div className={styles.links}>
{github?
<a href={github} title={"Github repository of the project"} className={styles.icon} data-aos={"fade-left"}>
<VscGithub/>
</a> : ""}
{access?
<a href={access} title={"View and use the project here"} className={styles.icon} data-aos={"fade-left"}>
<BsGlobe2/>
</a> : ""}
{document?
<a href={document} title={"The report here"} className={styles.icon} data-aos={"fade-left"}>
<BsFileEarmarkMedical/>
</a> : ""}
</div>
</div>
<div className={styles.imageAndTech}>
<div className={styles.imageContainer}>
<Image
src = {"/portfolio/projects/"+imagePath}
alt = {"Image showing the " + title + " project."}
fill = {true}
sizes = {"100%"}
/>
</div>
{/*<h3 data-aos={"fade-right"}>Technologies Used:</h3>*/}
{/*<div className={styles.tech}>*/}
{/* {tech.map((value) => {*/}
{/* i++;*/}
{/* return <SkillDisplay key={i} name={value}/>;*/}
{/* })}*/}
{/*</div>*/}
</div>
</div>
);
};
export default Project;

View File

@@ -1,6 +1,6 @@
import { FC } from "react";
import styles from "../styling/projects.module.scss";
import { SkillEnum } from "@/src/portfolio/helpers/Project";
import styles from "./skillDisplay.module.scss";
import { SkillEnum } from "@/src/portfolio/types/project";
type SkillDisplayArgs = {
name : SkillEnum,

View File

@@ -0,0 +1,15 @@
@import "@/src/styles/helpers.scss";
.technology {
font-family: "Ubuntu Mono", monospace;
font-size: 1em;
color: $black;
margin: 0.1em;
padding: 0.1em 0.3em;
background-color: $gray;
border-radius: 2px;
&:hover {
background-color: opacify($gray, 0.35);
}
}

View File

@@ -14,7 +14,7 @@ export interface ICityGroup {
const CITY_IMAGES: Record<string, string> = {
"Manchester": "/portfolio/cities/Manchester.png",
"Edinburgh": "/portfolio/cities/Edinburgh.png",
"Greater London": "/portfolio/cities/Greater London.png",
"Greater London": "/portfolio/cities/Greater%20London.png",
"Warsaw": "/portfolio/cities/Warsaw.png"
};

View File

@@ -11,8 +11,8 @@ const TopBar = (): JSX.Element => {
</div>
<a href={"#achievements"}>Achievements</a>
<a href={"#experience"}>Experience</a>
<a href={"#projects"}>Projects</a>
<a href={"#skills"}>Skills</a>
<a href={"#projects"}>Projects</a>
</div>
<div className={styles.left}>
<a href={"mailto: patrick@kuchta.uk"}>patrick@kuchta.uk</a>

View File

@@ -1,27 +1 @@
import { Splide, SplideSlide } from "@splidejs/react-splide";
import Project from "@/src/portfolio/helpers/Project";
import projectData from "@/src/portfolio/data/projectData";
import styles from "../styling/projects.module.scss";
const Projects = (): JSX.Element => {
return (
<div className={styles.section} id={"projects"}>
<Splide
options={{
rewind: true,
type: "slide",
perPage: 1
}}
>
{ projectData.map((entry, key) => {
return (
<SplideSlide key={key}>
<Project {...entry}/>
</SplideSlide> ); })
}
</Splide>
</div>
);
};
export default Projects;
export { default } from "./Projects/Projects";

View File

@@ -0,0 +1,35 @@
import Image from "next/image";
import { IProjectArguments } from "@/src/portfolio/types/project";
import ProjectLinks from "./ProjectLinks";
import styles from "./projects.module.scss";
interface IProjectCardProps extends IProjectArguments {
index: number;
onClick: () => void;
}
const ProjectCard = (props: IProjectCardProps): JSX.Element => {
const { imagePath, title, text, github, access, document, index, onClick } = props;
const delay = (index % 3) * 100;
return (
<div className={styles.card} data-aos={"fade-up"} data-aos-delay={delay} onClick={onClick}>
<div className={styles.imageWrapper}>
<Image
src={"/portfolio/projects/" + imagePath}
alt={"Image showing the " + title + " project."}
fill={true}
sizes={"(max-width: 768px) 100vw, (max-width: 1280px) 50vw, 33vw"}
className={styles.image}
/>
</div>
<div className={styles.content}>
<h3 className={styles.cardTitle}>{title}</h3>
<p className={styles.description}>{text}</p>
<ProjectLinks github={github} access={access} document={document}/>
</div>
</div>
);
};
export default ProjectCard;

View File

@@ -0,0 +1,57 @@
import { VscGithub } from "react-icons/vsc";
import { BsGlobe2, BsFileEarmarkMedical } from "react-icons/bs";
import styles from "./projects.module.scss";
interface IProjectLinksProps {
github?: string;
access?: string;
document?: string;
}
const ProjectLinks = (props: IProjectLinksProps): JSX.Element => {
const { github, access, document } = props;
const handleClick = (e: React.MouseEvent): void => {
e.stopPropagation();
};
return (
<div className={styles.links} onClick={handleClick}>
{github && (
<a
href={github}
title={"Github repository of the project"}
className={styles.icon}
target={"_blank"}
rel={"noopener noreferrer"}
>
<VscGithub/>
</a>
)}
{access && (
<a
href={access}
title={"View and use the project here"}
className={styles.icon}
target={"_blank"}
rel={"noopener noreferrer"}
>
<BsGlobe2/>
</a>
)}
{document && (
<a
href={document}
title={"The report here"}
className={styles.icon}
target={"_blank"}
rel={"noopener noreferrer"}
>
<BsFileEarmarkMedical/>
</a>
)}
</div>
);
};
export default ProjectLinks;

View File

@@ -0,0 +1,46 @@
import { FC } from "react";
import Image from "next/image";
import { IProjectArguments } from "@/src/portfolio/types/project";
import ProjectLinks from "./ProjectLinks";
import styles from "./projects.module.scss";
import { IoClose } from "react-icons/io5";
interface IProjectModalProps extends IProjectArguments {
onClose: () => void;
}
const ProjectModal: FC<IProjectModalProps> = (props) => {
const { imagePath, title, text, github, access, document, onClose } = props;
const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>): void => {
if (e.target === e.currentTarget) {
onClose();
}
};
return (
<div className={styles.modalBackdrop} onClick={handleBackdropClick}>
<div className={styles.modalContent}>
<button className={styles.closeButton} onClick={onClose} aria-label="Close modal">
<IoClose />
</button>
<div className={styles.modalImageWrapper}>
<Image
src={"/portfolio/projects/" + imagePath}
alt={"Image showing the " + title + " project."}
fill={true}
sizes={"(max-width: 768px) 100vw, 800px"}
className={styles.modalImage}
/>
</div>
<div className={styles.modalBody}>
<h2 className={styles.modalTitle}>{title}</h2>
<p className={styles.modalDescription}>{text}</p>
<ProjectLinks github={github} access={access} document={document}/>
</div>
</div>
</div>
);
};
export default ProjectModal;

View File

@@ -0,0 +1,41 @@
import { useState } from "react";
import projectData from "@/src/portfolio/data/projectData";
import ProjectCard from "./ProjectCard";
import ProjectModal from "./ProjectModal";
import { IProjectArguments } from "@/src/portfolio/types/project";
import styles from "./projects.module.scss";
const Projects = (): JSX.Element => {
const [selectedProject, setSelectedProject] = useState<IProjectArguments | null>(null);
const handleCardClick = (project: IProjectArguments): void => {
setSelectedProject(project);
};
const handleCloseModal = (): void => {
setSelectedProject(null);
};
return (
<div className={styles.section} id={"projects"}>
<div className={styles.container}>
<h2 className={styles.title} data-aos={"fade-up"}>Projects</h2>
<div className={styles.grid}>
{projectData.map((project, index) => (
<ProjectCard
key={index}
{...project}
index={index}
onClick={() => handleCardClick(project)}
/>
))}
</div>
</div>
{selectedProject && (
<ProjectModal {...selectedProject} onClose={handleCloseModal} />
)}
</div>
);
};
export default Projects;

View File

@@ -0,0 +1 @@
export { default } from "./Projects";

View File

@@ -0,0 +1,276 @@
@import "@/src/styles/helpers.scss";
.section {
@include darkSection;
@include regularFont;
@include fullPage;
padding: 40px 0 $largeGap 0;
min-height: auto;
@media (min-width: 768px) {
padding: $largeGap 0;
}
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 0 $mediumGap;
@media (min-width: 768px) {
padding: 0 $largeGap;
}
}
.title {
font-size: 3em;
text-align: center;
margin-bottom: $largeGap;
color: $white;
font-weight: 300;
@media (max-width: 768px) {
font-size: 2em;
margin-bottom: $mediumGap;
}
}
.grid {
display: grid;
grid-template-columns: 1fr;
gap: $mediumGap;
@media (min-width: 768px) {
grid-template-columns: repeat(2, 1fr);
gap: $largeGap;
}
@media (min-width: 1280px) {
grid-template-columns: repeat(3, 1fr);
}
}
.card {
background-color: rgba(255, 255, 255, 0.05);
border-radius: 8px;
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
display: flex;
flex-direction: column;
height: 100%;
&:hover {
transform: translateY(-8px);
box-shadow: 0 12px 24px rgba($accent_colour, 0.3);
}
}
.imageWrapper {
position: relative;
width: 100%;
height: 250px;
overflow: hidden;
background-color: rgba(0, 0, 0, 0.3);
@media (min-width: 768px) {
height: 280px;
}
}
.image {
object-fit: cover;
transition: transform 0.3s ease;
.card:hover & {
transform: scale(1.05);
}
}
.content {
padding: $smallGap $mediumGap $mediumGap $mediumGap;
display: flex;
flex-direction: column;
flex-grow: 1;
@media (max-width: 768px) {
padding: 10px $smallGap $smallGap $smallGap;
}
}
.cardTitle {
font-size: 1.4em;
margin-bottom: $smallGap;
color: $white;
font-weight: 400;
line-height: 1.3;
@media (max-width: 768px) {
font-size: 1.2em;
}
}
.description {
font-size: 0.95em;
line-height: 1.6;
color: rgba($white, 0.85);
margin-bottom: $mediumGap;
flex-grow: 1;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 6;
-webkit-box-orient: vertical;
@media (max-width: 768px) {
font-size: 0.9em;
-webkit-line-clamp: 5;
}
}
.links {
display: flex;
gap: $smallGap;
margin-top: auto;
justify-content: flex-end;
}
.icon {
color: $white;
transition: all 0.3s ease;
width: 2.5em;
height: 2.5em;
display: flex;
align-items: center;
justify-content: center;
svg {
width: 100%;
height: 100%;
}
&:hover {
color: $accent_colour;
transform: scale(1.1);
}
@media (max-width: 768px) {
width: 2em;
height: 2em;
}
}
.card {
cursor: pointer;
}
.modalBackdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.85);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: $mediumGap;
overflow-y: auto;
}
.modalContent {
background-color: $black;
border: 2px solid rgba($white, 0.1);
border-radius: 12px;
max-width: 900px;
width: 100%;
max-height: 90vh;
overflow-y: auto;
position: relative;
animation: modalSlideIn 0.3s ease-out;
@media (max-width: 768px) {
max-width: 100%;
max-height: 95vh;
}
}
@keyframes modalSlideIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.closeButton {
position: absolute;
top: $smallGap;
right: $smallGap;
background-color: rgba($white, 0.1);
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
transition: all 0.3s ease;
color: $white;
svg {
width: 24px;
height: 24px;
}
&:hover {
background-color: $accent_colour;
transform: scale(1.1);
}
}
.modalImageWrapper {
position: relative;
width: 100%;
height: 400px;
background-color: rgba(0, 0, 0, 0.3);
@media (max-width: 768px) {
height: 250px;
}
}
.modalImage {
object-fit: cover;
}
.modalBody {
padding: $largeGap;
@media (max-width: 768px) {
padding: $mediumGap;
}
}
.modalTitle {
font-size: 2em;
color: $white;
margin-bottom: $mediumGap;
font-weight: 400;
@media (max-width: 768px) {
font-size: 1.5em;
}
}
.modalDescription {
font-size: 1em;
line-height: 1.8;
color: rgba($white, 0.9);
margin-bottom: $largeGap;
white-space: pre-wrap;
}

View File

@@ -1,105 +0,0 @@
@import "@/src/styles/helpers.scss";
.section {
@include lightSection;
@include regularFont;
}
.technology {
font-family: 'Ubuntu Mono', monospace;
font-size: 1em;
color: $black;
margin: 0.1em;
padding: 0.1em 0.3em;
background-color: $gray;
border-radius: 2px;
:hover{
background-color: opacify($gray, 0.35);
}
}
.projectDisplay {
@include darkSection;
display: flex;
flex-direction: row;
@media (max-width: 1280px) {
flex-direction: column;
}
.imageAndTech {
flex-basis: calc(100% / 2);
margin: 1em;
display: flex;
.imageContainer {
margin: auto;
position: relative;
max-width: 100%;
max-height: 60vh;
@include nextImg;
img {
width: 100%;
height: 100%;
object-fit: contain; // or 'contain' depending on the effect you want
}
}
.tech {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
}
}
.text {
flex-basis: calc(100% / 2);
padding: 2em 2em;
h2 {
font-size: 1.8em;
}
p {
font-size: 1.05em;
line-height: 1.6;
}
.links {
display: flex;
flex-direction: row-reverse;
gap: 2em;
.icon {
color: $white;
filter: drop-shadow(7px 7px 5px $accent_colour);
$size: 4em;
width: $size;
height: $size;
svg {
width: $size;
height: $size;
}
:hover {
color: $gray;
}
}
}
}
}
.otherLinks {
list-style-type: none;
text-align: center;
margin: 0 auto;
width: fit-content;
}

View File

@@ -0,0 +1,43 @@
export interface IProjectArguments {
imagePath: string;
title: string;
text: string;
tech: SkillEnum[];
github?: string;
access?: string;
document?: string;
}
export enum SkillEnum {
java = "java",
pytorch = "pytorch",
numpy = "numpy",
plt = "matplotlib",
tensorflow = "tensorflow",
webscrape = "web scraping",
graphql = "graphQL",
spaCy = "spaCy",
cpp = "c++",
php = "php",
typescript = "typescript",
react = "react",
html = "html",
css = "css",
express = "express.js",
javascript = "javascript",
bootstrap = "bootstrap",
python = "python",
linux = "linux",
design3d = "3d design",
videoEditing = "video-editing",
photoshop = "photo-editing",
machineLearning = "machine learning",
computerVision = "computer vision",
dataEngineering = "data engineering",
nlp = "natural language processing",
latex = "LATEX",
research = "research",
polish = "Polish",
german = "German",
english = "English"
}

View File

@@ -21,28 +21,6 @@ body {
-moz-osx-font-smoothing: grayscale;
}
.splide__arrow svg {
fill: $accent_colour !important;
}
.splide__pagination__page.is-active {
background-color: $accent_colour !important;
}
.splide__arrow.splide__arrow--next {
height: 5em !important;
width: 5em !important;
}
.splide__arrow.splide__arrow--prev {
height: 5em !important;
width: 5em !important;
}
.splide__slide {
display: flex;
}
.accordion {
border: 1px solid transparentize($gray, 0.3);
border-radius: 8px;