diff --git a/README.md b/README.md
index 3bf3ec5c..37b390e3 100644
--- a/README.md
+++ b/README.md
@@ -1,80 +1,55 @@
-Focus on fleshing out and adding systems to my portfolio
+
My Portfolio
-- Not just past projects, but scripts to easily add new projects.
-- Responsive design
-- Resume page
-- SLCC e-portfolio
-
-The idea is to showcase my full-stack development skills in a future-ready manner.
-
-Timeline:
-Week 1: 17/12/24 - 23/12/24 (Planning for the future)
-
-- [x] Refactor and reorder the HTML & CSS.
-- [x] Template HTML blocks for repeated components.
-- [ ] Document classes, ID's, and other attributes.
- - [ ] Script to easily add new projects (components).
-- [x] Embeddable PDFs (research phase).
-
-Week 2: 24/12/24 - 30/12/24 (Adding key information)
-
-- [x] Resume page (HTML and PDF options).
-- [ ] Convert SLCC e-portfolio to my own website
- - [ ] Meet required pages and content
- - [ ] Embed PDF course reflections/signature assignments.
-- [x] Contact page.
- - [x] Implement MailTrap for sending me automated emails when there is a contact form submission.
-
-Week 3: 31/12/24 - 6/12/24
-
-- [ ] Refactor & polish codebase.
-- [ ] Update documentation.
-- [ ] Demo's of various projects.
-- [ ] Security:
- - [ ] Blacklist IPs which access /wp-admin and /wp-login.
- - [ ] Separate user for self-hosting, with reduced permissions.
- - [ ] Dockerize
-- [ ] Blog page (stretch goal).
-
-Major Bugs (UI, UX, or Functionality):
-
-- [x] Breathing animation of background gradient stopped. (v.0.0.1)
-- [x] Add state to skill accordions for mobile. (v.0.0.1)
-- [x] Alignment of project card on mobile. (v.0.0.1)
-- [x] PDF viewer width is constant, doesn't respond to resizing (or setting the width in CSS). (v.0.0.1)
-- [ ] More prominent slide indicator for projects (or n/10 counter, or some similar solution). (v.0.0.2)
-
-Features
-
-- [x] Refactor and reorder the HTML & CSS. (v.0.0.1)
-- [x] Template HTML blocks for repeated components. (v.0.0.1)
-- [x] Resume page (HTML and PDF options). (v.0.0.1)
-- [x] Contact page. (v.0.0.1)
-- [x] Proper 404 page (v.0.0.1)
-- [ ] Security:
- - [ ] Separate user for self-hosting, with reduced permissions. (v.0.0.1)
- - [ ] Dockerize (v.0.0.1)
-
-Future Features:
-
-- [ ] Convert SLCC e-portfolio to my own website (v.0.0.2)
-- [ ] Read in data for portfolio from a DB or preferably a plain text files. (v.0.0.2)
-- [ ] Script to easily add new projects. (v.0.0.2)
-- [ ] Demo's of various projects. (embed Java Swing app or Flutter app directly into portfolio). (v.0.0.2)
-- [ ] Blog page. (v.0.0.3)
-
-Refactoring:
-
-- [ ] Rewrite CSS into components/modules (v.0.0.2)
-
-Minor Bugs:
-
-- [ ] Contact form submission centers entire section. (v.0.0.2)
-- [ ] Profile picture animation is slightly cutoff on mobile. (v.0.0.2)
+URL: https://joshashton.dev
+Core technologies are React and Node.js, using swiper as well.
+See:
+- https://react.dev/learn
+- https://nodejs.org/en/learn/getting-started/introduction-to-nodejs
+- https://swiperjs.com/react
-COLORSCHEMES:
+
+
+
+
+- `/main.jsx` is the root and router.
+- `/App.jsx` is the primary page of my portfolio, and currently contains all application data. New projects are added here.
+- `/Home.jsx`, `/Projects.jsx`, and `/Contact.jsx` are pages (or slides) of `/App.jsx`.
+- `/components/*` are the components used to build the pages.
+
+
+
+- [x] Refactor and reorder the HTML & CSS. (v.0.0.1)
+- [x] Template HTML blocks for repeated components. (v.0.0.1)
+- [x] Contact page. (v.0.0.1)
+- [x] Proper 404 page (v.0.0.1)
+- [ ] Resume page (HTML and PDF options). (v.0.0.1)
+ - [ ] Make resume 1 page (v.0.0.1)
+- [ ] Security:
+ - [ ] Separate user for self-hosting, with reduced permissions. (v.0.0.1)
+ - [ ] Dockerize (v.0.0.1)
+
+
Future Features:
+
+- [ ] Rewrite CSS into components/modules (v.0.0.2)
+- [ ] Read in data for portfolio a plain text files. (v.0.0.2)
+- [ ] Script to easily add new projects. (v.0.0.2)
+- [ ] Demo's of various projects. (embed Java Swing app or Flutter app directly into portfolio). (v.0.0.2)
+- [ ] Blog page. (v.0.0.3)
+- [ ] Convert SLCC e-portfolio to my own website (v.0.0.4)
+
+
Bugs:
+
+- [ ] /Home.jsx, Profile picture animation is slightly cutoff on mobile. (v.0.0.2)
+- [X] /Projects.jsx, More prominent slide indicator for projects (or n/10 counter, or some similar solution). (v.0.0.2)
+- [x] /Projects.jsx, Alignment of project card on mobile. (v.0.0.1)
+- [ ] /Projects.jsx, cursor icon when hover on the slide buttons should be a pointer, not highlighter.
+- [ ] /Contact.jsx, handle blank and spam inputs
+- [ ] /Contact.jsx, Contact form submission centers entire section. (v.0.0.2)
+- [ ] /ErrorPage.jsx, stylize the error page.
+- [x] /Resume.jsx, PDF viewer width is constant, doesn't respond to resizing (or setting the width in CSS). (v.0.0.1)
+- [x] /components/SkillCard.jsx, Add state to skill accordions for mobile. (v.0.0.1)
+- [x] /main.css, Breathing animation of background gradient stopped. (v.0.0.1)
diff --git a/colorscheme.txt b/colorscheme.txt
deleted file mode 100644
index 617f844e..00000000
--- a/colorscheme.txt
+++ /dev/null
@@ -1,19 +0,0 @@
---dark-0: #170123;
---dark-1: #39124e;
---dark-2: #5e3276;
---dark-3: #84509f;
---primary: #4a245e;
---accent-1: #9e75a8;
---accent-2: #c69bca;
---text: #EBD2FF;
-
---dark-0: #091A19;
---dark-1: #10211E;
---dark-2: #162722;
---dark-3: #1C2E26;
---primary-1: #304334;
---primary: #6B825C;
---accent-0: #90D889;
---accent-1: #8FC973;
---accent-2: #B8DAA6;
---text: #E0EAD9;
diff --git a/src/App.jsx b/src/App.jsx
index d73f8ef6..d6a8c4c3 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -5,27 +5,23 @@ import Projects from "./Projects";
import Contact from "./Contact";
import "./main.css";
-/**
- * TODO: Read in data from a plain text file for easier editing in the future.
+/*
+ * Primary portfolio application. Utilizes swiper.js (via /components/VerticalSlider.jsx) to
+ * have a scrolling section to section effect.
+ *
+ * This is the primary place where (currently) application data is stored. This information will be read in
+ * from plain-text files in the future for ease of future editing. In the meantime, generated components,
+ * specifically the Skill Card and Project Card (see /components/SkillCard.jsx and /components/ProjectCard.jsx)
+ * get their data from here and thus any additions are made in this file.
+ *
+ * TODO: Read in data from a plain text file for easier editing in the future, and to separate domains.
*/
-export default function App() {
+const App = () => {
// DATA VARIABLES
- /*
- {
- title: "",
- descriptionA: "",
- featureList: [
-
- ],
- image: "",
- imageGallery: null,
- url: ""
- },
-*/
const projectData = [
{
title: "One AI",
- descriptionA:
+ description:
"An end-to-end self-hosted Artificial Intelligence solution, using Meta's Ollama platform to run Large Language Models. Go was used for the backend as a wrapper API and the frontend is Flutter. Here are some of the implemented features:",
featureList: [
"AES encryption of messages between client and server",
@@ -39,7 +35,7 @@ export default function App() {
},
{
title: "Snake",
- descriptionA:
+ description:
"A Snake clone in the command line, written in C#. Utilized a Queue data structure to keep track of the snake's body. Implemented features include:",
featureList: [
"basic pseudo-randomized obstacle generation",
@@ -53,7 +49,7 @@ export default function App() {
},
{
title: "Battery Monitor",
- descriptionA:
+ description:
"A battery monitor, written in C with a GUI written in Python. Utilizes the acpi Unix command to gather battery information. Implemented features incldue:",
featureList: [
"embed Unix commands in C to get the date, time, battery percentage, and battery status",
@@ -68,7 +64,7 @@ export default function App() {
},
{
title: "D&D Character Builder",
- descriptionA:
+ description:
"A Dungeons and Dragons character builder, following the 5th edition rules and creation process. A collaborative project creating a GUI in Java with Swing. Features include:",
featureList: [
"serialization of character data into custom filetype",
@@ -81,7 +77,7 @@ export default function App() {
},
{
title: "Calculator",
- descriptionA:
+ description:
"A simple and themable calculator written in Python. This uses CustomTKinter for the GUI and the eva CLI package for the actual calculator functionality. Supports MacOS and Linux. Implemented features include:",
featureList: [
"custom theming read in from a global .theme file",
@@ -123,9 +119,6 @@ export default function App() {
const slides = [
,
@@ -135,3 +128,5 @@ export default function App() {
return ;
}
+
+export default App;
diff --git a/src/Contact.jsx b/src/Contact.jsx
index 6c114eac..ef943e9b 100644
--- a/src/Contact.jsx
+++ b/src/Contact.jsx
@@ -1,9 +1,16 @@
import React, { useState } from "react";
-/**
- * TODO: Add email validation function.
+/*
+ * Contact page or "slide". The contact form functionality is enabled by a NodeJS server,
+ * see /server.js. A basic form for getting contact information. Emails are sent to me@joshashton.dev
+ * from contact@joshashton.dev.
+ *
+ * TODO: Handle blank and spam inputs.
+ *
+ * Managed by /App.jsx.
+ * Interacts with /server.js.
*/
-export default function Contact() {
+const Contact = () => {
const [formData, setFormData] = useState({
name: "",
emailAddress: "",
@@ -12,6 +19,7 @@ export default function Contact() {
const [isSubmitted, setIsSubmitted] = useState(false);
+ // Update form data to always be up to date with the entries on the form.
const handleChange = (event) => {
setFormData({
...formData,
@@ -19,6 +27,7 @@ export default function Contact() {
});
};
+ // Transmit data to the server's API for sending email.
const handleSubmit = async (event) => {
event.preventDefault(); // Prevent default form submission behavior
setIsSubmitted(true);
@@ -85,3 +94,5 @@ export default function Contact() {
);
}
+
+export default Contact;
diff --git a/src/ErrorPage.jsx b/src/ErrorPage.jsx
index f366d29f..a111bc88 100644
--- a/src/ErrorPage.jsx
+++ b/src/ErrorPage.jsx
@@ -1,6 +1,14 @@
import React from "react";
-export default function ErrorPage({ errorCode, errorMessage }) {
+/*
+ * An error page component, typically used for 404 errors, but available for use for any other errors.
+ *
+ * TODO: Stylize the error page.
+ *
+ * @param errorCode String
+ * @param errorMessage String
+ */
+const ErrorPage = ({ errorCode, errorMessage }) => {
return (
<>
@@ -14,3 +22,5 @@ export default function ErrorPage({ errorCode, errorMessage }) {
>
);
}
+
+export default ErrorPage;
diff --git a/src/Home.jsx b/src/Home.jsx
index 767d2c12..fa5e2e76 100644
--- a/src/Home.jsx
+++ b/src/Home.jsx
@@ -1,8 +1,19 @@
import React, { useState } from "react";
import SkillCard from "./components/SkillCard";
-export default function Home({ primaryText, secondaryText, image, skills }) {
+/**
+ * "Home" page/slide of my portfolio.
+ *
+ * Managed by /App.jsx.
+ * Data by /App.jsx.
+ *
+ * @param image String
+ * @param skills json, containing JSON values of String arrays.
+ */
+const Home = ({ image, skills }) => {
+ // Manage active card state, defaulting to languages card being active or open. Mobile only.
const [activeCardIndex, setActiveCardIndex] = useState(0);
+
return (
<>
- >
- );
-}
diff --git a/src/components/ProjectCard.jsx b/src/components/ProjectCard.jsx
index a3a12270..2475e374 100644
--- a/src/components/ProjectCard.jsx
+++ b/src/components/ProjectCard.jsx
@@ -1,15 +1,27 @@
import React from "react";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faGithub } from "@fortawesome/free-brands-svg-icons";
-export default function ProjectCard({
+/*
+ * An individual Project Card component, generated from project data in /App.jsx. Displays an image/gif
+ * of the project to the left on desktop, and above on mobile, a card. This card is simple, just a header,
+ * some description text, a list of implemented features, and a button to navigate to the GitHub for the
+ * project.
+ *
+ * Managed by /Projects.jsx.
+ * Data by /App.jsx.
+ *
+ * @param title String
+ * @param description String
+ * @param featureList String[]
+ * @param image String
+ * @param url String
+ */
+const ProjectCard = ({
title,
- descriptionA,
+ description,
featureList,
image,
- imageGallery,
url,
-}) {
+}) => {
return (
@@ -18,7 +30,7 @@ export default function ProjectCard({
{title}
-
{descriptionA}
+
{description}
{featureList.map((item, index) => (
{item}
@@ -34,3 +46,5 @@ export default function ProjectCard({
);
}
+
+export default ProjectCard;
diff --git a/src/components/SkillCard.jsx b/src/components/SkillCard.jsx
index 16c74d11..9061fa03 100644
--- a/src/components/SkillCard.jsx
+++ b/src/components/SkillCard.jsx
@@ -1,14 +1,23 @@
import React, { useState, useEffect } from "react";
-// TODO: Add state which only allows one skill card at a time to be expanded.
-// TODO: Ensure that the expandability of the skill card is only an option when @media max-width 790px (mobile)
+/**
+ * Skill Card component.
+ *
+ * On mobile, only one card at a time may be active. The currently active card is
+ * managed by the parent component, Home.jsx, via the isActive and onToggle props.
+ *
+ * @param header String, the skill card's header
+ * @param symbol String, a Material Icon from https://fonts.google.com/icons?icon.size=24&icon.color=%23e8eaed
+ * @param list String[], a list of skills
+ * @param isActive boolean, the parent's state tracking if this instance of a skill card is the active (open) one. Only applicable to mobile.
+ * @param onToggle function(), the action the parent component state takes when a skill card header is pressed. Only applicable to mobile.
+ */
const SkillCard = ({ header, symbol, list, isActive, onToggle }) => {
- // FUNCTION DATA
- const [isMobile, setIsMobile] = useState(window.innerWidth <= 790);
+ const [isMobile, setIsMobile] = useState(window.innerWidth <= 900);
useEffect(() => {
const handleResize = () => {
- const mediaQuery = window.matchMedia("(max-width: 790px)");
+ const mediaQuery = window.matchMedia("(max-width: 900px)");
setIsMobile(mediaQuery.matches);
};
@@ -18,6 +27,7 @@ const SkillCard = ({ header, symbol, list, isActive, onToggle }) => {
return () => window.removeEventListener("resize", handleResize);
}, []);
+ // Display if on desktop (!isMobile) or if Home.jsx determines this to be the currently active component.
return (
onToggle()}>
diff --git a/src/components/VerticalSlider.jsx b/src/components/VerticalSlider.jsx
index 52d0bea7..281cead9 100644
--- a/src/components/VerticalSlider.jsx
+++ b/src/components/VerticalSlider.jsx
@@ -1,11 +1,21 @@
-import React, { useRef, useState } from "react";
+import React from "react";
import { Swiper, SwiperSlide } from "swiper/react";
import { Mousewheel, Pagination } from "swiper/modules";
import "swiper/css";
import "swiper/css/pagination";
-export default function VerticalSlider({ slides }) {
+/*
+ * For my portfolio, this is the wrapper in which all other content is displayed. Every component is
+ * a child of the VerticalSlider, excluding the Resume page (for now?).
+ *
+ * Utilizes swiperjs, see https://swiperjs.com/ for documentation.
+ *
+ * @param slides ReactComponent[], an array of pages or "slides" to display in a vertical slider.
+ */
+const VerticalSlider = ({ slides }) => {
+ // Mousewheel is for scrolling effect. Pagination is for "bullets" to indicate active and inactive slides. Pagination
+ // clickable allows for going to the corresponding page or "slide".
return (
<>
);
}
+
+export default VerticalSlider;
diff --git a/src/main.css b/src/main.css
index d8f2ee6c..b774b62e 100644
--- a/src/main.css
+++ b/src/main.css
@@ -94,7 +94,7 @@ p {
.shadow {
box-shadow:
- 0 13px 27px -5px hsla(240, 30.1%, 28%, 0.25),
+ 0 13px 27px -5px var(--dark-0),
0 8px 16px -8px hsla(0, 0%, 0%, 0.3),
0 -6px 16px -6px hsla(0, 0%, 0%, 0.03);
}
@@ -352,6 +352,7 @@ p {
display: flex;
flex-direction: column;
justify-content: center;
+ margin-right: 5vw;
}
/* Tooltip container */
@@ -765,7 +766,7 @@ p {
}
}
-@media (max-width: 790px) {
+@media (max-width: 900px) {
#primary,
#slcc-splash {
@@ -785,7 +786,7 @@ p {
}
.header .header-image {
- max-height: 20vh;
+ max-height: 15vh;
width: 100%;
display: flex;
diff --git a/src/main.jsx b/src/main.jsx
index e7d4d0e0..5df50844 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -5,8 +5,11 @@ import App from "./App.jsx";
import Resume from "./Resume.jsx";
import ErrorPage from "./ErrorPage.jsx";
+/*
+ * Root React container and renderer. Also manages the router. Any new pages should be added here.
+ */
const container = document.getElementById("app");
-const root = createRoot(container); // createRoot(container!) if you use TypeScript
+const root = createRoot(container);
root.render(