feat: Add new sections and improve layout
- Added ExperienceSection and SkillsSection components to showcase user experience and skills. - Updated App component to include new sections in the Home layout. - Enhanced Projet component with improved styling and functionality, including a video player. - Integrated new translations in English and French for additional sections. - Updated navigation bar to include links to new sections. - Added Dockerfile and nginx configuration for deployment. - Removed unused video files and optimized existing components for better performance. - Updated CVSection to correctly reference the CV file path. - Improved styling across various components for a more cohesive design.
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
||||
FROM node:18-alpine AS build
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
|
||||
# Install dependencies
|
||||
COPY package*.json ./
|
||||
RUN npm ci --silent
|
||||
|
||||
# Copy source and build
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:stable-alpine AS production
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
types {
|
||||
application/javascript mjs;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
Generated
+94
-49
@@ -12,6 +12,7 @@
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@mui/icons-material": "^7.3.2",
|
||||
"@mui/lab": "^7.0.1-beta.22",
|
||||
"@mui/material": "^7.3.2",
|
||||
"@mui/x-data-grid": "^8.25.0",
|
||||
"@mui/x-data-grid-generator": "^8.25.0",
|
||||
@@ -275,9 +276,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
|
||||
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
|
||||
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -1254,9 +1255,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.2.tgz",
|
||||
"integrity": "sha512-AOyfHjyDKVPGJJFtxOlept3EYEdLoar/RvssBTWVAvDJGIE676dLi2oT/Kx+FoVXFoA/JdV7DEMq/BVWV3KHRw==",
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.8.tgz",
|
||||
"integrity": "sha512-s9UHZo7QJVly7gNArEZkbbsimHqJZhElgBpXIJdehZ4OWXt+CCr0SBDgUCDJnQrqpd1dWK2dLq5rmO4mCBmI3w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -1289,23 +1290,67 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.2.tgz",
|
||||
"integrity": "sha512-qXvbnawQhqUVfH1LMgMaiytP+ZpGoYhnGl7yYq2x57GYzcFL/iPzSZ3L30tlbwEjSVKNYcbiKO8tANR1tadjUg==",
|
||||
"node_modules/@mui/lab": {
|
||||
"version": "7.0.1-beta.22",
|
||||
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-7.0.1-beta.22.tgz",
|
||||
"integrity": "sha512-NHNIg51/CxiJTMTaWCxmqlqYIUfpXbdVjv/OyhSmuAb0z/lIOCMMu9fz0bIbQnZEh6H5L7DhTblynT7xwkXMIQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.3",
|
||||
"@mui/core-downloads-tracker": "^7.3.2",
|
||||
"@mui/system": "^7.3.2",
|
||||
"@mui/types": "^7.4.6",
|
||||
"@mui/utils": "^7.3.2",
|
||||
"@babel/runtime": "^7.28.6",
|
||||
"@mui/system": "^7.3.8",
|
||||
"@mui/types": "^7.4.11",
|
||||
"@mui/utils": "^7.3.8",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material": "^7.3.8",
|
||||
"@mui/material-pigment-css": "^7.3.8",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
},
|
||||
"@mui/material-pigment-css": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.8.tgz",
|
||||
"integrity": "sha512-QKd1RhDXE1hf2sQDNayA9ic9jGkEgvZOf0tTkJxlBPG8ns8aS4rS8WwYURw2x5y3739p0HauUXX9WbH7UufFLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.6",
|
||||
"@mui/core-downloads-tracker": "^7.3.8",
|
||||
"@mui/system": "^7.3.8",
|
||||
"@mui/types": "^7.4.11",
|
||||
"@mui/utils": "^7.3.8",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@types/react-transition-group": "^4.4.12",
|
||||
"clsx": "^2.1.1",
|
||||
"csstype": "^3.1.3",
|
||||
"csstype": "^3.2.3",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^19.1.1",
|
||||
"react-is": "^19.2.3",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1318,7 +1363,7 @@
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material-pigment-css": "^7.3.2",
|
||||
"@mui/material-pigment-css": "^7.3.8",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
@@ -1339,13 +1384,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/private-theming": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.2.tgz",
|
||||
"integrity": "sha512-ha7mFoOyZGJr75xeiO9lugS3joRROjc8tG1u4P50dH0KR7bwhHznVMcYg7MouochUy0OxooJm/OOSpJ7gKcMvg==",
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.8.tgz",
|
||||
"integrity": "sha512-du5dlPZ9XL3xW2apHoGDXBI+QLtyVJGrXNCfcNYfP/ojkz1RQ0rRV6VG9Rkm1DqEFRG8mjjTL7zmE1Bvn1eR4A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.3",
|
||||
"@mui/utils": "^7.3.2",
|
||||
"@babel/runtime": "^7.28.6",
|
||||
"@mui/utils": "^7.3.8",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1366,16 +1411,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/styled-engine": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.2.tgz",
|
||||
"integrity": "sha512-PkJzW+mTaek4e0nPYZ6qLnW5RGa0KN+eRTf5FA2nc7cFZTeM+qebmGibaTLrgQBy3UpcpemaqfzToBNkzuxqew==",
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.8.tgz",
|
||||
"integrity": "sha512-JHAeXQzS0tJ+Fq3C6J4TVDsW+yKhO4uuxuiLaopNStJeQYBIUCXpKYyUCcgXym4AmhbznQnv9RlHywSH6b0FOg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.3",
|
||||
"@babel/runtime": "^7.28.6",
|
||||
"@emotion/cache": "^11.14.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
"csstype": "^3.1.3",
|
||||
"csstype": "^3.2.3",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1400,18 +1445,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/system": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.2.tgz",
|
||||
"integrity": "sha512-9d8JEvZW+H6cVkaZ+FK56R53vkJe3HsTpcjMUtH8v1xK6Y1TjzHdZ7Jck02mGXJsE6MQGWVs3ogRHTQmS9Q/rA==",
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.8.tgz",
|
||||
"integrity": "sha512-hoFRj4Zw2Km8DPWZp/nKG+ao5Jw5LSk2m/e4EGc6M3RRwXKEkMSG4TgtfVJg7dS2homRwtdXSMW+iRO0ZJ4+IA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.3",
|
||||
"@mui/private-theming": "^7.3.2",
|
||||
"@mui/styled-engine": "^7.3.2",
|
||||
"@mui/types": "^7.4.6",
|
||||
"@mui/utils": "^7.3.2",
|
||||
"@babel/runtime": "^7.28.6",
|
||||
"@mui/private-theming": "^7.3.8",
|
||||
"@mui/styled-engine": "^7.3.8",
|
||||
"@mui/types": "^7.4.11",
|
||||
"@mui/utils": "^7.3.8",
|
||||
"clsx": "^2.1.1",
|
||||
"csstype": "^3.1.3",
|
||||
"csstype": "^3.2.3",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1440,12 +1485,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/types": {
|
||||
"version": "7.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.10.tgz",
|
||||
"integrity": "sha512-0+4mSjknSu218GW3isRqoxKRTOrTLd/vHi/7UC4+wZcUrOAqD9kRk7UQRL1mcrzqRoe7s3UT6rsRpbLkW5mHpQ==",
|
||||
"version": "7.4.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.11.tgz",
|
||||
"integrity": "sha512-fZ2xO9D08IKOxO2oUBi1nnVKH6oJUD+64cnv4YAaFoC0E5+i1+S5AHbNqqvZlYYsbPEQ6qEVwuBqY3jl5W4G+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4"
|
||||
"@babel/runtime": "^7.28.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
@@ -1457,13 +1502,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/utils": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.7.tgz",
|
||||
"integrity": "sha512-+YjnjMRnyeTkWnspzoxRdiSOgkrcpTikhNPoxOZW0APXx+urHtUoXJ9lbtCZRCA5a4dg5gSbd19alL1DvRs5fg==",
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.8.tgz",
|
||||
"integrity": "sha512-kZRcE2620CBGr+XI8YMmwPj6WIPwSF7uMJjvSfqd8zXVvlz0MCJbzRRUGNf8NgflCLthdji2DdS643TeyJ3+nA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@mui/types": "^7.4.10",
|
||||
"@babel/runtime": "^7.28.6",
|
||||
"@mui/types": "^7.4.11",
|
||||
"@types/prop-types": "^15.7.15",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1",
|
||||
@@ -3662,9 +3707,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@mui/icons-material": "^7.3.2",
|
||||
"@mui/lab": "^7.0.1-beta.22",
|
||||
"@mui/material": "^7.3.2",
|
||||
"@mui/x-data-grid": "^8.25.0",
|
||||
"@mui/x-data-grid-generator": "^8.25.0",
|
||||
|
||||
Binary file not shown.
@@ -10,5 +10,8 @@
|
||||
"SEE_ALL_PROJECTS": "See All Projects",
|
||||
"CONTACT_ME": "Contact Me",
|
||||
"CREATE_PROJECT": "Create Project",
|
||||
"EDIT_PROJECT": "Edit Project"
|
||||
"EDIT_PROJECT": "Edit Project",
|
||||
"MY_EXPERIENCE": "My Experience",
|
||||
"SKILLS": "Skills"
|
||||
|
||||
}
|
||||
@@ -10,5 +10,8 @@
|
||||
"SEE_ALL_PROJECTS": "Voir tous les projets",
|
||||
"CONTACT_ME": "Contactez-moi",
|
||||
"CREATE_PROJECT": "Créer un projet",
|
||||
"EDIT_PROJECT": "Modifier le projet"
|
||||
"EDIT_PROJECT": "Modifier le projet",
|
||||
"MY_EXPERIENCE": "Mon expérience",
|
||||
"SKILLS": "Compétences"
|
||||
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
+10
-4
@@ -1,20 +1,26 @@
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Container, Typography, Box, IconButton, Stack } from "@mui/material";
|
||||
import { Container, Box } from "@mui/material";
|
||||
import AppBar from "./section/navBar";
|
||||
|
||||
import "./App.css";
|
||||
import Footer from "./section/footer";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Box sx={{ minHeight: "100vh", display: "flex", flexDirection: "column" }}>
|
||||
<Box
|
||||
sx={{
|
||||
minHeight: "100vh",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
bgcolor: "#fafbfc",
|
||||
}}
|
||||
>
|
||||
<AppBar />
|
||||
<Container
|
||||
sx={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}
|
||||
maxWidth="xl"
|
||||
>
|
||||
<Outlet />
|
||||
</Container>
|
||||
<Footer />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,9 @@ export default function Card_Projet({
|
||||
{name}
|
||||
</Typography>
|
||||
|
||||
<Typography>{shortDescription}</Typography>
|
||||
<Typography variant="body1" component="p">
|
||||
{shortDescription}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
|
||||
@@ -30,8 +30,8 @@ export default function MenuLangue() {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={`drapeau/${i18n.language}.webp`}
|
||||
alt={`drapeau ${i18n.language}`}
|
||||
src={`/drapeau/${i18n.language}.webp`}
|
||||
alt={`drapeau/${i18n.language}.webp`}
|
||||
style={{ width: "30px", height: "20px", marginRight: "8px" }}
|
||||
/>
|
||||
</Button>
|
||||
@@ -57,7 +57,7 @@ export default function MenuLangue() {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="drapeau/fr.webp"
|
||||
src="/drapeau/fr.webp"
|
||||
alt={t("FRENCH")}
|
||||
style={{ width: "30px", height: "20px", marginRight: "8px" }}
|
||||
/>
|
||||
@@ -70,7 +70,7 @@ export default function MenuLangue() {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="drapeau/en.webp"
|
||||
src="/drapeau/en.webp"
|
||||
alt={t("ENGLISH")}
|
||||
style={{ width: "30px", height: "20px", marginRight: "8px" }}
|
||||
/>
|
||||
|
||||
@@ -2,13 +2,17 @@ import AboutSection from "./home/AboutSection";
|
||||
import CVSection from "./home/CVSection";
|
||||
import HeroSection from "./home/HeroSection";
|
||||
import ProjetSection from "./home/ProjetSection";
|
||||
import ExperienceSection from "./home/ExperienceSection";
|
||||
import SkillsSection from "./home/SkillsSection";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<HeroSection />
|
||||
<AboutSection />
|
||||
<SkillsSection />
|
||||
<ProjetSection />
|
||||
<ExperienceSection />
|
||||
<CVSection />
|
||||
</>
|
||||
);
|
||||
|
||||
+310
-11
@@ -1,21 +1,320 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import React from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { useProjet } from "../hook/useProjet";
|
||||
import {
|
||||
Box,
|
||||
Container,
|
||||
Typography,
|
||||
Button,
|
||||
Chip,
|
||||
CircularProgress,
|
||||
Stack,
|
||||
Paper,
|
||||
Grid,
|
||||
Divider,
|
||||
} from "@mui/material";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||
import LanguageIcon from "@mui/icons-material/Language";
|
||||
|
||||
export default function Projet() {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate(); // Permet de gérer le bouton "Retour"
|
||||
const apiprojet = {
|
||||
projets: [
|
||||
{
|
||||
id: 1,
|
||||
name: "protfolio",
|
||||
shortdescriptionfr:
|
||||
"Un portfolio personnel développé avec React et Material-UI, mettant en avant mes compétences, expériences et projets de manière moderne et responsive. avec une API en Go pour la gestion des données.",
|
||||
technologies: ["React", "Node.js", "Go", "SQL"],
|
||||
longdescriptionfr:
|
||||
"Ce projet de portfolio personnel a été conçu pour présenter mes compétences, expériences et réalisations de manière professionnelle et attrayante. Développé avec React pour une interface utilisateur dynamique et Material-UI pour un design moderne, il offre une navigation fluide et responsive sur tous les appareils. L'API en Go gère efficacement les données des projets, assurant une performance optimale. Ce portfolio met en avant mes projets phares, mes compétences techniques et mon parcours professionnel, offrant aux visiteurs une vue d'ensemble complète de mon expertise en développement web.",
|
||||
shortdescriptionen:
|
||||
"A personal portfolio developed with React and Material-UI, showcasing my skills, experiences, and projects in a modern and responsive way. It includes a Go API for data management.",
|
||||
longdescriptionen:
|
||||
"This personal portfolio project was designed to showcase my skills, experiences, and achievements in a professional and attractive manner. Developed with React for a dynamic user interface and Material-UI for a modern design, it offers smooth and responsive navigation across all devices. The Go API efficiently manages project data, ensuring optimal performance. This portfolio highlights my key projects, technical skills, and professional background, providing visitors with a comprehensive overview of my expertise in web development.",
|
||||
linkGithub: "https://github.com/sylvaintr/portfolio-react",
|
||||
linkWeb: "http://localhost:5173/",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Projet B",
|
||||
shortdescriptionfr: "Description courte du projet B",
|
||||
technologies: ["Go", "MongoDB"],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "VHS Video Home Share",
|
||||
shortdescriptionfr:
|
||||
"VHS est une plateforme web interactive qui transforme le streaming solitaire en une expérience collective. En mélangeant nostalgie et technologie moderne, l'application recrée la convivialité des cinémas et des soirées télé d'autrefois.",
|
||||
technologies: ["php", "javascript", "twig", "SQL"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const { data, isLoading } = useProjet(id);
|
||||
const isLoading = false;
|
||||
const data = apiprojet.projets.find((p) => p.id === Number(id));
|
||||
|
||||
const primaryBlue = "#7ab2cb";
|
||||
|
||||
// 1. ÉTAT DE CHARGEMENT PROPRE
|
||||
if (isLoading) {
|
||||
return (
|
||||
<>
|
||||
<h1>Projet {id}</h1>
|
||||
{isLoading && <p>Loading...</p>}
|
||||
{!isLoading && data && (
|
||||
<div>
|
||||
<h2>{data.name}</h2>
|
||||
<p>{data.shortdescriptionfr}</p>
|
||||
</div>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
minHeight: "60vh",
|
||||
}}
|
||||
>
|
||||
<CircularProgress sx={{ color: primaryBlue }} size={60} thickness={4} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// 2. GESTION DE L'ERREUR (Si le projet n'existe pas)
|
||||
if (!data) {
|
||||
return (
|
||||
<Container sx={{ py: 10, textAlign: "center" }}>
|
||||
<Typography variant="h4" color="text.secondary" gutterBottom>
|
||||
Projet introuvable
|
||||
</Typography>
|
||||
<Button
|
||||
startIcon={<ArrowBackIcon />}
|
||||
onClick={() => navigate(-1)}
|
||||
sx={{ color: primaryBlue }}
|
||||
>
|
||||
Retour aux projets
|
||||
</Button>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ py: 6, bgcolor: "#fafbfc", minHeight: "100vh" }}>
|
||||
<Container maxWidth="lg">
|
||||
{/* BOUTON RETOUR */}
|
||||
<Button
|
||||
startIcon={<ArrowBackIcon />}
|
||||
onClick={() => navigate(-1)}
|
||||
sx={{ mb: 4, color: "#666", textTransform: "none" }}
|
||||
>
|
||||
Retour
|
||||
</Button>
|
||||
|
||||
{/* GRAND CONTENEUR BLANC PRINCIPAL */}
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
p: { xs: 4, md: 6 },
|
||||
borderRadius: 4,
|
||||
border: "1px solid #eef2f6",
|
||||
bgcolor: "white",
|
||||
boxShadow: "0 12px 30px rgba(0,0,0,0.03)",
|
||||
// overflow: "hidden" retiré ici pour éviter de couper les ombres
|
||||
}}
|
||||
>
|
||||
<Grid container spacing={4} alignItems="flex-start">
|
||||
{/* COLONNE GAUCHE (Texte) : Prend 7 colonnes sur 12 */}
|
||||
<Grid item xs={12} md={7}>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
fontWeight: 800,
|
||||
color: "#2c3e50",
|
||||
mb: 2,
|
||||
fontSize: { xs: "2rem", md: "3rem" },
|
||||
}}
|
||||
>
|
||||
{data.name}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ color: primaryBlue, fontWeight: 400, lineHeight: 1.6 }}
|
||||
>
|
||||
{data.shortdescriptionfr}
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
{/* COLONNE DROITE (Vidéo) : Prend 5 colonnes sur 12 */}
|
||||
<Grid item xs={12} md={5}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
borderRadius: 3,
|
||||
overflow: "hidden",
|
||||
boxShadow: "0 8px 25px rgba(0,0,0,0.08)",
|
||||
bgcolor: "#444", // Fond sombre élégant
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<video
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
controls
|
||||
style={{
|
||||
width: "100%",
|
||||
maxHeight: "300px", // Hauteur max de la vidéo
|
||||
display: "block",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
>
|
||||
<source
|
||||
src={`/video_projet/projet_${id}.mp4`}
|
||||
type="video/mp4"
|
||||
/>
|
||||
Votre navigateur ne supporte pas les vidéos.
|
||||
</video>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Divider sx={{ my: 6, borderColor: "#f0f0f0" }} />
|
||||
|
||||
{/* 2. SECTION MILIEU : DESCRIPTION LONGUE (Prend toute la largeur) */}
|
||||
<Box sx={{ mb: 6 }}>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{ fontWeight: 700, mb: 3, color: "#333" }}
|
||||
>
|
||||
À propos de ce projet
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
color: "#555",
|
||||
lineHeight: 1.8,
|
||||
fontSize: "1.05rem",
|
||||
whiteSpace: "pre-line",
|
||||
}}
|
||||
>
|
||||
{data.longdescriptionfr}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* 3. SECTION BAS : TECHNOS & LIENS (Alignés, sans déborder) */}
|
||||
{/* J'ai utilisé alignItems="stretch" pour que les deux cartes aient la même hauteur naturellement */}
|
||||
<Grid container spacing={4} alignItems="stretch">
|
||||
{/* TECHNOLOGIES */}
|
||||
<Grid item xs={12} md={6}>
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
p: 4,
|
||||
bgcolor: "#f8fafd",
|
||||
borderRadius: 3,
|
||||
border: "1px solid #e8f0f4",
|
||||
height: "100%", // Permet à la carte de prendre toute la hauteur disponible de la grille
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ fontWeight: 700, mb: 3, color: "#333" }}
|
||||
>
|
||||
Technologies utilisées
|
||||
</Typography>
|
||||
{data.technologies && data.technologies.length > 0 ? (
|
||||
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 1.5 }}>
|
||||
{data.technologies.map((tech, idx) => (
|
||||
<Chip
|
||||
key={idx}
|
||||
label={tech}
|
||||
sx={{
|
||||
bgcolor: "white",
|
||||
color: primaryBlue,
|
||||
fontWeight: 600,
|
||||
border: "1px solid",
|
||||
borderColor: "rgba(122, 178, 203, 0.3)",
|
||||
py: 1,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
) : (
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Non spécifié
|
||||
</Typography>
|
||||
)}
|
||||
</>
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
{/* LIENS */}
|
||||
<Grid item xs={12} md={6}>
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
p: 4,
|
||||
bgcolor: "#f8fafd",
|
||||
borderRadius: 3,
|
||||
border: "1px solid #e8f0f4",
|
||||
height: "100%",
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ fontWeight: 700, mb: 3, color: "#333" }}
|
||||
>
|
||||
Liens du projet
|
||||
</Typography>
|
||||
<Stack
|
||||
spacing={2}
|
||||
direction={{ xs: "column", sm: "row" }}
|
||||
flexWrap="wrap"
|
||||
useFlexGap
|
||||
>
|
||||
{data.linkWeb && (
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<LanguageIcon />}
|
||||
href={data.linkWeb}
|
||||
target="_blank"
|
||||
disableElevation
|
||||
sx={{
|
||||
bgcolor: primaryBlue,
|
||||
textTransform: "none",
|
||||
borderRadius: 2,
|
||||
py: 1,
|
||||
px: 3,
|
||||
"&:hover": { bgcolor: "#689cb5" },
|
||||
}}
|
||||
>
|
||||
Voir le site en direct
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{data.linkGithub && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<GitHubIcon />}
|
||||
href={data.linkGithub}
|
||||
target="_blank"
|
||||
sx={{
|
||||
color: "#444",
|
||||
borderColor: "#ccc",
|
||||
textTransform: "none",
|
||||
borderRadius: 2,
|
||||
py: 1,
|
||||
px: 3,
|
||||
"&:hover": { borderColor: "#333", bgcolor: "white" },
|
||||
}}
|
||||
>
|
||||
Code source (GitHub)
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,9 @@
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Container,
|
||||
Grid,
|
||||
Paper,
|
||||
Stack,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { Avatar, Box, Stack, Typography } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DevicesIcon from "@mui/icons-material/Devices";
|
||||
import StorageIcon from "@mui/icons-material/Storage";
|
||||
import CodeIcon from "@mui/icons-material/Code";
|
||||
|
||||
export default function AboutSection() {
|
||||
const { t } = useTranslation();
|
||||
const skills = [
|
||||
{
|
||||
name: "Frontend",
|
||||
icon: <DevicesIcon sx={{ fontSize: 40, color: "#7ab2cb" }} />,
|
||||
desc: "React, MUI, Tailwind",
|
||||
},
|
||||
{
|
||||
name: "Backend",
|
||||
icon: <StorageIcon sx={{ fontSize: 40, color: "#ff4081" }} />,
|
||||
desc: "Node.js, Go, SQL",
|
||||
},
|
||||
{
|
||||
name: "Outils",
|
||||
icon: <CodeIcon sx={{ fontSize: 40, color: "#9c27b0" }} />,
|
||||
desc: "Git, Docker, Figma",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography
|
||||
@@ -45,76 +18,7 @@ export default function AboutSection() {
|
||||
>
|
||||
{t("ABOUT_ME")}
|
||||
</Typography>
|
||||
<Container maxWidth="lg" sx={{ my: 10 }}>
|
||||
<Grid container spacing={6} alignItems="center">
|
||||
{/* PARTIE GAUCHE : LE TEXTE */}
|
||||
<Grid item xs={12} md={6}>
|
||||
{/* Titre propre sans la grosse barre grise */}
|
||||
<Typography
|
||||
variant="h3"
|
||||
fontWeight="800"
|
||||
sx={{ mb: 3, color: "#2c3e50" }}
|
||||
>
|
||||
À propos de{" "}
|
||||
<Box component="span" sx={{ color: "#7ab2cb" }}>
|
||||
moi
|
||||
</Box>
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{ fontSize: "1.1rem", color: "#555", lineHeight: 1.8, mb: 2 }}
|
||||
>
|
||||
Étudiant en troisième année de BUT Informatique à Anglet.
|
||||
Passionné par le développement, j'aime explorer les nouvelles
|
||||
technologies et résoudre des problèmes complexes.
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{ fontSize: "1.1rem", color: "#555", lineHeight: 1.8 }}
|
||||
>
|
||||
Mon objectif est de concevoir des applications performantes qui
|
||||
ont un réel impact utilisateur. Quand je ne code pas, je fais de
|
||||
la veille technologique sur l'écosystème Go et React.
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
{/* PARTIE DROITE : REMPLACEMENT DE LA PHOTO PAR DES "CARTES COMPETENCES" */}
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid container spacing={2}>
|
||||
{skills.map((skill, index) => (
|
||||
<Grid item xs={12} sm={6} key={index}>
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
p: 3,
|
||||
height: "100%",
|
||||
bgcolor: "#f9f9f9",
|
||||
borderRadius: 4,
|
||||
border: "1px solid #eee",
|
||||
transition: "all 0.3s ease",
|
||||
"&:hover": {
|
||||
transform: "translateY(-5px)",
|
||||
boxShadow: "0 10px 20px rgba(0,0,0,0.05)",
|
||||
borderColor: "#7ab2cb",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box sx={{ mb: 2 }}>{skill.icon}</Box>
|
||||
<Typography variant="h6" fontWeight="bold" gutterBottom>
|
||||
{skill.name}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{skill.desc}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
<Stack
|
||||
direction={{ xs: "column", sm: "row" }}
|
||||
sx={{ alignItems: "center" }}
|
||||
@@ -122,11 +26,11 @@ export default function AboutSection() {
|
||||
mb={4}
|
||||
>
|
||||
<Box>
|
||||
<Typography sx={{ textAlign: "justify" }}>
|
||||
<Typography sx={{ textAlign: "justify", fontSize: "1.1rem" }} variant="body1" component="p">
|
||||
{t("Paragraph1_ABOUT_ME")}
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography sx={{ textAlign: "justify" }}>
|
||||
<Typography sx={{ textAlign: "justify", fontSize: "1.1rem" }} variant="body1" component="p">
|
||||
{t("Paragraph2_ABOUT_ME")}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -28,10 +28,12 @@ export default function CVSection() {
|
||||
alignItems: "center",
|
||||
mx: "auto",
|
||||
width: "100%",
|
||||
maxWidth: "100%",
|
||||
overflow: "hidden",
|
||||
mb: 4,
|
||||
}}
|
||||
>
|
||||
<Document file="cv.pdf">
|
||||
<Document file="/cv.pdf">
|
||||
<Page
|
||||
pageNumber={1}
|
||||
renderTextLayer={false}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Paper,
|
||||
Chip,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
Timeline,
|
||||
TimelineItem,
|
||||
TimelineSeparator,
|
||||
TimelineConnector,
|
||||
TimelineContent,
|
||||
TimelineDot,
|
||||
TimelineOppositeContent,
|
||||
} from "@mui/lab";
|
||||
import LaptopMacIcon from "@mui/icons-material/LaptopMac";
|
||||
import SchoolIcon from "@mui/icons-material/School";
|
||||
import WorkIcon from "@mui/icons-material/Work";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
// Tes données
|
||||
const experiences = [
|
||||
{
|
||||
title: "Développeur Full Stack",
|
||||
company: "Freelance",
|
||||
date: "2022 - Présent",
|
||||
icon: <LaptopMacIcon />,
|
||||
technos: ["React", "Node.js", "Docker"],
|
||||
desc: "Développement d'applications web complètes et déploiement CI/CD.",
|
||||
},
|
||||
{
|
||||
title: "Développeur Front-end",
|
||||
company: "Agence Web Exemple",
|
||||
date: "2020 - 2022",
|
||||
icon: <WorkIcon />,
|
||||
technos: ["TypeScript", "React", "Sass"],
|
||||
desc: "Création d'interfaces responsives et migration Angular vers React.",
|
||||
},
|
||||
{
|
||||
title: "BUT Informatique",
|
||||
company: "IUT de Bayonne",
|
||||
date: "2018 - 2021",
|
||||
icon: <SchoolIcon />,
|
||||
technos: ["Java", "SQL", "Algo"],
|
||||
desc: "Apprentissage des bases de l'ingénierie logicielle et gestion de projet.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function ExperienceTimeline() {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
// Détecte si on est sur mobile pour changer l'affichage
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
|
||||
const primaryBlue = "#7ab2cb";
|
||||
|
||||
return (
|
||||
<Box sx={{ bgcolor: "#fff" }} id="experience">
|
||||
{/* TITRE */}
|
||||
<Typography
|
||||
variant="h2"
|
||||
id="MY_EXPERIENCE"
|
||||
sx={{
|
||||
bgcolor: "#d8d7d7",
|
||||
py: 1,
|
||||
mb: 4,
|
||||
borderLeft: "10px solid #d8d7d7",
|
||||
}}
|
||||
>
|
||||
{t("MY_EXPERIENCE")}
|
||||
</Typography>
|
||||
|
||||
{/* TIMELINE */}
|
||||
{/* 'position="alternate"' crée l'effet zig-zag automatiquement */}
|
||||
<Timeline
|
||||
position={isMobile ? "right" : "alternate"}
|
||||
sx={{ bgcolor: "#fafbfc" }}
|
||||
>
|
||||
{experiences.map((exp, index) => (
|
||||
<TimelineItem key={index}>
|
||||
{/* LA DATE (s'affiche en face du contenu sur PC) */}
|
||||
<TimelineOppositeContent
|
||||
sx={{
|
||||
m: "auto 0",
|
||||
display: isMobile ? "none" : "block", // On cache la date opposée sur mobile pour la mettre dans la carte
|
||||
color: "#666",
|
||||
fontWeight: "bold",
|
||||
fontSize: "1.1rem",
|
||||
}}
|
||||
>
|
||||
{exp.date}
|
||||
</TimelineOppositeContent>
|
||||
|
||||
{/* LE SÉPARATEUR CENTRAL */}
|
||||
<TimelineSeparator>
|
||||
<TimelineConnector sx={{ bgcolor: primaryBlue }} />
|
||||
<TimelineDot
|
||||
sx={{
|
||||
bgcolor: primaryBlue,
|
||||
p: 2,
|
||||
boxShadow: "0 0 0 4px rgba(122, 178, 203, 0.2)", // Petit effet de halo autour du point
|
||||
}}
|
||||
>
|
||||
{exp.icon}
|
||||
</TimelineDot>
|
||||
<TimelineConnector sx={{ bgcolor: primaryBlue }} />
|
||||
</TimelineSeparator>
|
||||
|
||||
{/* LE CONTENU (LA CARTE) */}
|
||||
<TimelineContent sx={{ py: "12px", px: 2 }}>
|
||||
<Paper
|
||||
elevation={3}
|
||||
sx={{
|
||||
p: 3,
|
||||
bgcolor: "white",
|
||||
borderRadius: 4,
|
||||
textAlign: "left",
|
||||
position: "relative",
|
||||
"&:hover": {
|
||||
transform: "translateY(-3px)",
|
||||
boxShadow: "0 10px 20px rgba(0,0,0,0.1)",
|
||||
},
|
||||
transition: "all 0.3s ease",
|
||||
borderTop: `4px solid ${primaryBlue}`, // Petite touche de couleur en haut de la carte
|
||||
}}
|
||||
>
|
||||
{/* Sur mobile, on affiche la date DANS la carte car il n'y a pas de place à côté */}
|
||||
{isMobile && (
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
color: "#888",
|
||||
display: "block",
|
||||
mb: 1,
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{exp.date}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Typography variant="h6" component="span" fontWeight="bold">
|
||||
{exp.title}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{ color: primaryBlue, mb: 1 }}
|
||||
>
|
||||
{exp.company}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" color="text.secondary" paragraph>
|
||||
{exp.desc}
|
||||
</Typography>
|
||||
|
||||
{/* Chips technologies */}
|
||||
<Box
|
||||
sx={{ display: "flex", flexWrap: "wrap", gap: 0.5, mt: 1 }}
|
||||
>
|
||||
{exp.technos.map((tech, i) => (
|
||||
<Chip
|
||||
key={i}
|
||||
label={tech}
|
||||
size="small"
|
||||
sx={{ bgcolor: "#f0f4f8", color: "#555" }}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Paper>
|
||||
</TimelineContent>
|
||||
</TimelineItem>
|
||||
))}
|
||||
</Timeline>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +1,53 @@
|
||||
import { Box, Button, Container, Stack, Typography } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Container,
|
||||
Stack,
|
||||
Typography,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import LinkedInIcon from "@mui/icons-material/LinkedIn";
|
||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||
import EmailIcon from "@mui/icons-material/Email";
|
||||
|
||||
export default function HeroSection() {
|
||||
const { t } = useTranslation();
|
||||
const primaryBlue = "#7ab2cb"; // Ta couleur principale
|
||||
|
||||
// Définition des liens (remplace les href par tes vrais liens)
|
||||
const socialLinks = [
|
||||
{
|
||||
icon: <LinkedInIcon />,
|
||||
label: "LinkedIn",
|
||||
url: import.meta.env.VITE_LINK_TO_LINKEDIN,
|
||||
color: "#0077b5", // Couleur officielle LinkedIn
|
||||
},
|
||||
{
|
||||
icon: <GitHubIcon />,
|
||||
label: "GitHub",
|
||||
url: import.meta.env.VITE_LINK_TO_GITHUB,
|
||||
color: "#333", // Couleur officielle GitHub
|
||||
},
|
||||
{
|
||||
icon: <EmailIcon />,
|
||||
label: "Email",
|
||||
url: `mailto:${import.meta.env.VITE_MAIL}`,
|
||||
color: "#ea4335", // Couleur Gmail/Rouge
|
||||
text: import.meta.env.VITE_MAIL,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
background: "linear-gradient(135deg, #f5f7fa 0%, #eef2f3 100%)", // Dégradé plus subtil
|
||||
background: "linear-gradient(135deg, #f5f7fa 0%, #eef2f3 100%)",
|
||||
py: { xs: 8, md: 12 },
|
||||
mb: 8,
|
||||
minHeight: "80vh", // Prend 100% de la hauteur de l'écran visible
|
||||
display: "flex", // Active flexbox
|
||||
alignItems: "center", // Centre le contenu verticalement
|
||||
pt: { xs: 8, md: 0 }, // Un peu de padding en haut sur mobile, 0 sur PC car centré
|
||||
minHeight: "80vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
pt: { xs: 8, md: 0 },
|
||||
px: 2,
|
||||
borderRadius: "0 0 60px 60px",
|
||||
position: "relative",
|
||||
@@ -46,7 +78,7 @@ export default function HeroSection() {
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
fontSize: { xs: "3rem", md: "5rem" }, // Plus grand pour l'impact
|
||||
fontSize: { xs: "3rem", md: "5rem" },
|
||||
fontWeight: 800,
|
||||
color: "#2c3e50",
|
||||
lineHeight: 1.2,
|
||||
@@ -66,50 +98,137 @@ export default function HeroSection() {
|
||||
mb: 4,
|
||||
fontWeight: 400,
|
||||
maxWidth: "600px",
|
||||
mx: { xs: "auto", md: 0 }, // Centre le texte sur mobile
|
||||
}}
|
||||
>
|
||||
Développeur Full Stack passionné par la création d'expériences web
|
||||
modernes et intuitives.
|
||||
</Typography>
|
||||
|
||||
{/* Boutons d'action */}
|
||||
{/* Zone des Boutons (CV + Réseaux Sociaux) */}
|
||||
<Stack
|
||||
direction={{ xs: "column", sm: "row" }}
|
||||
spacing={2}
|
||||
direction={{ xs: "column", sm: "row" }} // Colonne sur mobile, ligne sur PC
|
||||
spacing={3} // Espace entre le bouton CV et les icônes
|
||||
alignItems="center"
|
||||
justifyContent={{ xs: "center", md: "flex-start" }}
|
||||
>
|
||||
{/* Bouton CV */}
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="large"
|
||||
href="/cv.pdf" // Mets le lien vers ton CV ici
|
||||
target="_blank"
|
||||
startIcon={<DownloadIcon />}
|
||||
sx={{
|
||||
color: "#555",
|
||||
borderColor: "#ccc",
|
||||
borderRadius: "20px",
|
||||
px: 4,
|
||||
px: 2,
|
||||
py: 1.5,
|
||||
fontSize: "1rem",
|
||||
textTransform: "none",
|
||||
backgroundColor: "white",
|
||||
boxShadow: "0 4px 6px rgba(0,0,0,0.05)",
|
||||
"&:hover": {
|
||||
borderColor: primaryBlue,
|
||||
color: primaryBlue,
|
||||
bgcolor: "transparent",
|
||||
bgcolor: "rgba(122, 178, 203, 0.1)",
|
||||
transform: "translateY(-2px)", // Petit effet de levée
|
||||
boxShadow: "0 6px 12px rgba(0,0,0,0.1)",
|
||||
},
|
||||
transition: "all 0.3s ease",
|
||||
}}
|
||||
>
|
||||
Télécharger CV
|
||||
</Button>
|
||||
|
||||
{/* Barre séparatrice verticale (visible uniquement sur écran large) */}
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: "none", sm: "block" },
|
||||
height: "30px",
|
||||
width: "1px",
|
||||
bgcolor: "#ddd",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Icônes Réseaux Sociaux */}
|
||||
<Stack direction="row" spacing={1}>
|
||||
{socialLinks.map((social, index) => {
|
||||
const hasText = !!social.text;
|
||||
const baseSx = {
|
||||
color: "#555",
|
||||
bgcolor: "white",
|
||||
border: "1px solid #eee",
|
||||
transition: "all 0.3s ease",
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 1,
|
||||
"&:hover": {
|
||||
color: social.color,
|
||||
bgcolor: "white",
|
||||
transform: "translateY(-3px)",
|
||||
borderColor: social.color,
|
||||
boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
|
||||
},
|
||||
};
|
||||
|
||||
const textSx = {
|
||||
border: `1px solid ${primaryBlue}`,
|
||||
borderColor: "#ccc",
|
||||
borderRadius: "20px",
|
||||
p: 1.2,
|
||||
fontSize: "1rem",
|
||||
textTransform: "none",
|
||||
boxShadow: "0 4px 6px rgba(0,0,0,0.05)",
|
||||
"&:hover": {
|
||||
borderColor: primaryBlue,
|
||||
color: primaryBlue,
|
||||
bgcolor: "rgba(122, 178, 203, 0.1)",
|
||||
transform: "translateY(-2px)",
|
||||
boxShadow: "0 6px 12px rgba(0,0,0,0.1)",
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip title={social.label} key={index} arrow>
|
||||
<IconButton
|
||||
component="a"
|
||||
href={social.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
sx={hasText ? { ...baseSx, ...textSx } : baseSx}
|
||||
>
|
||||
{social.icon}
|
||||
{hasText && (
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
ml: 1,
|
||||
color: "#555",
|
||||
fontSize: "0.95rem",
|
||||
display: { xs: "none", sm: "inline" },
|
||||
}}
|
||||
>
|
||||
{social.text}
|
||||
</Typography>
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
{/* Image / Avatar à droite */}
|
||||
<Box sx={{ flex: 1, display: "flex", justifyContent: "center" }}>
|
||||
{/* Remplace le src par ta photo */}
|
||||
<Box
|
||||
sx={{
|
||||
width: { xs: 250, md: 350 },
|
||||
height: { xs: 250, md: 350 },
|
||||
borderRadius: "30% 70% 70% 30% / 30% 30% 70% 70%", // Forme organique moderne
|
||||
borderRadius: "30% 70% 70% 30% / 30% 30% 70% 70%",
|
||||
bgcolor: "white",
|
||||
boxShadow: "0 20px 40px rgba(0,0,0,0.1)",
|
||||
overflow: "hidden",
|
||||
|
||||
@@ -1,14 +1,40 @@
|
||||
import { Box, Button, Stack, Typography } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Card_Projet from "../../components/Card_Projet";
|
||||
import { useProjets } from "../../hook/useProjet";
|
||||
//import { useProjets } from "../../hook/useProjet";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import type { Projetshort } from "../../types/projet";
|
||||
|
||||
export default function ProjetSection() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { data: apiprojet, isLoading } = useProjets(3);
|
||||
// const { data: apiprojet, isLoading } = useProjets(3);
|
||||
const apiprojet = {
|
||||
projets: [
|
||||
{
|
||||
id: 1,
|
||||
name: "protfolio",
|
||||
shortdescriptionfr:
|
||||
"Un portfolio personnel développé avec React et Material-UI, mettant en avant mes compétences, expériences et projets de manière moderne et responsive. avec une API en Go pour la gestion des données.",
|
||||
technologies: ["React", "Node.js", "Go", "SQL"],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Projet B",
|
||||
shortdescriptionfr: "Description courte du projet B",
|
||||
technologies: ["Go", "MongoDB"],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "VHS Video Home Share",
|
||||
shortdescriptionfr:
|
||||
"VHS est une plateforme web interactive qui transforme le streaming solitaire en une expérience collective. En mélangeant nostalgie et technologie moderne, l'application recrée la convivialité des cinémas et des soirées télé d'autrefois.",
|
||||
technologies: ["php", "javascript", "twig", "SQL"],
|
||||
},
|
||||
],
|
||||
};
|
||||
const isLoading = false;
|
||||
|
||||
return (
|
||||
<Box component="section">
|
||||
<Typography
|
||||
@@ -44,7 +70,7 @@ export default function ProjetSection() {
|
||||
/>
|
||||
))
|
||||
: null)}
|
||||
<Button
|
||||
{/* <Button
|
||||
variant="contained"
|
||||
sx={{ backgroundColor: "#7ab2cb", mb: 4 }}
|
||||
onClick={() => {
|
||||
@@ -54,7 +80,7 @@ export default function ProjetSection() {
|
||||
<Typography sx={{ textDecoration: "none" }}>
|
||||
{t("SEE_ALL_PROJECTS")}
|
||||
</Typography>
|
||||
</Button>
|
||||
</Button> */}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import { Box, Container, Stack, Typography } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
// Direct import is the "gold standard" for MUI v5 to avoid Type Overload issues
|
||||
import Grid from "@mui/material/Grid";
|
||||
|
||||
const skillCategories = [
|
||||
{
|
||||
title: "Front-end",
|
||||
skills: ["React", "TypeScript", "HTML", "CSS", "Material UI", "Tailwind"],
|
||||
},
|
||||
{
|
||||
title: "Back-end & BDD",
|
||||
skills: ["Node.js", "Go", "Express", "MongoDB", "PostgreSQL"],
|
||||
},
|
||||
{
|
||||
title: "Outils",
|
||||
skills: ["Git", "Docker"],
|
||||
},
|
||||
];
|
||||
|
||||
export default function SkillsSection() {
|
||||
const { t } = useTranslation();
|
||||
const primaryBlue = "#7ab2cb";
|
||||
|
||||
return (
|
||||
<Box sx={{ py: 8, bgcolor: "#fafbfc" }}>
|
||||
<Typography
|
||||
variant="h2"
|
||||
id="SKILLS"
|
||||
sx={{
|
||||
bgcolor: "#d8d7d7",
|
||||
py: 1,
|
||||
mb: 4,
|
||||
borderLeft: "10px solid #d8d7d7",
|
||||
}}
|
||||
>
|
||||
{t("SKILLS")}
|
||||
</Typography>
|
||||
<Container maxWidth="lg">
|
||||
<Stack spacing={4}>
|
||||
<Grid container spacing={5} sx={{ mt: 2 }}>
|
||||
{skillCategories.map((category, index) => (
|
||||
<Grid item xs={12} md={4} key={index}>
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
mb: 3,
|
||||
color: "#444",
|
||||
fontWeight: 600,
|
||||
borderBottom: `3px solid ${primaryBlue}`,
|
||||
display: "inline-block",
|
||||
pb: 0.5,
|
||||
}}
|
||||
>
|
||||
{category.title}
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 1.5 }}>
|
||||
{category.skills.map((s) => (
|
||||
<Box
|
||||
key={s}
|
||||
sx={{
|
||||
px: 2.5,
|
||||
py: 1,
|
||||
bgcolor: "white",
|
||||
borderRadius: 2,
|
||||
border: "1px solid #eaeaea",
|
||||
color: "#555",
|
||||
fontSize: "0.95rem",
|
||||
fontWeight: 500,
|
||||
boxShadow: "0 2px 6px rgba(0,0,0,0.02)",
|
||||
transition: "all 0.2s ease-in-out",
|
||||
cursor: "default",
|
||||
"&:hover": {
|
||||
borderColor: primaryBlue,
|
||||
color: primaryBlue,
|
||||
bgcolor: "rgba(122, 178, 203, 0.05)",
|
||||
transform: "translateY(-2px)",
|
||||
boxShadow: "0 6px 15px rgba(122, 178, 203, 0.15)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{s}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -15,7 +15,10 @@ import React from "react";
|
||||
|
||||
const pages = [
|
||||
{ name: "ABOUT_ME", path: "/#ABOUT_ME" },
|
||||
{ name: "SKILLS", path: "/#SKILLS" },
|
||||
{ name: "MY_PROJECTS", path: "/#MY_PROJECTS" },
|
||||
{ name: "MY_EXPERIENCE", path: "/#MY_EXPERIENCE" },
|
||||
{ name: "CV", path: "/#CV" },
|
||||
];
|
||||
|
||||
export default function NavBar() {
|
||||
@@ -28,11 +31,19 @@ export default function NavBar() {
|
||||
setAnchorElNav(null);
|
||||
};
|
||||
return (
|
||||
<AppBar position="static" sx={{ backgroundColor: "#7ab2cb" }}>
|
||||
<AppBar
|
||||
position="sticky"
|
||||
sx={{
|
||||
backgroundColor: "#7ab2cb",
|
||||
top: 0,
|
||||
zIndex: (theme) => theme.zIndex.appBar,
|
||||
}}
|
||||
>
|
||||
<Container>
|
||||
<Toolbar disableGutters>
|
||||
<Typography
|
||||
variant="h6"
|
||||
content="h1"
|
||||
noWrap
|
||||
sx={{
|
||||
mr: 2,
|
||||
|
||||
Reference in New Issue
Block a user