diff --git a/src/App.jsx b/src/App.jsx index d6a8c4c3..457e524c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -9,118 +9,17 @@ import "./main.css"; * 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. + * Managed by /main.jsx. + * Data by /main.jsx. * - * TODO: Read in data from a plain text file for easier editing in the future, and to separate domains. + * @param projectData JSON[], data necessary to create project cards. + * @param skillData JSON, data necessary to create skill cards. */ -const App = () => { - // DATA VARIABLES - const projectData = [ - { - title: "One AI", - 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", - "SHA-256 hashing for message validation", - "Base64 encoding for file uploads to the server", - "Docker container for isolation and security", - ], - image: "/images/one-ai-cropped.png", - imageGallery: null, - url: "https://github.com/quaxlyqueen/one-ai", - }, - { - title: "Snake", - 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", - "difficulty selection by adjusting the frequency of obstacle generation", - "keyboard input to move the snake", - "ASCII art for the menu and game over state", - ], - image: "/images/snake.gif", - imageGallery: null, - url: "https://github.com/quaxlyqueen/2410-Snake", - }, - { - title: "Battery Monitor", - 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", - "basic file I/O to store historical battery data", - "daemonize to run as a background process", - "script to install as a SystemD service", - "basic Python GUI in TKinter to provide graph of data", - ], - image: "/images/battery-mon.png", - imageGallery: null, - url: "https://github.com/quaxlyqueen/battery-mon", - }, - { - title: "D&D Character Builder", - 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", - "global styling and custom font import", - "button hover effects", - ], - image: "/images/dnd.png", - imageGallery: null, - url: "https://github.com/quaxlyqueen/dnd", - }, - { - title: "Calculator", - 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", - "button hover effects", - "keyboard input", - ], - image: "/images/calculator.gif", - imageGallery: null, - url: "https://github.com/quaxlyqueen/calculator", - }, - ]; - - const skillsData = { - languages: [ - "C & C#", - "HTML & CSS", - "Go", - "Java", - "JavaScript & Dart", - "Python", - ], - tools: [ - "Git & GitHub", - "Docker", - "Flutter & React", - "Linux & NeoVim", - "Cloudflare", - "Microsoft Intune", - ], - other: [ - "Written Communication", - "Conflict Resolution", - "Project Management", - "Event Planning", - "Electronics Repair", - "Soldering", - ], - }; - +const App = ({ projectData, skillData }) => { const slides = [ , , , diff --git a/src/Home.jsx b/src/Home.jsx index fa5e2e76..1b39ed65 100644 --- a/src/Home.jsx +++ b/src/Home.jsx @@ -5,7 +5,7 @@ import SkillCard from "./components/SkillCard"; * "Home" page/slide of my portfolio. * * Managed by /App.jsx. - * Data by /App.jsx. + * Data by /main.jsx. * * @param image String * @param skills json, containing JSON values of String arrays. @@ -18,10 +18,10 @@ const Home = ({ image, skills }) => { <>
-
+
-
+

Hi, I'm Josh Ashton.

Junior full-stack developer.

@@ -32,7 +32,7 @@ const Home = ({ image, skills }) => {
-
+
{ return ( <>
-

Resume

- +

Josh Ashton

+

Junior Full-Stack Developer

+

Work Experience

+
+ {jobData.map((job, index) => ( + + ))} +
+

Education

+
+ {eduData.map((edu, index) => ( + + ))} +
+

Skills

+
); } + +function Skill({ languages, tools, other }) { + return ( + <> +
+
+

Languages:

+

{languages.join(", ")}

+
+
+

Tools:

+

{tools.join(", ")}

+
+
+

Other:

+

{other.join(", ")}

+
+
+ + ); +} + +function Education({ institution, degree, gpa, start, end }) { + return ( + <> + +
+
+
+
+

{institution}

+
- {degree}
+
+
{start} - {end}
+
+
{gpa}
+
+
+ + ); +} + +function Job({ company, title, start, end, location, responsibilities }) { + return ( + <> +
+
+
+
+

{company}

+
- {location}
+
+
{start} - {end}
+
+
{title}
+
+
    + {responsibilities.map((r, index) => ( +
  • {r}
  • + ))} +
+
+ + ) +} + +export default Resume; diff --git a/src/components/PdfViewer.jsx b/src/components/PdfViewer.jsx deleted file mode 100644 index ad1b3ec3..00000000 --- a/src/components/PdfViewer.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { useState } from "react"; -import { Document, Page, pdfjs } from "react-pdf"; -import "react-pdf/dist/Page/TextLayer.css"; -import "react-pdf/dist/Page/AnnotationLayer.css"; - -export default function PdfViewer({ file }) { - pdfjs.GlobalWorkerOptions.workerSrc = new URL( - "pdfjs-dist/build/pdf.worker.min.mjs", - import.meta.url, - ).toString(); - - const [numPages, setNumPages] = useState(0); - const [pageNumber, setPageNumber] = useState(1); - function prev() { - if (pageNumber >= 2) { - setPageNumber(pageNumber - 1); - } - } - - function next() { - if (pageNumber < numPages) { - setPageNumber(pageNumber + 1); - } - } - - return ( - <> -
- { - setNumPages(numPagesInPdf); - }} - className="pdf-document" - > - - -
- - -
-
- - ); -} diff --git a/src/components/ProjectCard.jsx b/src/components/ProjectCard.jsx index 2475e374..ffc8d0ae 100644 --- a/src/components/ProjectCard.jsx +++ b/src/components/ProjectCard.jsx @@ -7,7 +7,7 @@ import React from "react"; * project. * * Managed by /Projects.jsx. - * Data by /App.jsx. + * Data by /main.jsx > /App.jsx > /Projects.jsx. * * @param title String * @param description String diff --git a/src/components/SkillCard.jsx b/src/components/SkillCard.jsx index 9061fa03..7dc38ae4 100644 --- a/src/components/SkillCard.jsx +++ b/src/components/SkillCard.jsx @@ -5,6 +5,9 @@ import React, { useState, useEffect } from "react"; * * 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. + * + * Managed by /Home.jsx. + * Data by /main.jsx > /App.jsx > /Home.jsx * * @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 @@ -32,7 +35,7 @@ const SkillCard = ({ header, symbol, list, isActive, onToggle }) => {
onToggle()}> {symbol} -

{header}

+

{header}

{(isActive || !isMobile) && (
    diff --git a/src/main.css b/src/main.css index b774b62e..5bae4aaa 100644 --- a/src/main.css +++ b/src/main.css @@ -41,39 +41,65 @@ body { background-attachment: fixed; } -h1 { - font-size: clamp(4.25rem, 5.5vw, 10rem); - font-weight: 600; - letter-spacing: 2px; - width: max-content; +h1, +h2, +h3, +h4, +h5, +h6, +p, +li { color: var(--text); } -h3, -h4 { - color: var(--text); - font-weight: 300; - line-height: 1.5; +h1 { + font-size: clamp(4rem, 5vw, 10rem); + font-weight: 600; + letter-spacing: 2px; + width: max-content; +} + +h2, +h3 { + font-weight: 500; + letter-spacing: 2px; + width: max-content; } h2 { - font-size: clamp(2rem, 3vw, 4rem); - font-weight: 600; - letter-spacing: 2px; - width: max-content; - color: var(--text); + font-size: clamp(3rem, 4vw, 7rem); } h3 { - font-size: clamp(1.5rem, 2vw, 3rem); + font-size: clamp(2rem, 3rem, 6rem); } h4 { - font-size: clamp(1rem, 1.5vw, 1.25rem); + font-weight: 400; + line-height: 1.5; } -p { - font-size: clamp(1rem, 1.5rem, 2rem); +h5, +h6 { + font-weight: 300; +} + +h4 { + font-size: clamp(1rem, 1.5vw, 4rem); +} + +h5 { + font-size: clamp(1rem, 1.25vw, 3.5rem); +} + +h6 { + font-size: clamp(1rem, 1.125vw, 2rem); +} + +p, +li { + font-weight: 300; + font-size: clamp(1rem, 1.1vw, 1.5rem); color: var(--text); } @@ -139,7 +165,7 @@ p { } .mySwiper { - width: 100vw; + width: 100%; height: 100%; } @@ -278,8 +304,8 @@ p { border-radius: 18px 18px 0 0; } -.skill-card-header h3 { - font-size: clamp(1.3rem, 2vw, 3rem); +.skill-card-header h4 { + font-weight: 700; text-align: right; } @@ -299,7 +325,6 @@ p { .card ul li { list-style: none; - font-size: clamp(1rem, 1.25vw, 1.5rem); color: var(--dark-0); } @@ -352,7 +377,6 @@ p { display: flex; flex-direction: column; justify-content: center; - margin-right: 5vw; } /* Tooltip container */ @@ -424,6 +448,7 @@ p { } .project-text p { + font-weight: 400; display: flex; flex-direction: column; justify-content: center; @@ -447,7 +472,6 @@ p { } .feature-list li { - font-size: clamp(1rem, 1.5rem, 2rem); color: var(--text); } @@ -530,6 +554,8 @@ p { #resume-section { margin: 2vw; display: flex; + flex-direction: column; + align-items: center; } /**PDF Viewer**/ @@ -692,6 +718,65 @@ p { width: 50%; } +.jobs, +.education-section { + border-top: 1px solid var(--accent-0); + width: 100%; +} + +.job, +.edu { + width: 100%; + padding: 0vw 1vw 1vw 1vw; +} + +.job-header-row, +.edu-header-row { + display: grid; + grid-template-columns: 70% 30%; + align-items: center; +} + +.job-time, +.edu-time { + justify-self: right; +} + +.company-and-location, +.edu-and-degree { + display: flex; + align-items: center; +} + +.company-and-location h6, +.edu-and-degree h6 { + margin-left: .25vw; +} + +.job-list { + margin: 0 auto; + width: 90%; +} + +.job-list-item { + font-weight: 300; +} + +.resume-skills { + border-top: 1px solid var(--accent-0); + width: 100%; +} + +.resume-skills-skill { + display: flex; + align-items: center; +} + +.resume-skills-skill p { + margin-left: .5vw; + padding: 1vw; +} + /**Animations**/ @keyframes background-fade { from { diff --git a/src/main.jsx b/src/main.jsx index 5df50844..a6eb30de 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -6,15 +6,158 @@ 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. + * Root React container and renderer. + * + * Contains all data and data processing, as well as routing to different pages. + * TODO: Read data in from plain text files. */ const container = document.getElementById("app"); const root = createRoot(container); + +const projectData = [ + { + title: "One AI", + 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", + "SHA-256 hashing for message validation", + "Base64 encoding for file uploads to the server", + "Docker container for isolation and security", + ], + image: "/images/one-ai-cropped.png", + imageGallery: null, + url: "https://github.com/quaxlyqueen/one-ai", + }, + { + title: "Snake", + 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", + "difficulty selection by adjusting the frequency of obstacle generation", + "keyboard input to move the snake", + "ASCII art for the menu and game over state", + ], + image: "/images/snake.gif", + imageGallery: null, + url: "https://github.com/quaxlyqueen/2410-Snake", + }, + { + title: "Battery Monitor", + 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", + "basic file I/O to store historical battery data", + "daemonize to run as a background process", + "script to install as a SystemD service", + "basic Python GUI in TKinter to provide graph of data", + ], + image: "/images/battery-mon.png", + imageGallery: null, + url: "https://github.com/quaxlyqueen/battery-mon", + }, + { + title: "D&D Character Builder", + 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", + "global styling and custom font import", + "button hover effects", + ], + image: "/images/dnd.png", + imageGallery: null, + url: "https://github.com/quaxlyqueen/dnd", + }, + { + title: "Calculator", + 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", + "button hover effects", + "keyboard input", + ], + image: "/images/calculator.gif", + imageGallery: null, + url: "https://github.com/quaxlyqueen/calculator", + }, +]; + +const skillData = { + languages: [ + "C & C#", + "HTML & CSS", + "Go", + "Java", + "JavaScript & Dart", + "Python", + ], + tools: [ + "Git & GitHub", + "Docker", + "Flutter & React", + "Linux & NeoVim", + "Cloudflare", + "Microsoft Intune", + ], + other: [ + "Written Communication", + "Conflict Resolution", + "Project Management", + "Event Planning", + "Electronics Repair", + "Soldering", + ], +}; + +const jobData = [ + { + company: "SLC Public Library", + title: "Technology & Digital Equity Assistant", + start: "Aug '23", + end: "Present", + location: "Salt Lake City, UT", + responsibilities: [ + "Assist library patrons with using technology, on devices such as publicly available PCs, laptops, mobile devices, and more.", + "Teach fundamental computer usage skills.", + "Expose patrons to the importance of digital security, password management, and two-factor authentication.", + "Developing workshop curriculum for Raspberry Pi’s." + ], + }, + { + company: "Trust Lands Admin.", + title: "IT Intern", + start: "Aug '23", + end: "Aug '24", + location: "Salt Lake City, UT", + responsibilities: [ + "Orchestrated Microsoft Intune setup and deployment to multiple platforms.", + "Streamlined IT equipment inventory process", + "Provided training to optimize the usage of hardware and software.", + "Troubleshoot and repair hardware and software on a variety of devices.", + "Developed automation scripts for use in various parts of the business.", + ], + }, +]; + +const eduData = [ + { + institution: "Salt Lake Comm. College", + degree: "AS Comp Sci", + gpa: "3.48 (CS) || 2.87 (Cumulative)", + start: "Jan '21", + end: "Present", + } +]; + root.render( - } /> - } /> + } /> + } />