Add GridWrapper and RetourEnHaut components
- Created GridWrapper component to wrap MUI Grid for easier usage. - Implemented RetourEnHaut component for a "back to top" button with smooth scrolling and navigation to the homepage.
This commit is contained in:
@@ -1,6 +0,0 @@
|
|||||||
FROM alpine
|
|
||||||
COPY . /app
|
|
||||||
WORKDIR /app
|
|
||||||
RUN apk add --update nodejs npm \
|
|
||||||
&& npm install \
|
|
||||||
&& npm run build \
|
|
||||||
+1
-1
@@ -13,5 +13,5 @@ RUN npm run build
|
|||||||
FROM nginx:stable-alpine AS production
|
FROM nginx:stable-alpine AS production
|
||||||
COPY --from=build /app/dist /usr/share/nginx/html
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
EXPOSE 80
|
EXPOSE 80 443
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
|||||||
+17
-11
@@ -4,12 +4,12 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
labels:
|
labels:
|
||||||
app: nginx
|
app: aplication_front_portefolio
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: nginx
|
app: aplication_front_portefolio
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
@@ -17,25 +17,31 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
image: nginx:alpine
|
image: front_portefolio:latest
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- name: http
|
||||||
- name: react-app
|
containerPort: 80
|
||||||
image: .
|
protocol: TCP
|
||||||
- name: api-go
|
- name: https
|
||||||
image: .
|
containerPort: 443
|
||||||
|
protocol: TCP
|
||||||
|
|
||||||
...
|
...
|
||||||
apiVersion: apps/v1
|
apiVersion: v1
|
||||||
kind: service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: serveur-portefolio-service
|
name: serveur-portefolio-service
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: aplication_front_portefolio
|
app: aplication_front_portefolio
|
||||||
ports:
|
ports:
|
||||||
- protocol: TCP
|
- name: http
|
||||||
|
protocol: TCP
|
||||||
port: 80
|
port: 80
|
||||||
targetPort: 80
|
targetPort: 80
|
||||||
|
- name: https
|
||||||
|
protocol: TCP
|
||||||
|
port: 443
|
||||||
|
targetPort: 443
|
||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
...
|
...
|
||||||
|
|||||||
Generated
+1460
-1
File diff suppressed because it is too large
Load Diff
@@ -28,9 +28,11 @@
|
|||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-hook-form": "^7.63.0",
|
"react-hook-form": "^7.63.0",
|
||||||
"react-i18next": "^15.7.3",
|
"react-i18next": "^15.7.3",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
"react-pdf": "^10.3.0",
|
"react-pdf": "^10.3.0",
|
||||||
"react-router": "^7.9.1",
|
"react-router": "^7.9.1",
|
||||||
"react-router-dom": "^7.9.1",
|
"react-router-dom": "^7.9.1",
|
||||||
|
"remark-gfm": "^4.0.1",
|
||||||
"yup": "^1.7.1"
|
"yup": "^1.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -51,7 +51,7 @@ export default function Card_Projet({
|
|||||||
objectFit: "cover",
|
objectFit: "cover",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<source src={`./video_projet/projet_${id}.mp4`} type="video/mp4" />
|
<source src={`./video_projet/projet_${id}.webm`} type="video/webm" />
|
||||||
Votre navigateur ne supporte pas les vidéos.
|
Votre navigateur ne supporte pas les vidéos.
|
||||||
</video>
|
</video>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Grid as MuiGrid } from "@mui/material";
|
||||||
|
|
||||||
|
export default function GridWrapper(props: any) {
|
||||||
|
return <MuiGrid {...props} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Fab } from "@mui/material";
|
||||||
|
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
export default function RetourEnHaut() {
|
||||||
|
const navigate = useNavigate(); // Permet de gérer le bouton "Retour en haut"
|
||||||
|
return (
|
||||||
|
<Fab
|
||||||
|
aria-label="retour-en-haut"
|
||||||
|
onClick={() => {
|
||||||
|
// Navigue vers la page d'accueil et tente un scroll doux
|
||||||
|
navigate("/");
|
||||||
|
try {
|
||||||
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
|
} catch (e) {}
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
position: "fixed",
|
||||||
|
bottom: 24,
|
||||||
|
right: 24,
|
||||||
|
zIndex: 1400,
|
||||||
|
backgroundColor: "#7ab2cb",
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowUpwardIcon />
|
||||||
|
</Fab>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import HeroSection from "./home/HeroSection";
|
|||||||
import ProjetSection from "./home/ProjetSection";
|
import ProjetSection from "./home/ProjetSection";
|
||||||
import ExperienceSection from "./home/ExperienceSection";
|
import ExperienceSection from "./home/ExperienceSection";
|
||||||
import SkillsSection from "./home/SkillsSection";
|
import SkillsSection from "./home/SkillsSection";
|
||||||
|
import RetourEnHaut from "../components/RetourEnHaut";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
@@ -14,6 +15,7 @@ export default function Home() {
|
|||||||
<ProjetSection />
|
<ProjetSection />
|
||||||
<ExperienceSection />
|
<ExperienceSection />
|
||||||
<CVSection />
|
<CVSection />
|
||||||
|
<RetourEnHaut />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-80
@@ -1,6 +1,5 @@
|
|||||||
import React from "react";
|
|
||||||
import { useParams, useNavigate } from "react-router-dom";
|
import { useParams, useNavigate } from "react-router-dom";
|
||||||
import { useProjet } from "../hook/useProjet";
|
// import { useProjet } from "../hook/useProjet";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Container,
|
Container,
|
||||||
@@ -10,48 +9,19 @@ import {
|
|||||||
CircularProgress,
|
CircularProgress,
|
||||||
Stack,
|
Stack,
|
||||||
Paper,
|
Paper,
|
||||||
Grid,
|
|
||||||
Divider,
|
Divider,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
import Grid from "../components/GridWrapper";
|
||||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||||
import LanguageIcon from "@mui/icons-material/Language";
|
import LanguageIcon from "@mui/icons-material/Language";
|
||||||
|
import { apiprojet } from "../types/projet.ts";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import remarkGfm from "remark-gfm";
|
||||||
|
|
||||||
export default function Projet() {
|
export default function Projet() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const navigate = useNavigate(); // Permet de gérer le bouton "Retour"
|
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 isLoading = false;
|
const isLoading = false;
|
||||||
const data = apiprojet.projets.find((p) => p.id === Number(id));
|
const data = apiprojet.projets.find((p) => p.id === Number(id));
|
||||||
@@ -113,12 +83,52 @@ export default function Projet() {
|
|||||||
border: "1px solid #eef2f6",
|
border: "1px solid #eef2f6",
|
||||||
bgcolor: "white",
|
bgcolor: "white",
|
||||||
boxShadow: "0 12px 30px rgba(0,0,0,0.03)",
|
boxShadow: "0 12px 30px rgba(0,0,0,0.03)",
|
||||||
|
position: "relative",
|
||||||
|
overflow: "visible",
|
||||||
|
// Espace à droite pour éviter que le contenu soit masqué par la vidéo flottante
|
||||||
|
pr: { md: "420px" },
|
||||||
// overflow: "hidden" retiré ici pour éviter de couper les ombres
|
// overflow: "hidden" retiré ici pour éviter de couper les ombres
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{/* Vidéo flottante en haut à droite du Paper */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
// En petit écran la vidéo s'insère dans le flux (au-dessus du contenu),
|
||||||
|
// en grand écran elle est absolue en haut à droite.
|
||||||
|
position: { xs: "relative", md: "absolute" },
|
||||||
|
top: { md: 20 },
|
||||||
|
right: { md: 20 },
|
||||||
|
width: { xs: "100%", sm: "45%", md: 360 },
|
||||||
|
maxHeight: { xs: 220, md: 300 },
|
||||||
|
borderRadius: 2,
|
||||||
|
overflow: "hidden",
|
||||||
|
boxShadow: "0 8px 25px rgba(0,0,0,0.12)",
|
||||||
|
zIndex: 2,
|
||||||
|
mb: { xs: 3, md: 0 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<video
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
muted
|
||||||
|
controls
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
display: "block",
|
||||||
|
objectFit: "cover",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<source
|
||||||
|
src={`/video_projet/projet_${id}.webm`}
|
||||||
|
type="video/webm"
|
||||||
|
/>
|
||||||
|
Votre navigateur ne supporte pas les vidéos.
|
||||||
|
</video>
|
||||||
|
</Box>
|
||||||
<Grid container spacing={4} alignItems="flex-start">
|
<Grid container spacing={4} alignItems="flex-start">
|
||||||
{/* COLONNE GAUCHE (Texte) : Prend 7 colonnes sur 12 */}
|
{/* COLONNE GAUCHE (Texte) : Prend 7 colonnes sur 12 */}
|
||||||
<Grid item xs={12} md={7}>
|
<Grid xs={12} md={7} component="div">
|
||||||
<Typography
|
<Typography
|
||||||
variant="h2"
|
variant="h2"
|
||||||
sx={{
|
sx={{
|
||||||
@@ -139,40 +149,7 @@ export default function Projet() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* COLONNE DROITE (Vidéo) : Prend 5 colonnes sur 12 */}
|
{/* La vidéo est maintenant flottante en haut à droite du Paper */}
|
||||||
<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>
|
</Grid>
|
||||||
|
|
||||||
<Divider sx={{ my: 6, borderColor: "#f0f0f0" }} />
|
<Divider sx={{ my: 6, borderColor: "#f0f0f0" }} />
|
||||||
@@ -187,14 +164,16 @@ export default function Projet() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body1"
|
variant="body1"
|
||||||
|
component="div"
|
||||||
sx={{
|
sx={{
|
||||||
color: "#555",
|
color: "#555",
|
||||||
lineHeight: 1.8,
|
lineHeight: 1.8,
|
||||||
fontSize: "1.05rem",
|
fontSize: "1.05rem",
|
||||||
whiteSpace: "pre-line",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{data.longdescriptionfr}
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||||
|
{data.longdescriptionfr}
|
||||||
|
</ReactMarkdown>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -202,7 +181,7 @@ export default function Projet() {
|
|||||||
{/* J'ai utilisé alignItems="stretch" pour que les deux cartes aient la même hauteur naturellement */}
|
{/* J'ai utilisé alignItems="stretch" pour que les deux cartes aient la même hauteur naturellement */}
|
||||||
<Grid container spacing={4} alignItems="stretch">
|
<Grid container spacing={4} alignItems="stretch">
|
||||||
{/* TECHNOLOGIES */}
|
{/* TECHNOLOGIES */}
|
||||||
<Grid item xs={12} md={6}>
|
<Grid xs={12} md={6} component="div">
|
||||||
<Paper
|
<Paper
|
||||||
elevation={0}
|
elevation={0}
|
||||||
sx={{
|
sx={{
|
||||||
@@ -246,7 +225,7 @@ export default function Projet() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* LIENS */}
|
{/* LIENS */}
|
||||||
<Grid item xs={12} md={6}>
|
<Grid xs={12} md={6} component="div">
|
||||||
<Paper
|
<Paper
|
||||||
elevation={0}
|
elevation={0}
|
||||||
sx={{
|
sx={{
|
||||||
@@ -264,12 +243,7 @@ export default function Projet() {
|
|||||||
>
|
>
|
||||||
Liens du projet
|
Liens du projet
|
||||||
</Typography>
|
</Typography>
|
||||||
<Stack
|
<Stack spacing={2} direction="row" flexWrap="wrap" useFlexGap>
|
||||||
spacing={2}
|
|
||||||
direction={{ xs: "column", sm: "row" }}
|
|
||||||
flexWrap="wrap"
|
|
||||||
useFlexGap
|
|
||||||
>
|
|
||||||
{data.linkWeb && (
|
{data.linkWeb && (
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import { Box, Stack, Typography, IconButton } from "@mui/material";
|
|
||||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
|
||||||
import LinkedInIcon from "@mui/icons-material/LinkedIn";
|
|
||||||
|
|
||||||
export default function Footer() {
|
|
||||||
return (
|
|
||||||
<footer>
|
|
||||||
<Box sx={{ bgcolor: "#7ab2cb", p: 2 }}>
|
|
||||||
<Stack
|
|
||||||
direction="row"
|
|
||||||
spacing={2}
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Typography variant="body1" color="white">
|
|
||||||
© {new Date().getFullYear()} Portfolio TROUILH Sylvain
|
|
||||||
</Typography>
|
|
||||||
<Box>
|
|
||||||
<a
|
|
||||||
href="mailto:sylvain.trouilh@example.com"
|
|
||||||
style={{
|
|
||||||
color: "white",
|
|
||||||
textDecoration: "none",
|
|
||||||
display: "inline-block",
|
|
||||||
marginLeft: 8,
|
|
||||||
}}
|
|
||||||
aria-label="Email"
|
|
||||||
>
|
|
||||||
<Typography variant="body1" color="white" component="span">
|
|
||||||
Email: {import.meta.env.VITE_MAIL}
|
|
||||||
</Typography>
|
|
||||||
</a>
|
|
||||||
<IconButton
|
|
||||||
component="a"
|
|
||||||
href={import.meta.env.VITE_LINK_TO_GITHUB}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
aria-label="GitHub"
|
|
||||||
>
|
|
||||||
<GitHubIcon />
|
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
|
||||||
component="a"
|
|
||||||
href={import.meta.env.VITE_LINK_TO_LINKEDIN}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
aria-label="LinkedIn"
|
|
||||||
>
|
|
||||||
<LinkedInIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
</footer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
import { Avatar, Box, Stack, Typography } from "@mui/material";
|
import { Box, Stack, Typography, useMediaQuery } from "@mui/material";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export default function AboutSection() {
|
export default function AboutSection() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const primaryBlue = "#7ab2cb";
|
||||||
|
const isWide = useMediaQuery("(min-width:1000px)");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box component="section">
|
||||||
<Typography
|
<Typography
|
||||||
variant="h2"
|
variant="h2"
|
||||||
id="ABOUT_ME"
|
id="ABOUT_ME"
|
||||||
@@ -14,32 +16,58 @@ export default function AboutSection() {
|
|||||||
py: 1,
|
py: 1,
|
||||||
mb: 4,
|
mb: 4,
|
||||||
borderLeft: "10px solid #d8d7d7",
|
borderLeft: "10px solid #d8d7d7",
|
||||||
|
scrollMarginTop: "80px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("ABOUT_ME")}
|
{t("ABOUT_ME")}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Stack
|
<Stack
|
||||||
direction={{ xs: "column", sm: "row" }}
|
direction={isWide ? "row" : "column"}
|
||||||
sx={{ alignItems: "center" }}
|
sx={{
|
||||||
|
alignItems: isWide ? "flex-start" : "center",
|
||||||
|
pl: isWide ? 12 : 2,
|
||||||
|
}}
|
||||||
spacing={2}
|
spacing={2}
|
||||||
mb={4}
|
mb={4}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box sx={{ flex: 1, minWidth: 0 }}>
|
||||||
<Typography sx={{ textAlign: "justify", fontSize: "1.1rem" }} variant="body1" component="p">
|
<Typography
|
||||||
|
sx={{ textAlign: "justify", fontSize: "1.1rem" }}
|
||||||
|
variant="body1"
|
||||||
|
component="p"
|
||||||
|
>
|
||||||
{t("Paragraph1_ABOUT_ME")}
|
{t("Paragraph1_ABOUT_ME")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<Typography sx={{ textAlign: "justify", fontSize: "1.1rem" }} variant="body1" component="p">
|
<Typography
|
||||||
|
sx={{ textAlign: "justify", fontSize: "1.1rem" }}
|
||||||
|
variant="body1"
|
||||||
|
component="p"
|
||||||
|
>
|
||||||
{t("Paragraph2_ABOUT_ME")}
|
{t("Paragraph2_ABOUT_ME")}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Avatar
|
<Box
|
||||||
alt="Sylvain Trouilh"
|
sx={{
|
||||||
src="/avatar.jpeg"
|
width: { xs: "100%", sm: 320, md: 380 },
|
||||||
sx={{ width: 300, height: 300, borderRadius: "50%" }}
|
height: { xs: 220, md: 380 },
|
||||||
/>
|
minHeight: { xs: 220, md: 380 },
|
||||||
|
maxWidth: { md: 380 },
|
||||||
|
flexShrink: 0,
|
||||||
|
borderRadius: "30% 70% 70% 30% / 30% 30% 70% 70%",
|
||||||
|
bgcolor: "white",
|
||||||
|
boxShadow: "0 20px 40px rgba(0,0,0,0.1)",
|
||||||
|
overflow: "hidden",
|
||||||
|
border: `4px solid ${primaryBlue}`,
|
||||||
|
backgroundImage: "url('/avatar.jpeg')",
|
||||||
|
backgroundSize: "cover",
|
||||||
|
backgroundPosition: "center",
|
||||||
|
display: "block",
|
||||||
|
ml: { sm: 2 },
|
||||||
|
}}
|
||||||
|
></Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export default function CVSection() {
|
|||||||
py: 1,
|
py: 1,
|
||||||
mb: 4,
|
mb: 4,
|
||||||
borderLeft: "10px solid #d8d7d7",
|
borderLeft: "10px solid #d8d7d7",
|
||||||
|
scrollMarginTop: "80px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("CV")}
|
{t("CV")}
|
||||||
|
|||||||
@@ -23,27 +23,35 @@ import { useTranslation } from "react-i18next";
|
|||||||
// Tes données
|
// Tes données
|
||||||
const experiences = [
|
const experiences = [
|
||||||
{
|
{
|
||||||
title: "Développeur Full Stack",
|
title: "Développeur Front-end (stage 4 mois) ",
|
||||||
company: "Freelance",
|
company: "TotalEnergies",
|
||||||
date: "2022 - Présent",
|
date: "mars 2026 - juin 2026",
|
||||||
icon: <LaptopMacIcon />,
|
icon: <LaptopMacIcon />,
|
||||||
technos: ["React", "Node.js", "Docker"],
|
technos: ["React", "Node.js", "typescript"],
|
||||||
desc: "Développement d'applications web complètes et déploiement CI/CD.",
|
desc: "Développement d'applications web pour aider la mise en place du finOps",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Développeur Front-end",
|
title: "Développeur Front-end (stage 2 mois)",
|
||||||
company: "Agence Web Exemple",
|
company: "TotalEnergies",
|
||||||
date: "2020 - 2022",
|
date: "avril 2025 - juin 2025",
|
||||||
icon: <WorkIcon />,
|
icon: <WorkIcon />,
|
||||||
technos: ["TypeScript", "React", "Sass"],
|
technos: ["TypeScript", "React", "Material UI"],
|
||||||
desc: "Création d'interfaces responsives et migration Angular vers React.",
|
desc: "Développement d'applications web pour aider la mise en place du finOps",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "BUT Informatique",
|
title: "BUT Informatique",
|
||||||
company: "IUT de Bayonne",
|
company: "IUT de Bayonne",
|
||||||
date: "2018 - 2021",
|
date: "2023 - 2025",
|
||||||
icon: <SchoolIcon />,
|
icon: <SchoolIcon />,
|
||||||
technos: ["Java", "SQL", "Algo"],
|
technos: [
|
||||||
|
"Java",
|
||||||
|
"SQL",
|
||||||
|
"Algo",
|
||||||
|
"Gestion de projet",
|
||||||
|
"Git",
|
||||||
|
"Docker",
|
||||||
|
"c/c++",
|
||||||
|
],
|
||||||
desc: "Apprentissage des bases de l'ingénierie logicielle et gestion de projet.",
|
desc: "Apprentissage des bases de l'ingénierie logicielle et gestion de projet.",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -66,6 +74,7 @@ export default function ExperienceTimeline() {
|
|||||||
py: 1,
|
py: 1,
|
||||||
mb: 4,
|
mb: 4,
|
||||||
borderLeft: "10px solid #d8d7d7",
|
borderLeft: "10px solid #d8d7d7",
|
||||||
|
scrollMarginTop: "80px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("MY_EXPERIENCE")}
|
{t("MY_EXPERIENCE")}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export default function HeroSection() {
|
|||||||
<Stack
|
<Stack
|
||||||
direction={{ xs: "column-reverse", md: "row" }}
|
direction={{ xs: "column-reverse", md: "row" }}
|
||||||
spacing={6}
|
spacing={6}
|
||||||
alignItems="center"
|
alignItems={{ xs: "center", md: "stretch" }}
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
>
|
>
|
||||||
{/* Texte à gauche */}
|
{/* Texte à gauche */}
|
||||||
@@ -222,22 +222,183 @@ export default function HeroSection() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Image / Avatar à droite */}
|
{/* Image / Graphique à droite */}
|
||||||
<Box sx={{ flex: 1, display: "flex", justifyContent: "center" }}>
|
<Box sx={{ flex: 1, display: "flex", justifyContent: "center" }}>
|
||||||
<Box
|
<svg
|
||||||
sx={{
|
viewBox="0 0 600 600"
|
||||||
width: { xs: 250, md: 350 },
|
width="100%"
|
||||||
height: { xs: 250, md: 350 },
|
height="100%"
|
||||||
borderRadius: "30% 70% 70% 30% / 30% 30% 70% 70%",
|
preserveAspectRatio="xMidYMid meet"
|
||||||
bgcolor: "white",
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
boxShadow: "0 20px 40px rgba(0,0,0,0.1)",
|
role="img"
|
||||||
overflow: "hidden",
|
aria-label="Graphic"
|
||||||
border: `4px solid ${primaryBlue}`,
|
style={{ display: "block" }}
|
||||||
backgroundImage: "url('/avatar.jpeg')",
|
>
|
||||||
backgroundSize: "cover",
|
<defs>
|
||||||
backgroundPosition: "center",
|
<linearGradient id="g1" x1="0" x2="1" y1="0" y2="1">
|
||||||
}}
|
<stop offset="0%" stopColor="#7ab2cb">
|
||||||
/>
|
<animate
|
||||||
|
attributeName="stop-color"
|
||||||
|
values="#7ab2cb;#9b59b6;#7ab2cb"
|
||||||
|
dur="6s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</stop>
|
||||||
|
<stop offset="100%" stopColor="#f76c6c">
|
||||||
|
<animate
|
||||||
|
attributeName="stop-color"
|
||||||
|
values="#bfeaf8;#f76c6c;#bfeaf8"
|
||||||
|
dur="6s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</stop>
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<radialGradient id="rg" cx="50%" cy="30%" r="60%">
|
||||||
|
<stop offset="0%" stopColor="#ffffff" stopOpacity="0.9" />
|
||||||
|
<stop offset="60%" stopColor="#ffd166" stopOpacity="0.15" />
|
||||||
|
<stop offset="100%" stopColor="#ffd166" stopOpacity="0" />
|
||||||
|
</radialGradient>
|
||||||
|
|
||||||
|
<filter id="fblur" x="-20%" y="-20%" width="140%" height="140%">
|
||||||
|
<feGaussianBlur stdDeviation="8" result="b" />
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="b" />
|
||||||
|
<feMergeNode in="SourceGraphic" />
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
|
||||||
|
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||||
|
<feGaussianBlur stdDeviation="6" result="coloredBlur" />
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="coloredBlur" />
|
||||||
|
<feMergeNode in="SourceGraphic" />
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
{/* Background soft radial light */}
|
||||||
|
<rect
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="600"
|
||||||
|
height="600"
|
||||||
|
fill="url(#rg)"
|
||||||
|
opacity="0.9"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<g transform="translate(300,300)">
|
||||||
|
<g filter="url(#glow)">
|
||||||
|
<path
|
||||||
|
d="M150 -140C200 -100 230 -50 240 10C250 70 220 120 170 150C120 180 60 190 10 170C-40 150 -90 110 -130 70C-170 30 -210 -10 -210 -60C-210 -110 -170 -150 -120 -180C-70 -210 -10 -220 50 -200C110 -180 100 -180 150 -140Z"
|
||||||
|
fill="url(#g1)"
|
||||||
|
opacity="0.95"
|
||||||
|
>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
type="scale"
|
||||||
|
values="1;1.05;1"
|
||||||
|
dur="6s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
type="translate"
|
||||||
|
values="0 0; 0 -18; 0 0"
|
||||||
|
dur="6s"
|
||||||
|
additive="sum"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g transform="rotate(0)">
|
||||||
|
<path
|
||||||
|
d="M120 -120C160 -90 190 -50 200 -10C210 30 190 70 150 100C110 130 60 150 10 140C-40 130 -80 100 -120 70C-160 40 -190 0 -190 -40C-190 -80 -160 -120 -120 -140C-80 -160 -40 -170 0 -160C40 -150 80 -140 120 -120Z"
|
||||||
|
fill="#6ee7b7"
|
||||||
|
opacity="0.18"
|
||||||
|
filter="url(#fblur)"
|
||||||
|
>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
type="rotate"
|
||||||
|
values="0;15;0"
|
||||||
|
dur="10s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
type="translate"
|
||||||
|
values="0 0; 12 6; 0 0"
|
||||||
|
dur="8s"
|
||||||
|
additive="sum"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g>
|
||||||
|
<circle
|
||||||
|
cx="-160"
|
||||||
|
cy="-120"
|
||||||
|
r="12"
|
||||||
|
fill="#ffd166"
|
||||||
|
opacity="0.95"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="r"
|
||||||
|
values="8;14;8"
|
||||||
|
dur="3s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
<circle cx="160" cy="140" r="8" fill="#f76c6c" opacity="0.95">
|
||||||
|
<animate
|
||||||
|
attributeName="cx"
|
||||||
|
values="160;140;160"
|
||||||
|
dur="4s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="cy"
|
||||||
|
values="140;120;140"
|
||||||
|
dur="4s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g fill="#fff" opacity="0.9">
|
||||||
|
<circle r="3" cx="-40" cy="-10">
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
type="translate"
|
||||||
|
values="0 0; 40 -20; 0 0"
|
||||||
|
dur="2.6s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
<circle r="2" cx="80" cy="20">
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
type="translate"
|
||||||
|
values="0 0; -60 30; 0 0"
|
||||||
|
dur="3.2s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
<circle r="2.5" cx="20" cy="90" fill="#e0f7fa">
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
type="translate"
|
||||||
|
values="0 0; -20 -60; 0 0"
|
||||||
|
dur="2.8s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</circle>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -1,38 +1,16 @@
|
|||||||
import { Box, Button, Stack, Typography } from "@mui/material";
|
import { Box, Stack, Typography } from "@mui/material";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import Card_Projet from "../../components/Card_Projet";
|
import Card_Projet from "../../components/Card_Projet";
|
||||||
//import { useProjets } from "../../hook/useProjet";
|
//import { useProjets } from "../../hook/useProjet";
|
||||||
import { useNavigate } from "react-router-dom";
|
//import { useNavigate } from "react-router-dom";
|
||||||
import type { Projetshort } from "../../types/projet";
|
import type { Projetshort } from "../../types/projet";
|
||||||
|
import { apiprojet } from "../../types/projet.ts";
|
||||||
|
|
||||||
export default function ProjetSection() {
|
export default function ProjetSection() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
//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;
|
const isLoading = false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -45,6 +23,7 @@ export default function ProjetSection() {
|
|||||||
py: 1,
|
py: 1,
|
||||||
mb: 4,
|
mb: 4,
|
||||||
borderLeft: "10px solid #d8d7d7",
|
borderLeft: "10px solid #d8d7d7",
|
||||||
|
scrollMarginTop: "80px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("MY_PROJECTS")}
|
{t("MY_PROJECTS")}
|
||||||
|
|||||||
@@ -1,20 +1,40 @@
|
|||||||
import { Box, Container, Stack, Typography } from "@mui/material";
|
import { Box, Stack, Typography } from "@mui/material";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
// Direct import is the "gold standard" for MUI v5 to avoid Type Overload issues
|
// Direct import is the "gold standard" for MUI v5 to avoid Type Overload issues
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "../../components/GridWrapper";
|
||||||
|
|
||||||
const skillCategories = [
|
const skillCategories = [
|
||||||
{
|
{
|
||||||
title: "Front-end",
|
title: "Front-end",
|
||||||
skills: ["React", "TypeScript", "HTML", "CSS", "Material UI", "Tailwind"],
|
skills: [
|
||||||
|
"React",
|
||||||
|
"TypeScript",
|
||||||
|
"HTML",
|
||||||
|
"CSS",
|
||||||
|
"Material UI",
|
||||||
|
"Tailwind",
|
||||||
|
"Bootstrap",
|
||||||
|
"angular",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Back-end & BDD",
|
title: "Back-end & BDD",
|
||||||
skills: ["Node.js", "Go", "Express", "MongoDB", "PostgreSQL"],
|
skills: [
|
||||||
|
"Node.js",
|
||||||
|
"Go",
|
||||||
|
"Express",
|
||||||
|
"MongoDB",
|
||||||
|
"PostgreSQL",
|
||||||
|
"MySQL",
|
||||||
|
"PHP",
|
||||||
|
"Laravel",
|
||||||
|
"Java",
|
||||||
|
"c/c++",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Outils",
|
title: "Outils",
|
||||||
skills: ["Git", "Docker"],
|
skills: ["Git", "Docker", "Jira", "Bash", "Figma", "Github/actions"],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -23,7 +43,7 @@ export default function SkillsSection() {
|
|||||||
const primaryBlue = "#7ab2cb";
|
const primaryBlue = "#7ab2cb";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ py: 8, bgcolor: "#fafbfc" }}>
|
<Box sx={{ py: 4, bgcolor: "#fafbfc" }}>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h2"
|
variant="h2"
|
||||||
id="SKILLS"
|
id="SKILLS"
|
||||||
@@ -32,65 +52,65 @@ export default function SkillsSection() {
|
|||||||
py: 1,
|
py: 1,
|
||||||
mb: 4,
|
mb: 4,
|
||||||
borderLeft: "10px solid #d8d7d7",
|
borderLeft: "10px solid #d8d7d7",
|
||||||
|
scrollMarginTop: "80px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("SKILLS")}
|
{t("SKILLS")}
|
||||||
</Typography>
|
</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 }}>
|
<Stack spacing={4} sx={{ pl: 12 }}>
|
||||||
{category.skills.map((s) => (
|
<Grid container spacing={5} sx={{ mt: 2 }}>
|
||||||
<Box
|
{skillCategories.map((category, index) => (
|
||||||
key={s}
|
<Grid xs={12} md={4} key={index} component="div">
|
||||||
sx={{
|
<Box>
|
||||||
px: 2.5,
|
<Typography
|
||||||
py: 1,
|
variant="h6"
|
||||||
bgcolor: "white",
|
sx={{
|
||||||
borderRadius: 2,
|
mb: 3,
|
||||||
border: "1px solid #eaeaea",
|
color: "#444",
|
||||||
color: "#555",
|
fontWeight: 600,
|
||||||
fontSize: "0.95rem",
|
borderBottom: `3px solid ${primaryBlue}`,
|
||||||
fontWeight: 500,
|
display: "inline-block",
|
||||||
boxShadow: "0 2px 6px rgba(0,0,0,0.02)",
|
pb: 0.5,
|
||||||
transition: "all 0.2s ease-in-out",
|
}}
|
||||||
cursor: "default",
|
>
|
||||||
"&:hover": {
|
{category.title}
|
||||||
borderColor: primaryBlue,
|
</Typography>
|
||||||
color: primaryBlue,
|
|
||||||
bgcolor: "rgba(122, 178, 203, 0.05)",
|
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 1.5 }}>
|
||||||
transform: "translateY(-2px)",
|
{category.skills.map((s) => (
|
||||||
boxShadow: "0 6px 15px rgba(122, 178, 203, 0.15)",
|
<Box
|
||||||
},
|
key={s}
|
||||||
}}
|
sx={{
|
||||||
>
|
px: 2.5,
|
||||||
{s}
|
py: 1,
|
||||||
</Box>
|
bgcolor: "white",
|
||||||
))}
|
borderRadius: 2,
|
||||||
</Box>
|
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>
|
</Box>
|
||||||
))}
|
</Grid>
|
||||||
</Grid>
|
))}
|
||||||
</Stack>
|
</Grid>
|
||||||
</Container>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,9 +55,10 @@ export default function NavBar() {
|
|||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
}}
|
}}
|
||||||
component="div"
|
component="a"
|
||||||
|
href="/"
|
||||||
>
|
>
|
||||||
Portfolio TROUILH Sylvain
|
Portfolio {import.meta.env.VITE_NAME}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Box sx={{ display: { xs: "flex", md: "none" } }}>
|
<Box sx={{ display: { xs: "flex", md: "none" } }}>
|
||||||
@@ -117,7 +118,7 @@ export default function NavBar() {
|
|||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
TROUILH Sylavin
|
{import.meta.env.VITE_NAME}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: { xs: "none", md: "flex" } }}>
|
<Box sx={{ display: { xs: "none", md: "flex" } }}>
|
||||||
{pages.map((page) => (
|
{pages.map((page) => (
|
||||||
|
|||||||
+51
-2
@@ -45,8 +45,6 @@ export type ProjetEdit = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// shema
|
|
||||||
|
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
export const schemaCreateProjet = yup.object().shape({
|
export const schemaCreateProjet = yup.object().shape({
|
||||||
@@ -64,3 +62,54 @@ export const schemaEditProjet = yup.object().shape({
|
|||||||
linkWeb: yup.string().url("Le lien Web doit être une URL valide").nullable(),
|
linkWeb: yup.string().url("Le lien Web doit être une URL valide").nullable(),
|
||||||
technologies: yup.array().of(yup.string()).nullable(),
|
technologies: yup.array().of(yup.string()).nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export 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:
|
||||||
|
"# À propos\nCe 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.\n\n## Objectifs\n- Fournir une vitrine claire et moderne de mes compétences techniques.\n- Permettre une navigation fluide et réactive sur tous les appareils.\n- Centraliser les informations de projets via une API dédiée en Go.\n\n## Détails techniques\nL'interface utilisateur est développée avec React et Material-UI pour garantir une expérience cohérente et accessible. L'API backend, écrite en Go, expose des endpoints REST pour gérer les projets et leurs métadonnées. La communication front–back est gérée via axios, avec une stratégie simple de mise en cache pour améliorer les performances.\n\n## Déploiement et maintenance\nLe projet utilise Vite pour des builds rapides et un workflow de développement optimisé. Le code est structuré pour faciliter l'ajout de nouvelles sections et la migration vers une source de données distante (BDD ou CMS). Des scripts de linting et des contrôles basiques sont exécutés avant les builds.\n\n[Voir le dépôt GitHub](https://github.com/sylvaintr/portfolio-react)",
|
||||||
|
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: "Poloko Ikastola",
|
||||||
|
shortdescriptionfr: "Poloko Ikastola est une application de gestion d'une association d'une Ikastola (école basque). Elle permet de gérer des actulités, des événements, faire l'appel des élèves, génére les factures.",
|
||||||
|
technologies: ["php", "javascript", "laravel", "SQL", "docker"],
|
||||||
|
longdescriptionfr:
|
||||||
|
"## Poloko IkastolaPoloko Ikastola est une application de gestion d'une Ikastola (école basque) visant à simplifier les tâches administratives et la communication entre enseignants, administrateurs et parents.\n\n### Fonctionnalités principales\n- Gestion des actualités et calendrier d'événements.\n- Module d'appel \n- Génération et export des factures.\n- Gestion des utilisateurs et des permissions (administrateur, enseignant, parent).\n\n### Architecture & technos\nBackend : PHP + Laravel migrations et gestion des droits.\nFrontend : JavaScript avec templates et composants réutilisables.\nBase de données : SQL (optimisée pour requêtes courantes).\nEnvironnement : Docker Compose pour l'orchestration des services en dev/test.\n\n### Défis & solutions\n- Mise en place d'un modèle de permissions granulaires pour protéger les données élèves.\n- Automatisation des exports de factures .\n\n### Bénéfices\nLe système a réduit la charge administrative et centralisé les informations essentielles, facilitant le quotidien des équipes pédagogiques.",
|
||||||
|
shortdescriptionen: "Poloko Ikastola is a management application for a Basque Ikastola (school). It allows managing activities, events, student attendance, and generating invoices.",
|
||||||
|
longdescriptionen:
|
||||||
|
"Poloko Ikastola is a management application for a Basque Ikastola (school). It allows managing activities, events, student attendance, and generating invoices. The application was developed using PHP with the Laravel framework for the backend, JavaScript for client-side interactions, and SQL for database management. Docker was used to facilitate deployment and environment management. Poloko Ikastola aims to simplify administrative tasks and improve communication within the association.",
|
||||||
|
linkGithub: "https://github.com/sylvaintr/sae-ikastola-Poloko",
|
||||||
|
linkWeb: "https://app.polokohiriondoikastola.eus",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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"],
|
||||||
|
longdescriptionfr:
|
||||||
|
"### VHS Video Home Share\nVHS Video Home Share est une plateforme de forme sur les film et série avec aussi les avis quiz et watchlist collaborative qui permet à des utilisateurs distants de regarder des vidéos ensemble, en synchronisant la lecture et en proposant des interactions en temps réel.\n\n#### Fonctionnalités détaillées\n- Création et gestion de salles privées ou publiques avec code d'accès.\n- Synchronisation de la lecture (play/pause/seek) pour tous les participants.\n- Chat en temps réel et gestion des invitations.\n- Rôles et permissions (hôte, modérateur, participant).\n\n#### Implémentation technique\nLa solution combine une API classique pour la gestion des ressources et un canal temps réel pour synchroniser les états de lecture entre clients. La persistance est assurée par une base SQL; des optimisations ciblées limitent la latence pour les commandes de contrôle.\n\n#### Perspectives d'amélioration\nAjout d'une authentification robuste, support de multiples sources vidéo, meilleure gestion adaptative de la qualité pour connexions lentes, et intégration d'un système de modération pour les salles publiques.\n\n_Lien du projet :_ https://sylvaintr.alwaysdata.net/vhs",
|
||||||
|
shortdescriptionen:
|
||||||
|
"VHS is an interactive web platform that transforms solitary streaming into a collective experience. By blending nostalgia with modern technology, the application recreates the conviviality of cinemas and old TV nights.",
|
||||||
|
longdescriptionen:
|
||||||
|
"VHS Video Home Share is an interactive web platform that transforms solitary streaming into a collective experience. By blending nostalgia with modern technology, the application recreates the conviviality of cinemas and old TV nights. Users can create virtual viewing rooms, invite friends, and synchronize video playback to share entertainment moments",
|
||||||
|
linkWeb: "https://sylvaintr.alwaysdata.net/sae/SAE3.01/",
|
||||||
|
linkGithub: "https://github.com/maximeBourciez/VideoHomeShare-Groupe5",
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user