started contact page, added resume page and pdf rendering, and default state for the skill cards on mobile.

This commit is contained in:
Joshua Ashton
2025-01-02 14:09:20 -07:00
parent 0efb297ead
commit 37cfab705b
16 changed files with 822 additions and 56 deletions
-5
View File
@@ -1,5 +0,0 @@
import React from "react";
export default function About() {
return <p>TODO: About()</p>;
}
+2
View File
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
import VerticalSlider from "./components/VerticalSlider";
import Primary from "./Primary";
import Projects from "./Projects";
import Contact from "./Contact";
import "./main.css";
import { terminal } from "virtual:terminal";
@@ -81,6 +82,7 @@ export default function App() {
skills={skillsData}
/>,
<Projects cardData={projectData} />,
<Contact />,
];
return <VerticalSlider slides={slides} />;
+60 -2
View File
@@ -1,5 +1,63 @@
import React from "react";
import React, { useState } from "react";
/**
* TODO: Add email validation function.
*/
export default function Contact() {
return <p>TODO: Contact()</p>;
const [formData, setFormData] = useState({
name: "",
email: "",
message: "",
});
const handleChange = (event) => {
setFormData({
...formData,
[event.target.name]: event.target.value,
});
};
const handleSubmit = (event) => {
event.preventDefault();
// Handle form submission here, e.g., send data to server
console.log(formData);
};
return (
<div id="contact-section">
<h1>Contact Me</h1>
<form onSubmit={handleSubmit}>
<div className="row">
<input
placeholder="Name"
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
/>
<input
placeholder="example@email.com"
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
</div>
<div className="row">
<textarea
placeholder="Message"
id="message"
name="message"
value={formData.message}
onChange={handleChange}
/>
</div>
<button type="submit" className="row">
Submit
</button>
</form>
</div>
);
}
+6 -12
View File
@@ -1,16 +1,8 @@
// TODO: Replace h4 callToAction with a GREEN button callToAction.
import React, { useState } from "react";
import SkillCard from "./components/SkillCard";
export default function Primary({
primaryText,
secondaryText,
callToAction,
image,
skills,
}) {
const [activeCardIndex, setActiveCardIndex] = useState(null);
() => setActiveCardIndex(0);
export default function Primary({ primaryText, secondaryText, image, skills }) {
const [activeCardIndex, setActiveCardIndex] = useState(0);
return (
<>
<div id="primary">
@@ -18,13 +10,15 @@ export default function Primary({
<div id="header-text">
<h1>{primaryText}</h1>
<h3>{secondaryText}</h3>
<h4>{callToAction}</h4>
<a className="button" href="/resume" target="_blank">
<div className="button-grid">Access my resume</div>
</a>
</div>
<div id="header-image">
<img id="profile" src={image} />
</div>
</div>
<div id="skills-cards">
<div id="skill-cards">
<SkillCard
header="Languages"
symbol="code"
+10
View File
@@ -0,0 +1,10 @@
import React from "react";
import PdfViewer from "./components/PdfViewer";
export default function Resume() {
return (
<>
<PdfViewer file="resume.pdf" />
</>
);
}
-8
View File
@@ -1,8 +0,0 @@
import React from "react";
export default function Skills({ skills }) {
const languages = skills.languages;
const tools = skills.tools;
const other = skills.other;
return <></>;
}
-19
View File
@@ -1,19 +0,0 @@
import React, { createContext, useState } from "react";
const AccordionContext = createContext();
export const AccordionProvider = ({ children }) => {
const [expandedCardId, setExpandedCardId] = useState(null);
const handleAccordionClick = (cardId) => {
setExpandedCardId(cardId === expandedCardId ? null : cardId);
};
return (
<AccordionContext.Provider value={{ expandedCardId, handleAccordionClick }}>
{children}
</AccordionContext.Provider>
);
};
export default AccordionContext;
View File
+22
View File
@@ -0,0 +1,22 @@
import React 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();
return (
<>
<div className="pdf-container">
<Document file={file}>
<Page pageNumber={1} />
<Page pageNumber={2} />
</Document>
</div>
</>
);
}
+16 -1
View File
@@ -1,8 +1,23 @@
import React, { useState } from "react";
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)
const SkillCard = ({ header, symbol, list, isActive, onToggle }) => {
// FUNCTION DATA
const [isMobile, setIsMobile] = useState(window.innerWidth <= 790);
useEffect(() => {
const handleResize = () => {
const mediaQuery = window.matchMedia("(max-width: 790px)");
setIsMobile(mediaQuery.matches);
};
handleResize(); // Initial check on mount
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return (
<div className="card shadow">
<div className="skill-card-header" onClick={() => onToggle()}>
+52 -5
View File
@@ -87,22 +87,31 @@ h4 {
/**SWIPER PAGES**/
.swiper {
width: 100%;
width: 100vw;
height: 100vh;
overflow: hidden;
}
.slide-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: row;
justify-content: center;
}
.mySwiper {
width: 100vw;
height: 100%;
}
/**ACTIVE PAGE ANIMATIONS**/
#header,
#skill-cards,
#project-section,
#project-cards-section {
opacity: 0;
}
.swiper-slide-active #header,
.swiper-slide-active #skill-cards,
.swiper-slide-active #project-section,
@@ -159,7 +168,7 @@ h4 {
overflow: hidden;
display: grid;
grid-template-columns: 60% 35%;
grid-template-columns: 55% 45%;
grid-template-rows: 100%;
column-gap: 2vw;
}
@@ -199,7 +208,7 @@ h4 {
animation: imgMoveDown 0.8s ease-in forwards 0.2s;
}
#skills-cards {
#skill-cards {
width: 100%;
display: grid;
place-items: center;
@@ -264,6 +273,11 @@ h4 {
grid-template-rows: 20% 80%;
}
#project-grid-container h1 {
margin-left: 5vw;
text-align: left;
}
#project-cards-section {
display: flex;
flex-direction: column;
@@ -347,8 +361,7 @@ h4 {
}
.button {
height: 100%;
width: 50%;
width: 80%;
color: #4a245e;
background-color: #ebd2ff;
border-radius: 18px;
@@ -359,6 +372,31 @@ h4 {
flex-direction: row;
align-items: center;
justify-content: center;
padding: 1vw;
}
/**Contact Section**/
#contact-section {
height: 90vh;
width: 100%;
margin: 5vw;
}
#contact-section h1 {
margin-left: 5vw;
}
#contact-section form {
border: 1px solid white;
height: 70vh;
}
/**PDF Viewer**/
.pdf-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
/**Animations**/
@@ -524,6 +562,15 @@ h4 {
.project-text-column {
margin-left: 5vw;
}
.pdf-viewer {
max-width: 100%;
margin-right: 0;
}
.pdf-page {
width: 100%;
}
}
@media (max-width: 485px) {
+13 -3
View File
@@ -1,6 +1,16 @@
import React from "react";
import ReactDOM from "react-dom";
import { createRoot } from "react-dom/client";
import { BrowserRouter as Router, Routes, Route } from "react-router";
import App from "./App.jsx";
import Resume from "./Resume.jsx";
ReactDOM.render(<App />, document.getElementById("app"));
const container = document.getElementById("app");
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(
<Router>
<Routes>
<Route path="/" element={<App />} />
<Route path="/resume" element={<Resume />} />
</Routes>
</Router>,
);