add of projects page, and new projects

This commit is contained in:
Giovanni-Josserand 2025-09-01 22:22:20 +02:00
parent 78dec2a053
commit b17d88f869
12 changed files with 167 additions and 92 deletions

View File

@ -1,10 +1,14 @@
# To do # To do
- Faire une page pour lister tous les projets - Régler problème de scoll entre page
- Rafaire l'organisation du projet - Rafaire l'organisation du projet
- Rajouter les images manquantes des projects
- Faire une page explicative par projet - Faire une page explicative par projet
- Faire du responsive - Faire du responsive
## Plus tard
- Refaire la section des skills - Refaire la section des skills
- Ajouter un fonction de filtrage des projects (en fonction de la date, des skills)
## Usefull commands ## Usefull commands

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -8,7 +8,7 @@ function App() {
return ( return (
<Routes> <Routes>
<Route path="/" element={<HomePage />} /> <Route path="/" element={<HomePage />} />
<Route path="/projets" element={<ProjectsPage />} /> <Route path="/projects" element={<ProjectsPage />} />
</Routes> </Routes>
) )
} }

View File

@ -31,7 +31,7 @@ function Experiences() {
}, []); }, []);
if (error) { if (error) {
return <div>Erreur lors de la récupération des données : {error}</div>; return <div>Error retrieving data: {error}</div>;
} }
return ( return (

View File

@ -1,11 +1,15 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import "../styles/NavBar.css"; import "../styles/NavBar.css";
const NavBar = () => { const NavBar = () => {
const [active, setActive] = useState("home-section"); const [active, setActive] = useState("home-section");
const [isScrolling, setIsScrolling] = useState(false); const [isScrolling, setIsScrolling] = useState(false);
const navigate = useNavigate();
useEffect(() => { useEffect(() => {
if (location.pathname === '/') {
const sections = document.querySelectorAll("section"); const sections = document.querySelectorAll("section");
const navHeight = document.querySelector(".navbar")?.offsetHeight + 40 || 120; const navHeight = document.querySelector(".navbar")?.offsetHeight + 40 || 120;
@ -19,7 +23,6 @@ const NavBar = () => {
current = section.id; current = section.id;
} }
}); });
setActive(current); setActive(current);
}; };
@ -29,16 +32,20 @@ const NavBar = () => {
return () => { return () => {
window.removeEventListener("scroll", handleScroll); window.removeEventListener("scroll", handleScroll);
}; };
}, [isScrolling]); }
}, [isScrolling, location.pathname]);
const handleClick = (id) => { const handleClick = (id) => {
if (location.pathname === '/') {
setActive(id); setActive(id);
setIsScrolling(true); setIsScrolling(true);
document.getElementById(id)?.scrollIntoView({ behavior: "smooth" }); document.getElementById(id)?.scrollIntoView({ behavior: "smooth" });
setTimeout(() => setIsScrolling(false), 800); setTimeout(() => setIsScrolling(false), 800);
} else {
navigate(`/#${id}`);
}
}; };
return ( return (
<nav className="navbar"> <nav className="navbar">
<ul className="nav-list"> <ul className="nav-list">

View File

@ -1,10 +1,13 @@
import SingleProject from "./SingleProject.jsx"; import SingleProject from "./SingleProject.jsx";
import "../styles/Projects.css" import "../styles/Projects.css"
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import {Link} from "react-router-dom"; import {Link, useLocation} from "react-router-dom";
import NavBar from "./NavBar.jsx";
function Projects() { function Projects() {
const [projects, setProjects] = useState([]); const [projects, setProjects] = useState([]);
const [error, setError] = useState(null); const [error, setError] = useState(null);
const location = useLocation();
useEffect(() => { useEffect(() => {
const fetchProjects = async () => { const fetchProjects = async () => {
@ -22,24 +25,63 @@ function Projects() {
fetchProjects(); fetchProjects();
}, []); }, []);
if (error) { if (error) {
return <div>Erreur lors de la récupération des données : {error}</div>; return <div>Error retrieving data: {error}</div>;
} }
if (location.pathname === '/') {
return ( return (
<section id="projects-section"> <section id="projects-section">
<h1 className="section-title">Projects</h1> <h1 className="section-title">Projects</h1>
<div className="projects-section-list"> <div className="projects-section-list">
{projects.map(project => ( {projects
<SingleProject image={project.image_name} title={project.title} description={project.description} skills={project.skills} color={project.color} nbImage={project.nb_image}/> .filter(project => project.id <= 3)
.map(project => (
<SingleProject
image={project.image_name}
title={project.title}
description={project.description}
skills={project.skills}
id={project.id}
nbImage={project.nb_image}
/>
))} ))}
</div> </div>
<div className="show-more-container"> <div className="show-more-container">
<Link to="/projets" className="show-more-link">Show more</Link> <Link to="/projects" className="projects-link">Show more</Link>
</div> </div>
</section> </section>
) )
}else if (location.pathname === '/projects') {
return (
<section id="projects-section">
<div className="projects-section-header">
<NavBar />
<h1 className="section-title">All Projects</h1>
<p className="projects-section-subtitle">Here you can find a collection of my work.</p>
</div>
<div className="projects-section-list">
{projects.map(project => (
<SingleProject
image={project.image_name}
title={project.title}
description={project.description}
skills={project.skills}
id={project.id}
nbImage={project.nb_image}
/>
))}
</div>
<div className="projects-back-link">
<Link to="/" className="projects-link"> Back to Home</Link>
</div>
</section>
)
}else {
return <div>Page inexistante</div>;
}
} }

View File

@ -2,10 +2,11 @@ import { useState, useEffect, useRef } from "react";
import SkillCard from "./SkillCard"; import SkillCard from "./SkillCard";
import "../styles/SingleProject.css"; import "../styles/SingleProject.css";
function SingleProject({ image, title, description, skills, color, nbImage }) { function SingleProject({ image, title, description, skills, id, nbImage }) {
const [imageID, setImageID] = useState(1); const [imageID, setImageID] = useState(1);
const [isFading, setIsFading] = useState(true); const [isFading, setIsFading] = useState(true);
const intervalRef = useRef(null); const intervalRef = useRef(null);
const color = ["blue", "green", "purple", "red", "yellow"]
const handleChangeImage = (direction) => { const handleChangeImage = (direction) => {
if (nbImage <= 1) return; if (nbImage <= 1) return;
@ -52,7 +53,7 @@ function SingleProject({ image, title, description, skills, color, nbImage }) {
<div className="single-project-right"> <div className="single-project-right">
<div className="single-project-right-top"> <div className="single-project-right-top">
<div className={`single-project-line color-${color}`}></div> <div className={`single-project-line color-${color[(id-1)%color.length]}`}></div>
<h3 className="single-project-title">{title}</h3> <h3 className="single-project-title">{title}</h3>
</div> </div>

View File

@ -23,7 +23,7 @@ function Skills() {
}, []); }, []);
if (error) { if (error) {
return <div>Erreur lors de la récupération des données : {error}</div>; return <div>Error retrieving data: {error}</div>;
} }
const uniqueSkillTypes = [...new Set(skills.map(skill => skill.type))]; const uniqueSkillTypes = [...new Set(skills.map(skill => skill.type))];

View File

@ -1,9 +1,26 @@
import Home from '../components/Home.jsx' import {useEffect, useState} from 'react'; // 1. Import useEffect
import Experiences from '../components/Experiences.jsx' import { useLocation } from 'react-router-dom'; // 2. Import useLocation
import Projects from '../components/Projects.jsx' import Home from '../components/Home.jsx';
import Skills from '../components/Skills.jsx' import Experiences from '../components/Experiences.jsx';
import Footer from '../components/Footer.jsx' import Projects from '../components/Projects.jsx';
import Skills from '../components/Skills.jsx';
import Footer from '../components/Footer.jsx';
function HomePage() { function HomePage() {
const location = useLocation();
useEffect(() => {
if (location.hash) {
const id = location.hash.replace('#', '');
const element = document.getElementById(id);
if (element) {
setTimeout(() => {
element.scrollIntoView({ behavior: 'smooth' });
}, 100);
}
}
}, [location]);
return ( return (
<div> <div>
<Home/> <Home/>

View File

@ -1,38 +1,8 @@
import React, {useEffect, useState} from "react"; import Projects from "../components/Projects.jsx";
import SingleProject from "../components/SingleProject.jsx";
function ProjectsPage() { function ProjectsPage() {
const [projects, setProjects] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const fetchProjects = async () => {
try {
const response = await fetch('/api/projects/');
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status}`);
}
const data = await response.json();
setProjects(data.data);
} catch (err) {
setError(err.message);
}
};
fetchProjects();
}, []);
if (error) {
return <div>Erreur lors de la récupération des données : {error}</div>;
}
return ( return (
<div> <Projects/>
<h1>Voici mes projets</h1>; );
{projects.map(project => (
<SingleProject image={project.image_name} title={project.title} description={project.description} skills={project.skills} color={project.color} nbImage={project.nb_image}/>
))}
</div>
)
} }
export default ProjectsPage; export default ProjectsPage;

View File

@ -10,7 +10,7 @@
} }
.show-more-link { .projects-link {
display: inline-block; display: inline-block;
color: #B0B0B0; color: #B0B0B0;
border: 1px solid #B0B0B0; border: 1px solid #B0B0B0;
@ -22,7 +22,7 @@
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.show-more-link:hover { .projects-link:hover {
background-color: #B0B0B0; background-color: #B0B0B0;
color: #1E1E1E; color: #1E1E1E;
transform: translateY(-3px); transform: translateY(-3px);
@ -31,4 +31,29 @@
.projects-section-list { .projects-section-list {
display: flex; display: flex;
flex-wrap: wrap;
justify-content: center;
}
.projects-section-header {
text-align: center;
margin-bottom: 2rem;
position: relative;
display: flex;
align-items: center;
flex-direction: column;
}
.projects-section-subtitle {
font-size: 1.2rem;
color: #B0B0B0;
}
.projects-back-link {
text-align: center;
margin-bottom: 3rem;
} }

View File

@ -69,18 +69,27 @@
margin-right: 1rem; margin-right: 1rem;
} }
.color-orange { .color-blue {
background-color: #D95F46; background-color: #2556ff;
}
.color-green {
background-color: #36a837;
} }
.color-purple { .color-purple {
background-color: #a646d9; background-color: #a646d9;
} }
.color-green { .color-red {
background-color: #36a837; background-color: #ff0000;
} }
.color-yellow {
background-color: #ffd427;
}
.single-project-title { .single-project-title {
font-size: 2rem; font-size: 2rem;
color: #EAEAEA; color: #EAEAEA;