diff --git a/README.md b/README.md index ca583d6..08310a4 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,12 @@ # To do -## For V1 : -- Revoir les titres de section (+espace au dessus des titres) - -## For V2 : -- Mettre la base de donnée en place -- Refaire en conséquence les choses nécessaires - -## For V3 : -- Faire une page pour lister tous les projets +- Refaire les images qui vont pas - Faire une page explicative par projet +- Faire du responsive + +## Plus tard +- Régler problème de scoll entre page +- Refaire la section des skills ## Usefull commands diff --git a/package-lock.json b/package-lock.json index ad5d14d..2a8e3ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "dependencies": { "react": "^19.1.0", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "react-router-dom": "^7.8.2" }, "devDependencies": { "@eslint/js": "^9.25.0", @@ -1600,6 +1601,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2471,6 +2481,44 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.2.tgz", + "integrity": "sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.8.2.tgz", + "integrity": "sha512-Z4VM5mKDipal2jQ385H6UBhiiEDlnJPx6jyWsTYoZQdl5TrjxEV2a9yl3Fi60NBJxYzOTGTTHXPi0pdizvTwow==", + "license": "MIT", + "dependencies": { + "react-router": "7.8.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2544,6 +2592,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index 385876e..00752b0 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^19.1.0", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "react-router-dom": "^7.8.2" }, "devDependencies": { "@eslint/js": "^9.25.0", diff --git a/public/assets/images/SAE_C++/SAE_C++_1.png b/public/assets/images/SAE_C++/SAE_C++_1.png deleted file mode 100644 index 1f6368d..0000000 Binary files a/public/assets/images/SAE_C++/SAE_C++_1.png and /dev/null differ diff --git a/public/assets/images/SAE_C++/SAE_C++_2.png b/public/assets/images/SAE_C++/SAE_C++_2.png deleted file mode 100644 index 3804acf..0000000 Binary files a/public/assets/images/SAE_C++/SAE_C++_2.png and /dev/null differ diff --git a/public/assets/images/SAE_C++/SAE_C++_3.png b/public/assets/images/SAE_C++/SAE_C++_3.png deleted file mode 100644 index 9af9166..0000000 Binary files a/public/assets/images/SAE_C++/SAE_C++_3.png and /dev/null differ diff --git a/public/assets/images/codev/codev_1.png b/public/assets/images/codev/codev_1.png deleted file mode 100644 index e8cec9a..0000000 Binary files a/public/assets/images/codev/codev_1.png and /dev/null differ diff --git a/public/assets/images/codev/codev_2.png b/public/assets/images/codev/codev_2.png deleted file mode 100644 index 9c78b67..0000000 Binary files a/public/assets/images/codev/codev_2.png and /dev/null differ diff --git a/public/assets/images/codev/codev_3.png b/public/assets/images/codev/codev_3.png deleted file mode 100644 index b359a96..0000000 Binary files a/public/assets/images/codev/codev_3.png and /dev/null differ diff --git a/public/assets/images/codev/codev_4.png b/public/assets/images/codev/codev_4.png deleted file mode 100644 index c7620b4..0000000 Binary files a/public/assets/images/codev/codev_4.png and /dev/null differ diff --git a/public/assets/images/codev/codev_5.png b/public/assets/images/codev/codev_5.png deleted file mode 100644 index 86a1d0a..0000000 Binary files a/public/assets/images/codev/codev_5.png and /dev/null differ diff --git a/public/assets/images/proxmox/proxmox_1.png b/public/assets/images/proxmox/proxmox_1.png deleted file mode 100644 index d9d5ebd..0000000 Binary files a/public/assets/images/proxmox/proxmox_1.png and /dev/null differ diff --git a/public/assets/images/proxmox/proxmox_2.png b/public/assets/images/proxmox/proxmox_2.png deleted file mode 100644 index e887f8e..0000000 Binary files a/public/assets/images/proxmox/proxmox_2.png and /dev/null differ diff --git a/public/assets/images/proxmox/proxmox_3.png b/public/assets/images/proxmox/proxmox_3.png deleted file mode 100644 index 093598a..0000000 Binary files a/public/assets/images/proxmox/proxmox_3.png and /dev/null differ diff --git a/public/assets/images/proxmox/proxmox_4.png b/public/assets/images/proxmox/proxmox_4.png deleted file mode 100644 index f2a2a89..0000000 Binary files a/public/assets/images/proxmox/proxmox_4.png and /dev/null differ diff --git a/public/assets/images/proxmox/proxmox_5.png b/public/assets/images/proxmox/proxmox_5.png deleted file mode 100644 index c968f4b..0000000 Binary files a/public/assets/images/proxmox/proxmox_5.png and /dev/null differ diff --git a/src/App.jsx b/src/App.jsx index fff1480..3eb8608 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,20 +1,15 @@ import './App.css' -import Home from './components/Home.jsx' -import Experiences from './components/Experiences.jsx' -import Projects from './components/Projects.jsx' -import Skills from './components/Skills.jsx' -import Footer from './components/Footer.jsx' +import { Routes, Route, Link } from 'react-router-dom'; +import HomePage from './pages/HomePage.jsx'; +import ProjectsPage from './pages/ProjectsPage'; function App() { return ( -
- - - - -
+ + } /> + } /> + ) } diff --git a/src/styles/Experiences.css b/src/components/Experiences/Experiences.css similarity index 100% rename from src/styles/Experiences.css rename to src/components/Experiences/Experiences.css diff --git a/src/components/Experiences.jsx b/src/components/Experiences/Experiences.jsx similarity index 89% rename from src/components/Experiences.jsx rename to src/components/Experiences/Experiences.jsx index be29a31..857ba60 100644 --- a/src/components/Experiences.jsx +++ b/src/components/Experiences/Experiences.jsx @@ -1,5 +1,5 @@ -import '../styles/Experiences.css'; -import SingleExperience from './SingleExperience'; +import './Experiences.css'; +import SingleExperience from '../SingleExperience/SingleExperience.jsx'; import React, {useEffect, useState} from "react"; function Experiences() { @@ -31,7 +31,7 @@ function Experiences() { }, []); if (error) { - return
Erreur lors de la récupération des données : {error}
; + return
Error retrieving data: {error}
; } return ( diff --git a/src/components/Filter/Filter.css b/src/components/Filter/Filter.css new file mode 100644 index 0000000..f1e1937 --- /dev/null +++ b/src/components/Filter/Filter.css @@ -0,0 +1,127 @@ +.filter-btn { + position: absolute; + top: 2em; + right: 20px; + border: none; + background: none; + padding: 14px; + cursor: pointer; + z-index: 10; + transition: background-color 0.3s ease; +} + +.filter-btn svg { + color: var(--text-color); + transition: all 0.3s ease; +} + +.filter-btn svg:hover { + color: var(--title-color); + transform: translateY(-2px); +} + + +.filter-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(5px); + z-index: 100; +} + +.filter-sidebar { + position: fixed; + top: 0; + right: -100%; + width: 100%; + max-width: 350px; + height: 100%; + background: rgba(42, 42, 42, 0.5); + backdrop-filter: blur(15px); + border-left: solid rgba(100,100,100,0.5) 1px; + z-index: 101; + display: flex; + flex-direction: column; + transition: right 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); +} + + +.filter-sidebar.visible { + right: 0; +} + +.filter-sidebar-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 1.5rem; + border-bottom: 1px solid rgba(100, 100, 100, 0.5); +} + + +.close-filter-btn { + background: none; + border: none; + color: var(--text-color); + cursor: pointer; + padding: 5px; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.3s ease; +} + +.close-filter-btn:hover { + color: var(--title-color); +} + +.filter-sidebar-content { + padding: 1.5rem; + overflow-y: auto; +} + +.filter-sidebar-content h4 { + color: var(--text-color); + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 1px; + margin-top: 0; + margin-bottom: 1rem; + display: flex; + justify-content: start; +} + +.filter-group { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin-bottom: 2rem; +} + +.filter-tag { + background-color: transparent; + border: 1px solid var(--text-color); + color: var(--text-color); + padding: 8px 16px; + border-radius: 20px; + cursor: pointer; + font-size: 0.9rem; + font-weight: 600; + text-decoration: none; + transition: all 0.3s ease; +} + +.filter-tag:hover { + background-color: var(--text-color); + color: #1E1E1E; + transform: translateY(-2px); +} + +.filter-tag.active { + background-color: rgba(217, 95, 70, 0.3); + color: var(--important-color); + border-color: var(--important-color); +} \ No newline at end of file diff --git a/src/components/Filter/Filter.jsx b/src/components/Filter/Filter.jsx new file mode 100644 index 0000000..58764b1 --- /dev/null +++ b/src/components/Filter/Filter.jsx @@ -0,0 +1,154 @@ +import './Filter.css'; +import React, {useEffect, useState} from "react"; + +function Filter({ filters, setFilters }) { + const [isFilterVisible, setIsFilterVisible] = useState(false); + const showFilters = () => setIsFilterVisible(true); + const hideFilters = () => setIsFilterVisible(false); + const [skills, setSkills] = useState([]); + const [error, setError] = useState(null); + + + const handleCategoryFilter = (category) => { + setFilters(prev => { + const alreadySelected = prev.category.includes(category); + return { + ...prev, + category: alreadySelected + ? prev.category.filter(c => c !== category) + : [...prev.category, category], + }; + }); + }; + + const toggleTechnologyFilter = (tech) => { + setFilters(prev => { + const alreadySelected = prev.technology.includes(tech); + return { + ...prev, + technology: alreadySelected + ? prev.technology.filter(t => t !== tech) + : [...prev.technology, tech], + }; + }); + }; + + const setYearOrder = (order) => { + setFilters(prev => ({ + ...prev, + yearOrder: prev.yearOrder === order ? null : order + })); + }; + + const resetFilters = () => { + setFilters({ + category: [], + technology: [], + yearOrder: filters.yearOrder, + }); + }; + + + + useEffect(() => { + if (isFilterVisible) { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = ''; + } + + return () => { + document.body.style.overflow = ''; + }; + }, [isFilterVisible]); + + + + + useEffect(() => { + const fetchSkills = async () => { + try { + const response = await fetch('/api/skills/'); + if (!response.ok) { + throw new Error(`Erreur HTTP: ${response.status}`); + } + const data = await response.json(); + setSkills(data.data); + } catch (err) { + setError(err.message); + } + }; + fetchSkills(); + }, []); + + if (error) { + return
Error retrieving data: {error}
; + } + + + + return ( +
+ + + {isFilterVisible ?
: null} + +
+
+

Filter Projects

+ +
+
+
+ + +
+

Category

+
+ + +
+

Technology

+
+ {skills.map(skill => ( + + ))} +
+

YEAR

+
+ + +
+
+
+
+ ); +} + +export default Filter; \ No newline at end of file diff --git a/src/styles/Footer.css b/src/components/Footer/Footer.css similarity index 86% rename from src/styles/Footer.css rename to src/components/Footer/Footer.css index 6760a72..5cc13d2 100644 --- a/src/styles/Footer.css +++ b/src/components/Footer/Footer.css @@ -2,7 +2,7 @@ padding: 2rem 0; margin-top: 4rem; border-top: 1px solid #333; - color: #B0B0B0; + color: var(--text-color); font-size: 0.9rem; } @@ -22,7 +22,7 @@ display: flex; align-items: center; justify-content: center; - color: #B0B0B0; + color: var(--text-color); transition: color 0.3s ease, transform 0.3s ease; svg{ width: 24px; @@ -32,6 +32,6 @@ } .footer-links a:hover { - color: #EAEAEA; + color: var(--title-color); transform: translateY(-3px); } \ No newline at end of file diff --git a/src/components/Footer.jsx b/src/components/Footer/Footer.jsx similarity index 99% rename from src/components/Footer.jsx rename to src/components/Footer/Footer.jsx index ef9b50f..6a0845c 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer/Footer.jsx @@ -1,4 +1,4 @@ -import '../styles/Footer.css'; +import './Footer.css'; function Footer() { const currentYear = new Date().getFullYear(); diff --git a/src/styles/Home.css b/src/components/Home/Home.css similarity index 80% rename from src/styles/Home.css rename to src/components/Home/Home.css index 0d119bd..66469ca 100644 --- a/src/styles/Home.css +++ b/src/components/Home/Home.css @@ -16,19 +16,19 @@ #home-section h2 { font-size: 1.8rem; - color: #EAEAEA; + color: var(--title-color); margin-bottom: 1rem; } #home-section p { margin-bottom: 2rem; - color: #EAEAEA; + color: var(--title-color); max-width: 40%; } .highlight { - color: #D95F46; + color: var(--important-color); } .btn { @@ -37,8 +37,8 @@ border-radius: 5px; text-decoration: none; font-weight: bold; - background-color: #D95F46; - color: #fff; + background-color: var(--important-color); + color: white; transition: background-color 0.3s ease; margin-bottom: 200px; } diff --git a/src/components/Home.jsx b/src/components/Home/Home.jsx similarity index 80% rename from src/components/Home.jsx rename to src/components/Home/Home.jsx index 1a0bdf1..bec6881 100644 --- a/src/components/Home.jsx +++ b/src/components/Home/Home.jsx @@ -1,6 +1,6 @@ -import '../styles/Home.css'; -import Background from "./thirdParty/Background.jsx"; -import NavBar from "./NavBar.jsx"; +import './Home.css'; +import Background from "../thirdParty/Background/Background.jsx"; +import NavBar from "../NavBar/NavBar.jsx"; function Home() { return ( diff --git a/src/styles/NavBar.css b/src/components/NavBar/NavBar.css similarity index 85% rename from src/styles/NavBar.css rename to src/components/NavBar/NavBar.css index 934c704..f0d13ec 100644 --- a/src/styles/NavBar.css +++ b/src/components/NavBar/NavBar.css @@ -5,7 +5,7 @@ backdrop-filter: blur(12px); border-radius: 25px; padding: 6px 10px; - z-index: 1000; + z-index: 100; border: solid rgba(100,100,100,0.5) 0.001rem; } @@ -25,7 +25,7 @@ text-decoration: none; padding: 0.3rem 0.8rem; border-radius: 20px; - color: #B0B0B0; + color: var(--text-color); transition: background-color 0.3s ease, color 0.3s ease; cursor: pointer; border: none; @@ -34,10 +34,10 @@ } .nav-link:hover { - color: #D95F46; + color: var(--important-color); } .nav-link.active { background-color: rgba(217, 95, 70, 0.3); - color: #D95F46; + color: var(--important-color); } \ No newline at end of file diff --git a/src/components/NavBar.jsx b/src/components/NavBar/NavBar.jsx similarity index 55% rename from src/components/NavBar.jsx rename to src/components/NavBar/NavBar.jsx index b9f50ee..42796d1 100644 --- a/src/components/NavBar.jsx +++ b/src/components/NavBar/NavBar.jsx @@ -1,44 +1,51 @@ import { useEffect, useState } from "react"; -import "../styles/NavBar.css"; +import { useNavigate } from "react-router-dom"; +import "./NavBar.css"; const NavBar = () => { const [active, setActive] = useState("home-section"); const [isScrolling, setIsScrolling] = useState(false); + const navigate = useNavigate(); + useEffect(() => { - const sections = document.querySelectorAll("section"); - const navHeight = document.querySelector(".navbar")?.offsetHeight + 40 || 120; + if (location.pathname === '/') { + const sections = document.querySelectorAll("section"); + const navHeight = document.querySelector(".navbar")?.offsetHeight + 40 || 120; - const handleScroll = () => { - if (isScrolling) return; + const handleScroll = () => { + if (isScrolling) return; - let current = "home-section"; - sections.forEach((section) => { - const sectionTop = section.offsetTop - navHeight; - if (window.scrollY >= sectionTop) { - current = section.id; - } - }); + let current = "home-section"; + sections.forEach((section) => { + const sectionTop = section.offsetTop - navHeight; + if (window.scrollY >= sectionTop) { + current = section.id; + } + }); + setActive(current); + }; - setActive(current); - }; + window.addEventListener("scroll", handleScroll); + handleScroll(); - window.addEventListener("scroll", handleScroll); - handleScroll(); - - return () => { - window.removeEventListener("scroll", handleScroll); - }; - }, [isScrolling]); + return () => { + window.removeEventListener("scroll", handleScroll); + }; + } + }, [isScrolling, location.pathname]); const handleClick = (id) => { - setActive(id); - setIsScrolling(true); - document.getElementById(id)?.scrollIntoView({ behavior: "smooth" }); - setTimeout(() => setIsScrolling(false), 800); + if (location.pathname === '/') { + setActive(id); + setIsScrolling(true); + document.getElementById(id)?.scrollIntoView({ behavior: "smooth" }); + setTimeout(() => setIsScrolling(false), 800); + } else { + navigate(`/#${id}`); + } }; - return (