From 10cdb7bb5f3776b15ec1011ac80e1f17b97c6a86 Mon Sep 17 00:00:00 2001 From: 2ManyProjects Date: Fri, 18 Apr 2025 21:42:19 -0500 Subject: [PATCH] added routing --- package.json | 2 + src/App.js | 95 ++-------- src/components/Header.js | 9 +- src/components/ProjectCard.js | 5 +- src/index.js | 1 - src/pages/Dashboard.css | 344 ++++++++++++++++++++++++++++++++++ src/pages/Dashboard.js | 78 ++++++++ src/pages/Layout.js | 37 ++++ 8 files changed, 489 insertions(+), 82 deletions(-) create mode 100644 src/pages/Dashboard.css create mode 100644 src/pages/Dashboard.js create mode 100644 src/pages/Layout.js diff --git a/package.json b/package.json index c92b492..68e1a8b 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,8 @@ "express": "^4.21.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router": "^7.5.1", + "react-router-dom": "^7.5.1", "react-scripts": "5.0.1" }, "scripts": { diff --git a/src/App.js b/src/App.js index 471134e..15468d1 100644 --- a/src/App.js +++ b/src/App.js @@ -1,80 +1,23 @@ -import React, { useState } from 'react'; -import './App.css'; -import Header from './components/Header'; -import ProjectCard from './components/ProjectCard'; -import ProjectDetails from './components/ProjectDetails'; -import forgejoLogo from './assets/forgejo.svg' -import githubLogo from './assets/github-mark-white.svg' -import LinkedinLogo from './assets/linkedIn.svg' +import ReactDOM from "react-dom/client"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import Layout from "./pages/Layout"; +import Dashboard from "./pages/Dashboard"; import projects from './data/projects'; -function App() { - const [selectedProjectId, setSelectedProjectId] = useState(null); - - const handleProjectClick = (projectId) => { - setSelectedProjectId(projectId); - window.scrollTo(0, 0); - }; - - const handleBackClick = () => { - setSelectedProjectId(null); - }; - - const selectedProject = selectedProjectId ? projects.find(p => p.id === selectedProjectId) : null; - - return ( -
-
- -
- {selectedProject ? ( - - ) : ( -
-
-

Freelance Development Experience

-

- Below is a portfolio of my freelance projects. - Each project represents solutions to complex problems across various domains. This site is hosted on my rural farm in Northern, Ontario, Canada. If the site is down its most likely due to weather conditions like a blizzard if its winter. Relavent Links in the footer unfortunately a significant amount of my professional work is locked in private git, code commit and gitlab repos. If you've been given this site please feel free to contact me for more details. -

-

- This site is a minimal React site, hosted on my own git server with a custom CI/CD pipline hooked up to pm2 for loadbalancing across a proxmox cluster (mainly for other stuff but it hosts this site too). The images are hosted via Google drive so if too many people hit the site the images will stop loading. Thanks for your understanding. - -

-
- - {projects.map(project => ( - - ))} -
- )} -
- -
-
-

© {new Date().getFullYear()} Comet Technologies | All Rights Reserved

- -
-
-
- ); +export default function App() { + return ( + + + }> + } /> + {projects.map(item => { + return(} />) + })} + + + + ); } -export default App; \ No newline at end of file +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render(); \ No newline at end of file diff --git a/src/components/Header.js b/src/components/Header.js index 6bf6b62..1cc6ed0 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -1,13 +1,16 @@ import React from 'react'; +import { Link } from "react-router-dom"; const Header = ({ onBackClick, showBackButton }) => { return (
{showBackButton && ( - + + + )}

Comet Technologies

You need it, I'll build it

diff --git a/src/components/ProjectCard.js b/src/components/ProjectCard.js index 43d6b70..9f7b1fb 100644 --- a/src/components/ProjectCard.js +++ b/src/components/ProjectCard.js @@ -1,5 +1,6 @@ import React from 'react'; import RenderChips from './RenderChips'; +import { Link } from "react-router-dom"; const ProjectCard = ({ project, onClick }) => { @@ -8,7 +9,7 @@ const ProjectCard = ({ project, onClick }) => { return ( -
onClick(project.id)}> +

{project.title}

{project.langs && } @@ -22,7 +23,7 @@ const ProjectCard = ({ project, onClick }) => {

View Details
-
+ ); }; diff --git a/src/index.js b/src/index.js index dbb893e..5851d84 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import './App.css'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); diff --git a/src/pages/Dashboard.css b/src/pages/Dashboard.css new file mode 100644 index 0000000..3d282d4 --- /dev/null +++ b/src/pages/Dashboard.css @@ -0,0 +1,344 @@ +/* Base Styles */ +* { + box-sizing: border-box; + margin: 0; + padding: 0; + } + + body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + line-height: 1.6; + color: #333; + background-color: #f8f9fa; + } + + .app { + display: flex; + flex-direction: column; + min-height: 100vh; + } + + /* Header Styles */ + .app-header { + background-color: #1a1a2e; + color: white; + padding: 2rem 1rem; + text-align: center; + } + + .header-content { + max-width: 1200px; + margin: 0 auto; + position: relative; + } + + .back-button { + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + background: none; + border: none; + color: white; + font-size: 1rem; + cursor: pointer; + padding: 0.5rem; + } + + .back-button:hover { + text-decoration: underline; + } + + .app-header h1 { + font-size: 2.5rem; + margin-bottom: 0.5rem; + } + + .tagline { + font-size: 1.1rem; + font-weight: 300; + } + + /* Main Content */ + .app-content { + flex: 1; + padding: 2rem 1rem; + max-width: 1200px; + margin: 0 auto; + width: 100%; + } + + /* Project List */ + .projects-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; + } + + .intro { + grid-column: 1 / -1; + margin-bottom: 1rem; + } + + .intro h2 { + font-size: 2rem; + margin-bottom: 1rem; + } + + .intro p { + font-size: 1.1rem; + color: #555; + } + + /* Project Card */ + .project-card { + background-color: white; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + padding: 1.5rem; + transition: transform 0.3s ease, box-shadow 0.3s ease; + cursor: pointer; + } + + .project-card:hover { + transform: translateY(-5px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15); + } + + .project-card h3 { + margin-bottom: 1rem; + color: #1a1a2e; + } + + .project-summary { + margin-bottom: 1.5rem; + color: #555; + } + + .view-details { + text-align: right; + color: #16213e; + font-weight: 500; + } + + /* Project Details */ + .project-details { + background-color: white; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + padding: 2rem; + } + + .project-details h2 { + color: #1a1a2e; + margin-bottom: 1.5rem; + font-size: 1.8rem; + } + + .project-carousel { + margin-bottom: 2rem; + } + + .project-description p { + margin-bottom: 1rem; + font-size: 1.05rem; + } + + /* Carousel Styles */ + .carousel { + display: flex; + align-items: center; + justify-content: space-between; + position: relative; + background-color: #f1f1f1; + border-radius: 4px; + overflow: hidden; + } + + .carousel-image-container { + flex: 1; + display: flex; + justify-content: center; + align-items: center; + position: relative; + height: 400px; + } + + .carousel-image { + max-width: 100%; + max-height: 100%; + object-fit: contain; + } + + .carousel-button { + background-color: rgba(0, 0, 0, 0.5); + border: none; + color: white; + font-size: 1.5rem; + padding: 0.5rem 1rem; + cursor: pointer; + height: 100%; + z-index: 2; + } + + .carousel-button:hover { + background-color: rgba(0, 0, 0, 0.7); + } + + .carousel-counter { + position: absolute; + bottom: 10px; + right: 10px; + background-color: rgba(0, 0, 0, 0.6); + color: white; + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.8rem; + } + + .carousel-loading, .carousel-empty { + display: flex; + justify-content: center; + align-items: center; + height: 300px; + background-color: #f1f1f1; + color: #555; + font-style: italic; + } + + /* Footer */ + .app-footer { + background-color: #1a1a2e; + color: white; + text-align: center; + padding: 1.5rem; + margin-top: 2rem; + } + .footer-content { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; + } + + .social-links { + display: flex; + gap: 1.5rem; + } + + .social-icon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + border: 2px solid rgba(255, 255, 255, 0.5); + color: white; + transition: all 0.3s ease; + padding: 8px; + } + + .social-icon:hover { + color: #4361ee; + border-color: #4361ee; + transform: translateY(-3px); + background-color: rgba(255, 255, 255, 0.1); + } + + .social-icon svg { + width: 20px; + height: 20px; + } + + + + /* Chip styling */ + .chips-container { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin: 2px 0; + } + + .chip { + display: inline-block; + padding: 1px 12px; + border-radius: 16px; + font-size: 0.6rem; + font-weight: 1000; + } + + .chip-large { + display: inline-block; + padding: 1px 16px; + border-radius: 16px; + font-size: 1rem; + font-weight: 750; + } + .lang-chip { + background-color: #e0f2ff; + color: #0366d6; + border: 1px solid #79b8ff; + } + + .software-chip { + background-color: #f3e8ff; + color: #5a32a3; + border: 1px solid #d1bef2; + } + + .project-card { + display: flex; + flex-direction: column; + } + + .view-details { + margin-top: auto; + text-align: right; + } + + /* Responsive styles */ + @media (max-width: 768px) { + .header-content { + padding: 0 1rem; + } + + .back-button { + position: static; + transform: none; + margin-bottom: 1rem; + display: inline-block; + } + + .project-details { + padding: 1.5rem; + } + + .carousel-image-container { + height: 300px; + } + } + + @media (max-width: 480px) { + .app-header h1 { + font-size: 2rem; + } + + .carousel-image-container { + height: 200px; + } + } + + @media (max-width: 768px) { + .footer-content { + flex-direction: column; + gap: 1rem; + } + .social-links { + margin-bottom: 1rem; + } + } \ No newline at end of file diff --git a/src/pages/Dashboard.js b/src/pages/Dashboard.js new file mode 100644 index 0000000..376d407 --- /dev/null +++ b/src/pages/Dashboard.js @@ -0,0 +1,78 @@ +import React, { useState, useEffect } from 'react'; +import './Dashboard.css'; +import Header from '../components/Header'; +import ProjectCard from '../components/ProjectCard'; +import ProjectDetails from '../components/ProjectDetails'; +import forgejoLogo from '../assets/forgejo.svg' +import githubLogo from '../assets/github-mark-white.svg' +import LinkedinLogo from '../assets/linkedIn.svg' +import projects from '../data/projects'; + +function Dashboard({projectId}) { + const [selectedProjectId, setSelectedProjectId] = useState(projectId); + + useEffect(() => {setSelectedProjectId(projectId)}, [projectId]) + + const handleProjectClick = (projectId) => { + setSelectedProjectId(projectId); + window.scrollTo(0, 0); + }; + + + const selectedProject = selectedProjectId ? projects.find(p => p.id === selectedProjectId) : null; + + return ( +
+
+ +
+ {selectedProject ? ( + + ) : ( +
+
+

Freelance Development Experience

+

+ Below is a portfolio of my freelance projects. + Each project represents solutions to complex problems across various domains. This site is hosted on my rural farm in Northern, Ontario, Canada. If the site is down its most likely due to weather conditions like a blizzard if its winter. Relavent Links in the footer unfortunately a significant amount of my professional work is locked in private git, code commit and gitlab repos. If you've been given this site please feel free to contact me for more details. +

+

+ This site is a minimal React site, hosted on my own git server with a custom CI/CD pipline hooked up to pm2 for loadbalancing across a proxmox cluster (mainly for other stuff but it hosts this site too). The images are hosted via Google drive so if too many people hit the site the images will stop loading. Thanks for your understanding. + +

+
+ + {projects.map(project => ( + + ))} +
+ )} +
+ +
+
+

© {new Date().getFullYear()} Comet Technologies | All Rights Reserved

+ +
+
+
+ ); +} + +export default Dashboard; \ No newline at end of file diff --git a/src/pages/Layout.js b/src/pages/Layout.js new file mode 100644 index 0000000..c54900c --- /dev/null +++ b/src/pages/Layout.js @@ -0,0 +1,37 @@ +import { Outlet } from "react-router-dom"; + +const Layout = () => { + return ( + <> + + + ) +}; + +export default Layout; + +/* +import ReactDOM from "react-dom/client"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import Layout from "./pages/Layout"; +import Dashboard from "./pages/Dashboard"; +import projects from './data/projects'; + +export default function App() { + return ( + + + }> + } /> + {projects.map(item => { + return(} />) + })} + + + + ); +} + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render(); +*/ \ No newline at end of file