From 02fd54ca5df4c96306a4cae7c3934b68d45cb111 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 8 Jan 2025 13:43:37 -0700 Subject: [PATCH] documentation and README.md updates. --- README.md | 162 ++++++++++++++++-------------- colorscheme.txt | 19 ---- src/App.jsx | 41 ++++---- src/Contact.jsx | 17 +++- src/ErrorPage.jsx | 12 ++- src/Home.jsx | 71 +++++++------ src/Projects.jsx | 27 +++-- src/Resume.jsx | 6 ++ src/components/Error.jsx | 16 --- src/components/ProjectCard.jsx | 28 ++++-- src/components/SkillCard.jsx | 20 +++- src/components/VerticalSlider.jsx | 16 ++- src/main.css | 7 +- src/main.jsx | 5 +- 14 files changed, 256 insertions(+), 191 deletions(-) delete mode 100644 colorscheme.txt delete mode 100644 src/components/Error.jsx 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: +
+ +

Documentation

+ +

Setup

+ +

Development Environment

+ +``` +git clone https://github.com/quaxlyqueen/portfolio +cd portfolio +npm install +node src/server.js & +npm start +``` + +

Production Environment

+ +``` +git clone https://github.com/quaxlyqueen/portfolio +cd portfolio +npm run build +cd dist +node ../src/server.js & +serve -s +``` + +

Project structure

+ +- `/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. + +

Styling guidelines:

+ +

Colorscheme data:

+ +
Purple
+ +``` --dark-0: #170123; --dark-1: #39124e; --dark-2: #5e3276; @@ -83,7 +58,11 @@ COLORSCHEMES: --accent-1: #9e75a8; --accent-2: #c69bca; --text: #EBD2FF; +``` +
Green
+ +``` --dark-0: #091A19; --dark-1: #10211E; --dark-2: #162722; @@ -94,3 +73,40 @@ COLORSCHEMES: --accent-1: #8FC973; --accent-2: #B8DAA6; --text: #E0EAD9; +``` + +

TODO

+ +

Features

+ +- [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 ( <>
@@ -12,43 +23,39 @@ export default function Home({ primaryText, secondaryText, image, skills }) {
-

{primaryText}

-

{secondaryText}

+

Hi, I'm Josh Ashton.

+

Junior full-stack developer.

Access my resume
- {skills.languages !== null ? ( -
-
- setActiveCardIndex(0)} - /> - setActiveCardIndex(1)} - /> - setActiveCardIndex(2)} - /> -
+
+
+ setActiveCardIndex(0)} + /> + setActiveCardIndex(1)} + /> + setActiveCardIndex(2)} + />
- ) : ( -

Something here

- )} +
keyboard_double_arrow_down @@ -56,3 +63,5 @@ export default function Home({ primaryText, secondaryText, image, skills }) { ); } + +export default Home; diff --git a/src/Projects.jsx b/src/Projects.jsx index 5df1c78a..c5c57e2a 100644 --- a/src/Projects.jsx +++ b/src/Projects.jsx @@ -1,12 +1,25 @@ import React, { useRef } from "react"; import ProjectCard from "./components/ProjectCard"; -import { Swiper, SwiperSlide, useSwiper } from "swiper/react"; -import { Navigation, Scrollbar } from "swiper/modules"; +import { Swiper, SwiperSlide } from "swiper/react"; +import { Scrollbar } from "swiper/modules"; import "swiper/css"; import "swiper/css/effect-cards"; import "swiper/css/scrollbar"; -export default function Projects({ cardData }) { +/* + * The Projects page or "slide". Implements swiperjs (see documentation at https://swiperjs.com/) to create a nested + * slider/swiper inside of the root VerticalSlider. This nested slider is horizontal, and displays Project Cards + * (see /components/ProjectCard.jsx), generated for each element of the parameter cardData. + * + * Managed by /App.jsx. + * Data by /App.jsx. + * + * @param cardData json[], each object contains the following: title, descriptionA, featureList, image, url + * + * TODO: Cursor icon when hover on the slide buttons should be a pointer, not highlighter. + */ +const Projects = ({ cardData }) => { + // Reference the nested slider instead of the root slider. const projectSwiperRef = useRef(null); const prevSlide = () => { @@ -44,8 +57,7 @@ export default function Projects({ cardData }) {
@@ -71,3 +82,5 @@ export default function Projects({ cardData }) { ); } + +export default Projects; diff --git a/src/Resume.jsx b/src/Resume.jsx index 50c4850a..b38424bc 100644 --- a/src/Resume.jsx +++ b/src/Resume.jsx @@ -1,6 +1,12 @@ import React from "react"; import PdfViewer from "./components/PdfViewer"; +/* + * Page to display my Resume. + * + * TODO: Rework resume to make it one page + * TODO: Convert resume to a React component, with a link to save as PDF + */ export default function Resume() { return ( <> diff --git a/src/components/Error.jsx b/src/components/Error.jsx deleted file mode 100644 index 18d1b9d2..00000000 --- a/src/components/Error.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; - -export default function Error({ errorCode, message }) { - return ( - <> -
-

{errorCode}

-

{message}

- - -
Return to joshashton.dev
-
-
- - ); -} 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(