Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 167 additions & 57 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/components/AnimatedRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { Routes, Route, useLocation } from "react-router-dom";
import Landing from "../pages/landing/Landing";
import About from "../pages/about/About";
import Portfolio from "../pages/portfolio/Portfolio";
import ProjectsOverview from "../pages/projects/ProjectsOverview";
import ProjectDetails from "../pages/projects/ProjectDetails";
import ProjectArchive from "../pages/projects/ProjectArchive";
import Contact from "../pages/contact/Contact";

const AnimatedRoutes = ({ personalDetails }) => {
Expand All @@ -24,6 +27,9 @@ const AnimatedRoutes = ({ personalDetails }) => {
}
/>
<Route path="/portfolio" element={<Portfolio />} />
<Route path="/projects" element={<ProjectsOverview />} />
<Route path="/projects/archive" element={<ProjectArchive />} />
<Route path="/projects/:slug" element={<ProjectDetails />} />
<Route
path="/contact"
element={
Expand Down
4 changes: 1 addition & 3 deletions src/components/Header.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import NavLinks from "./NavLinks";
import logo from "../images/logo.svg";

const Header = () => {
return (
<header className="header">
<h5 className="logo">The journey of a thousand miles begins with one step.</h5>
<h5 className="logo">The journey of a thousand miles begins with one step.</h5>
<NavLinks />
</header>
);
Expand Down
7 changes: 6 additions & 1 deletion src/components/NavLinks.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ const NavLinks = () => {
<NavLink to="/about" onClick={() => setIsMenuOpen(false)}>
About
</NavLink>

<NavLink to="/portfolio" onClick={() => setIsMenuOpen(false)}>
Portfolio
</NavLink>
<NavLink to="/projects" onClick={() => setIsMenuOpen(false)}>
Projects
</NavLink>
<NavLink to="/contact" onClick={() => setIsMenuOpen(false)}>
Contact
</NavLink>
Expand Down
26 changes: 16 additions & 10 deletions src/components/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { motion } from "framer-motion";
import { useInView } from "react-intersection-observer";
import Modal from "react-modal";
import { useState } from "react";
import { Link } from "react-router-dom";
import closeModal from "../images/close.svg";

const Project = ({ technologies, title, image, color, id, github, deployed, description }) => {
const Project = ({ technologies, title, image, color, id, github, deployed, description, slug }) => {
const [ref, inView] = useInView({
threshold: 0.5,
triggerOnce: true,
Expand Down Expand Up @@ -40,10 +41,10 @@ const Project = ({ technologies, title, image, color, id, github, deployed, desc
<em>{technologies}</em>
</p>
<h3 className="projectTitle">{title}</h3>
<span className="viewWork">View Work &#8594;</span>
<span className="viewWork">Quick View &#8594;</span>
</div>
<div className="imageContainer col-6 d-flex align-items-center justify-content-center">
<img src={image} alt="Laptop displaying application" />
<img src={image} alt={`${title} preview`} />
</div>
</div>
<Modal
Expand All @@ -67,15 +68,20 @@ const Project = ({ technologies, title, image, color, id, github, deployed, desc
},
}}
>
<img src={closeModal} className="closeMenu closeModal" onClick={handleCloseModal} alt="Close"></img>
<img src={closeModal} className="closeMenu closeModal" onClick={handleCloseModal} alt="Close" />
<h3 className="modalTitle">{title}</h3>
<p className="projectDescription">{description}</p>
<button className="btn" onClick={() => (window.location.href = github)}>
GitHub Repo
</button>
<button className="btn" onClick={() => (window.location.href = deployed)}>
Live Link
</button>
<div className="modalActions">
<button className="btn" onClick={() => (window.location.href = github)}>
GitHub Repo
</button>
<button className="btn" onClick={() => (window.location.href = deployed)}>
Live Link
</button>
<Link className="btn btn-secondary" to={`/projects/${slug}`} onClick={handleCloseModal}>
Project Details
</Link>
</div>
</Modal>
</motion.div>
);
Expand Down
19 changes: 17 additions & 2 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ body {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}

.btn.btn-secondary {
background-color: transparent;
border: 1px solid var(--hl-color);
color: var(--text-color);
text-decoration: none;
}

.btn.btn-secondary:hover {
background-color: rgba(0, 158, 102, 0.15);
}

/* --- SKILLS SECTION --- */
.skillsSection {
background-color: rgba(255, 255, 255, 0.1);
Expand Down Expand Up @@ -142,7 +153,11 @@ header {
}

.logo {
height: 30px;
font-size: 14px;
font-weight: 500;
letter-spacing: 0.5px;
color: var(--secondary-text-color);
max-width: 420px;
}

.links {
Expand Down Expand Up @@ -304,4 +319,4 @@ header {
.closed {
background-color: transparent;
}
}
}
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "./index.css";
import "./pages/landing/landing.css";
import "./pages/about/about.css";
import "./pages/portfolio/portfolio.css";
import "./pages/projects/projects.css";
import "./pages/contact/contact.css";

import App from "./App";
Expand Down
19 changes: 16 additions & 3 deletions src/pages/portfolio/Portfolio.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Link } from "react-router-dom";
import projectData from "./projectsData.json";
import Project from "../../components/Project";
import PageHeader from "../../components/PageHeader";

const Portfolio = () => {
const featuredProjects = projectData.slice(0, 4);

const ProjectList = () =>
projectData.map((project, i) => (
featuredProjects.map((project) => (
<Project
key={i}
key={project.id}
id={project.id}
title={project.title}
technologies={project.technologies}
Expand All @@ -15,12 +18,22 @@ const Portfolio = () => {
github={project.github}
deployed={project.deployed}
description={project.description}
slug={project.slug}
/>
));

return (
<section className="portfolio">
<PageHeader title="Portfolio" description="View my work" />
<PageHeader title="Featured Projects" description="A curated selection of recent work" />
<div className="portfolioIntro">
<p>
Here is a snapshot of my latest builds. Explore the full project library for detailed case studies,
timelines, and tech stacks.
</p>
<Link className="btn btn-secondary" to="/projects">
Explore All Projects
</Link>
</div>
<div className="row">
<ProjectList />
</div>
Expand Down
27 changes: 27 additions & 0 deletions src/pages/portfolio/portfolio.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@
padding-bottom: var(--spacing);
}

.portfolioIntro {
display: flex;
flex-direction: column;
gap: 16px;
max-width: 720px;
text-align: center;
margin-bottom: 20px;
color: var(--secondary-text-color);
}

.portfolioIntro .btn {
align-self: center;
text-decoration: none;
}

.projectCard {
color: white;
border-radius: 3px;
Expand Down Expand Up @@ -91,6 +106,13 @@
margin-bottom: 10px;
}

.modalActions {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 10px;
}

/* --- MEDIA QUEIRES --- */

@media (min-width: 750px) {
Expand All @@ -105,6 +127,11 @@
padding-right: var(--spacing);
}

.portfolioIntro {
text-align: left;
align-self: flex-start;
}

.imageContainer {
transform: translateX(30%);
transition: all 0.3s ease-in-out;
Expand Down
90 changes: 74 additions & 16 deletions src/pages/portfolio/projectsData.json
Original file line number Diff line number Diff line change
@@ -1,64 +1,122 @@
[
{
"id": 1,
"title": "Bike Buddy",
"image": "./projectImages/bike.png",
"description": "I was responsible for the routing functionality of Bike Buddy, an application that helps parents and guardians keep their children safe while cycling. Utilises the Leaflet.js library and TomTom Routing/Reverse Geolocation API's to create an interactive and visually appealing map that allows a user to plot a route, taking into account various factors such as traffic levels, bicycle lanes, and elevation.",
"slug": "bike-buddy",
"image": "/projectImages/bike.png",
"summary": "Route-planning experience that helps guardians map safe cycling paths.",
"description": "I was responsible for the routing functionality of Bike Buddy, an application that helps parents and guardians keep their children safe while cycling. Utilises the Leaflet.js library and TomTom Routing/Reverse Geolocation APIs to create an interactive and visually appealing map that allows a user to plot a route, taking into account various factors such as traffic levels, bicycle lanes, and elevation.",
"highlights": [
"Built a multi-step routing flow with live map previews",
"Integrated TomTom routing and reverse geolocation",
"Layered elevation and traffic data for safer routes"
],
"technologies": "Leaflet | jQuery | Bootstrap",
"github": "https://github.com/mdyeates/bike-buddy",
"deployed": "https://norrinradd8.github.io/bike_buddy/",
"bgcolor": "#f37737",
"id": "1"
"category": "Mapping",
"year": "2023",
"role": "Maps + Front-End"
},
{
"id": 2,
"title": "Weather",
"image": "./projectImages/weather.png",
"slug": "weather-dashboard",
"image": "/projectImages/weather.png",
"summary": "A weather dashboard with smart theming, forecasts, and search history.",
"technologies": "jQuery | HTML | CSS",
"description": " This dashboard uses the OpenWeatherMap API to retrieve detailed weather data, including the city name, date, weather icon, temperature, humidity, and wind speed. The dashboard has a 5-day forecast feature and a search history feature that saves past searches for easy access. It also has a dark/light theme that changes the look of the page based on day or night in the searched location and uses the geolocation API to automatically determine the user's current location and display the weather.",
"description": "This dashboard uses the OpenWeatherMap API to retrieve detailed weather data, including the city name, date, weather icon, temperature, humidity, and wind speed. The dashboard has a 5-day forecast feature and a search history feature that saves past searches for easy access. It also has a dark/light theme that changes the look of the page based on day or night in the searched location and uses the geolocation API to automatically determine the user's current location and display the weather.",
"highlights": [
"Automated dark/light mode based on local time",
"Search history with quick recall",
"Forecast and live conditions on one screen"
],
"github": "https://github.com/mdyeates/weather-app",
"deployed": "https://mdyeates.github.io/weather-app/",
"bgcolor": "#29cbe0",
"id": "2"
"category": "Dashboard",
"year": "2022",
"role": "Front-End"
},
{
"id": 3,
"title": "Team",
"image": "./projectImages/team.png",
"slug": "team-dashboard",
"image": "/projectImages/team.png",
"summary": "CLI tool that generates a clean employee directory site.",
"technologies": "JS | Node | Bootstrap | OOP | TTD",
"description": "Team Dashboard is a Node command-line application that takes in information about employees to generate a dynamic HTML web page.",
"highlights": [
"CLI prompts for multiple employee roles",
"Generated HTML templates with consistent styling",
"Object-oriented patterns for data validation"
],
"github": "https://github.com/mdyeates/team-dashboard",
"deployed": "https://drive.google.com/file/d/1_atUXTM-L9r4NccsrPaaWqPyNu37ftwH/view",
"bgcolor": "#6c4bf4",
"id": "3"
"category": "Automation",
"year": "2022",
"role": "Node CLI"
},
{
"id": 4,
"title": "Quiz",
"image": "./projectImages/quiz.png",
"slug": "coding-quiz",
"image": "/projectImages/quiz.png",
"summary": "Timed JavaScript quiz with scoring and feedback.",
"technologies": "JS | HTML | CSS",
"description": "A quiz app on the fundamentals of JavaScript. There are 5 randomly selected multiple-choice questions and the user will have a time limit of 50 seconds. The remaining time will determine their score at the end. The user will be penalised by 10 seconds for every incorrect answer.",
"highlights": [
"Randomized question order",
"Time penalties for incorrect answers",
"Score summary at completion"
],
"github": "https://github.com/mdyeates/coding-quiz",
"deployed": "https://mdyeates.github.io/coding-quiz/",
"bgcolor": "#f85781",
"id": "4"
"category": "Front-End",
"year": "2021",
"role": "Front-End"
},

{
"id": 5,
"title": "P1",
"image": "./projectImages/P1.png",
"slug": "p1-heroes",
"image": "/projectImages/P1.png",
"summary": "F1 updates, standings, and a driver knowledge card game.",
"description": "Welcome to P1, your virtual pit stop for the latest updates and standings in the world of F1. But that's not all—we've also got the ultimate card game where driver knowledge is the key to victory.",
"technologies": "React | Framer | CSS | Bootstrap | Node",
"highlights": [
"Interactive card game flow",
"Live standings experience",
"Responsive layout for race-day browsing"
],
"github": "https://github.com/Jake-W95/P1",
"deployed": "https://p1-heroes.netlify.app/",
"bgcolor": "#ffcc33",
"id": "1"
"category": "React",
"year": "2023",
"role": "Front-End"
},

{
"id": 6,
"title": "Planner",
"image": "./projectImages/planner.png",
"slug": "daily-planner",
"image": "/projectImages/planner.png",
"summary": "Daily planner that keeps the 9-5 schedule on track.",
"technologies": "JS | Bootstrap | Moment",
"description": "Make every hour count with this simple Daily Planner App. You can keep track of all important appointments during a normal 9-5 work day!",
"highlights": [
"Color-coded schedule blocks",
"Local storage persistence",
"Time-aware status indicators"
],
"github": "https://github.com/mdyeates/daily-planner",
"deployed": "https://mdyeates.github.io/daily-planner/",
"bgcolor": "#3e67ff",
"id": "6"
"category": "Productivity",
"year": "2021",
"role": "Front-End"
}
]
Loading
Loading