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 (