From bdb9866164f53001114250018e863140a408468a Mon Sep 17 00:00:00 2001
From: SYLVAIN TROUILH <158754102+sylvaintr@users.noreply.github.com>
Date: Sat, 12 Sep 2026 20:17:33 +0000
Subject: [PATCH 1/2] fix kubernit (#1)
* fix kubernit
* fix
---
kubernite.yaml | 49 +++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 45 insertions(+), 4 deletions(-)
diff --git a/kubernite.yaml b/kubernite.yaml
index 68dff45..36b4219 100644
--- a/kubernite.yaml
+++ b/kubernite.yaml
@@ -18,6 +18,7 @@ spec:
containers:
- name: nginx
image: front_portefolio:latest
+ imagePullPolicy: Always
ports:
- name: http
containerPort: 80
@@ -25,13 +26,13 @@ spec:
- name: https
containerPort: 443
protocol: TCP
-
-...
+---
apiVersion: v1
kind: Service
metadata:
name: serveur-portefolio-service
spec:
+ # On supprime type: LoadBalancer. Le type par défaut est ClusterIP.
selector:
app: aplication_front_portefolio
ports:
@@ -43,5 +44,45 @@ spec:
protocol: TCP
port: 443
targetPort: 443
- type: LoadBalancer
-...
+
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: portefolio-ingress
+ annotations:
+ cert-manager.io/cluster-issuer: "letsencrypt-prod"
+ # L'annotation kubernetes.io/ingress.class est obsolète, on utilise spec.ingressClassName à la place
+spec:
+ ingressClassName: traefik
+ tls:
+ - hosts:
+ - portefolio.sylvaintrouilh.fr
+ secretName: portefolio-tls-secret
+ rules:
+ - host: portefolio.sylvaintrouilh.fr
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: serveur-portefolio-service
+ port:
+ number: 80
+
+---
+apiVersion: cert-manager.io/v1
+kind: ClusterIssuer
+metadata:
+ name: letsencrypt-prod
+spec:
+ acme:
+ server: https://acme-v02.api.letsencrypt.org/directory
+ email: sylvain.trouilh@hotmail.com
+ privateKeySecretRef:
+ name: letsencrypt-prod
+ solvers:
+ - http01:
+ ingress:
+ class: traefik
From cf68d2d2e49c2f83124e1f8cea287745e07deb08 Mon Sep 17 00:00:00 2001
From: SYLVAIN TROUILH <158754102+sylvaintr@users.noreply.github.com>
Date: Sat, 12 Sep 2026 20:29:55 +0000
Subject: [PATCH 2/2] =?UTF-8?q?feat:=20Enhance=20portfolio=20with=20SEO=20?=
=?UTF-8?q?meta=20tags,=20implement=20API=20for=20experie=E2=80=A6=20(#2)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: Enhance portfolio with SEO meta tags, implement API for experiences and projects, and refactor components for improved structure
* fix: address Copilot review comments on PR #2
- Migrate Grid callers to the System Grid size prop (GridWrapper)
- Render experience icons from exp.type instead of exp.icon
- Show experience date range (dateS-dateE) on desktop and mobile
- Select experience description from the active i18n language
- Restore technology chips from exp.technologies
- Rename uesExperience hook to useExperience
- Normalize getProjets response to the { data, count } shape
- Return ExperienceFull from getExperience
- Type useEditProjet/updateProjet with ProjetEdit
- Fetch project details from the API in Projet page (useProjet)
- Replace ExperienceType enum with const object (erasableSyntaxOnly)
- Sync document lang attribute with the active language
---
index.html | 4 +-
nginx.conf | 9 ++
src/{assets => }/api/Projet.ts | 21 +++-
src/api/experience.ts | 26 +++++
src/{assets => }/api/ipPublic.ts | 0
src/components/GridWrapper.tsx | 4 +-
src/hook/useExperience.ts | 42 +++++++
src/hook/useIpPublic.ts | 2 +-
src/hook/useProjet.ts | 10 +-
src/i18n.ts | 6 +
src/section/AllProjet.tsx | 2 +-
src/section/Projet.tsx | 13 +--
src/section/home/ExperienceSection.tsx | 69 +++++++-----
src/section/home/ProjetSection.tsx | 43 ++++----
src/section/home/SkillsSection.tsx | 2 +-
src/types/experience.ts | 34 ++++++
src/types/projet.ts | 146 ++++++++++++-------------
src/types/types.ts | 6 +
18 files changed, 294 insertions(+), 145 deletions(-)
rename src/{assets => }/api/Projet.ts (55%)
create mode 100644 src/api/experience.ts
rename src/{assets => }/api/ipPublic.ts (100%)
create mode 100644 src/hook/useExperience.ts
create mode 100644 src/types/experience.ts
create mode 100644 src/types/types.ts
diff --git a/index.html b/index.html
index 81dd417..763821c 100644
--- a/index.html
+++ b/index.html
@@ -1,5 +1,5 @@
-
+
@@ -11,6 +11,8 @@
href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap"
rel="stylesheet">
Portfolio TROUILH Sylvain
+
+
diff --git a/nginx.conf b/nginx.conf
index b7acdb8..f52a92f 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -4,6 +4,15 @@ server {
root /usr/share/nginx/html;
index index.html;
+
+ gzip on;
+ gzip_min_length 256;
+ gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
+ gzip_vary on;
+ gzip_proxied any;
+ gzip_comp_level 5;
+ gzip_buffers 16 8k;
+
include /etc/nginx/mime.types;
types {
application/javascript mjs;
diff --git a/src/assets/api/Projet.ts b/src/api/Projet.ts
similarity index 55%
rename from src/assets/api/Projet.ts
rename to src/api/Projet.ts
index 2922492..38170fd 100644
--- a/src/assets/api/Projet.ts
+++ b/src/api/Projet.ts
@@ -1,16 +1,29 @@
import axios from "axios";
-import type { ProjetCreate, Projetfull, Projets, Projetshort } from "../../types/projet";
+import type { ProjetCreate, ProjetEdit, Projetfull, Projets, Projetshort } from "../types/projet";
export async function getProjets(limit?: number, page?: number): Promise {
const url = import.meta.env.VITE_API_URL + "/projets";
- const projets = await axios.get(url, {
+ const response = await axios.get(url, {
params: {
nb: limit,
page: page,
},
});
- return projets.data;
+
+ // Normalise la réponse de l'API pour exposer la forme { data, count }.
+ // Accepte { data, count }, { projets, count } ou un simple tableau.
+ const body = response.data;
+ if (body && Array.isArray(body.data)) {
+ return body as Projets;
+ }
+ if (body && Array.isArray(body.projets)) {
+ return { data: body.projets, count: body.count ?? body.projets.length };
+ }
+ return {
+ data: Array.isArray(body) ? body : [],
+ count: Array.isArray(body) ? body.length : 0,
+ };
}
export async function getProjet(id: number): Promise {
@@ -30,7 +43,7 @@ export async function deleteProjet(id: number): Promise {
await axios.delete(url);
}
-export async function updateProjet(projet: Projetshort): Promise {
+export async function updateProjet(projet: ProjetEdit): Promise {
const url = import.meta.env.VITE_API_URL + "/projet/" + projet.id;
const updatedprojet = await axios.put(url, projet);
return updatedprojet.data;
diff --git a/src/api/experience.ts b/src/api/experience.ts
new file mode 100644
index 0000000..c392632
--- /dev/null
+++ b/src/api/experience.ts
@@ -0,0 +1,26 @@
+import axios from "axios";
+import type { Experiencecreate, ExperienceFull, Experiences } from "../types/experience";
+
+
+export async function getExperiences(): Promise {
+ const url = import.meta.env.VITE_API_URL + "/experiences";
+ const experiences = await axios.get(url);
+ return experiences.data;
+}
+
+export async function getExperience(id: number): Promise {
+ const url = import.meta.env.VITE_API_URL + "/experience/" + id;
+ const experience = await axios.get(url);
+ return experience.data;
+}
+
+export async function createExperience(experience: Experiencecreate): Promise {
+ const url = import.meta.env.VITE_API_URL + "/experiences";
+ const newexperience = await axios.post(url, experience);
+ return newexperience.data;
+}
+
+export async function deleteExperience(id: number): Promise {
+ const url = import.meta.env.VITE_API_URL + "/experience/" + id;
+ await axios.delete(url);
+}
\ No newline at end of file
diff --git a/src/assets/api/ipPublic.ts b/src/api/ipPublic.ts
similarity index 100%
rename from src/assets/api/ipPublic.ts
rename to src/api/ipPublic.ts
diff --git a/src/components/GridWrapper.tsx b/src/components/GridWrapper.tsx
index 9aeed91..3ac0de5 100644
--- a/src/components/GridWrapper.tsx
+++ b/src/components/GridWrapper.tsx
@@ -1,5 +1,5 @@
-import { Grid as MuiGrid } from "@mui/material";
+import Grid from "@mui/system/Grid";
export default function GridWrapper(props: any) {
- return ;
+ return ;
}
diff --git a/src/hook/useExperience.ts b/src/hook/useExperience.ts
new file mode 100644
index 0000000..5b9a7df
--- /dev/null
+++ b/src/hook/useExperience.ts
@@ -0,0 +1,42 @@
+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+
+
+import { createExperience, deleteExperience, getExperience, getExperiences } from '../api/experience';
+import type { Experiencecreate } from '../types/experience';
+
+export function useExperiences() {
+ return useQuery({
+ queryKey: ['experiences'],
+ queryFn: () => getExperiences(),
+ })
+}
+
+export function useExperience(id: number | undefined) {
+ return useQuery({
+ queryKey: ['experience', id],
+ queryFn: () => getExperience(id!),
+ enabled: !!id,
+ })
+}
+
+export default function useCreateExperience() {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: (newExperience: Experiencecreate) => createExperience(newExperience),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['experiences'] });
+ },
+
+ })
+}
+
+export function useDeleteExperience() {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: (id: number) => deleteExperience(id),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['experiences'] });
+ },
+
+ })
+}
\ No newline at end of file
diff --git a/src/hook/useIpPublic.ts b/src/hook/useIpPublic.ts
index 684e5e9..de141a6 100644
--- a/src/hook/useIpPublic.ts
+++ b/src/hook/useIpPublic.ts
@@ -1,6 +1,6 @@
import { useQuery } from '@tanstack/react-query';
-import { fetchIpPublic } from '../assets/api/ipPublic';
+import { fetchIpPublic } from '../api/ipPublic';
export function useIpPublic() {
diff --git a/src/hook/useProjet.ts b/src/hook/useProjet.ts
index f242d95..ff25e5f 100644
--- a/src/hook/useProjet.ts
+++ b/src/hook/useProjet.ts
@@ -1,7 +1,7 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
-import { createProjet, getProjet, getProjets, updateProjet } from '../assets/api/Projet';
-import type { ProjetCreate } from '../types/projet';
+import { createProjet, getProjet, getProjets, updateProjet } from '../api/Projet';
+import type { ProjetCreate, ProjetEdit } from '../types/projet';
export function useProjets(limit?: number, page?: number) {
@@ -26,17 +26,17 @@ export default function useCreateProjet() {
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['projets'] });
},
-
+
})
}
export function useEditProjet() {
const queryClient = useQueryClient();
return useMutation({
- mutationFn: (projet: any) => updateProjet(projet),
+ mutationFn: (projet: ProjetEdit) => updateProjet(projet),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['projets'] });
},
-
+
})
}
\ No newline at end of file
diff --git a/src/i18n.ts b/src/i18n.ts
index 3a69704..b2280b6 100644
--- a/src/i18n.ts
+++ b/src/i18n.ts
@@ -20,4 +20,10 @@ i18n
defaultNS: 'translation'
});
+// Synchronise l'attribut lang du document avec la langue active (SEO/accessibilité)
+i18n.on('languageChanged', (lng: string) => {
+ document.documentElement.lang = lng;
+});
+document.documentElement.lang = i18n.language;
+
export default i18n;
\ No newline at end of file
diff --git a/src/section/AllProjet.tsx b/src/section/AllProjet.tsx
index 2fa85b4..026c96e 100644
--- a/src/section/AllProjet.tsx
+++ b/src/section/AllProjet.tsx
@@ -25,7 +25,7 @@ export default function AllProjet() {
sx={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}
>
{projets &&
- projets.projets.map((projet) => (
+ projets.data.map((projet) => (
p.id === Number(id));
-
+ const { data, isLoading } = useProjet(id);
const primaryBlue = "#7ab2cb";
// 1. ÉTAT DE CHARGEMENT PROPRE
@@ -128,7 +125,7 @@ export default function Projet() {
{/* COLONNE GAUCHE (Texte) : Prend 7 colonnes sur 12 */}
-
+
{/* TECHNOLOGIES */}
-
+
{/* LIENS */}
-
+ ;
+ case "internship":
+ return ;
+ case "volunteer":
+ return ;
+ default:
+ return ;
+ }
+}
export default function ExperienceTimeline() {
const theme = useTheme();
- const { t } = useTranslation();
+ const { t, i18n } = useTranslation();
// Détecte si on est sur mobile pour changer l'affichage
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
- const primaryBlue = "#7ab2cb";
+ const { data: apiExperiences } = useExperiences();
+ // Sélectionne la description selon la langue active (repli sur le français)
+ const isEnglish = i18n.language?.toLowerCase().startsWith("en");
return (
@@ -86,9 +105,8 @@ export default function ExperienceTimeline() {
position={isMobile ? "right" : "alternate"}
sx={{ bgcolor: "#fafbfc" }}
>
- {experiences.map((exp, index) => (
+ {apiExperiences?.data.map((exp, index) => (
- {/* LA DATE (s'affiche en face du contenu sur PC) */}
- {exp.date}
+ {exp.dateS}
+ {exp.dateE ? ` — ${exp.dateE}` : ""}
- {/* LE SÉPARATEUR CENTRAL */}
- {exp.icon}
+ {getExperienceIcon(exp.type)}
- {/* LE CONTENU (LA CARTE) */}
- {/* Sur mobile, on affiche la date DANS la carte car il n'y a pas de place à côté */}
{isMobile && (
- {exp.date}
+ {exp.dateS}
+ {exp.dateE ? ` — ${exp.dateE}` : ""}
)}
@@ -160,22 +177,24 @@ export default function ExperienceTimeline() {
- {exp.desc}
+ {(isEnglish ? exp.descriptionEn : exp.descriptionFr) ||
+ exp.descriptionFr}
- {/* Chips technologies */}
-
- {exp.technos.map((tech, i) => (
-
- ))}
-
+ {exp.technologies && exp.technologies.length > 0 && (
+
+ {exp.technologies.map((tech, i) => (
+
+ ))}
+
+ )}
diff --git a/src/section/home/ProjetSection.tsx b/src/section/home/ProjetSection.tsx
index d66a2aa..fa9efd9 100644
--- a/src/section/home/ProjetSection.tsx
+++ b/src/section/home/ProjetSection.tsx
@@ -1,17 +1,15 @@
-import { Box, Stack, Typography } from "@mui/material";
+import { Box, Stack, Typography, Button } from "@mui/material";
import { useTranslation } from "react-i18next";
import Card_Projet from "../../components/Card_Projet";
-//import { useProjets } from "../../hook/useProjet";
-//import { useNavigate } from "react-router-dom";
+import { useProjets } from "../../hook/useProjet";
+import { useNavigate } from "react-router-dom";
import type { Projetshort } from "../../types/projet";
-import { apiprojet } from "../../types/projet.ts";
export default function ProjetSection() {
const { t } = useTranslation();
- //const navigate = useNavigate();
- // const { data: apiprojet, isLoading } = useProjets(3);
-
- const isLoading = false;
+ const navigate = useNavigate();
+ const { data: apiprojet } = useProjets(3);
+ const projets = Array.isArray(apiprojet?.data) ? apiprojet.data : [];
return (
@@ -36,20 +34,19 @@ export default function ProjetSection() {
mb: 4,
}}
>
- {!isLoading &&
- (Array.isArray(apiprojet?.projets)
- ? apiprojet.projets.map((projet: Projetshort) => (
-
- ))
- : null)}
- {/*
);
diff --git a/src/section/home/SkillsSection.tsx b/src/section/home/SkillsSection.tsx
index 0fb6651..cd1d072 100644
--- a/src/section/home/SkillsSection.tsx
+++ b/src/section/home/SkillsSection.tsx
@@ -61,7 +61,7 @@ export default function SkillsSection() {
{skillCategories.map((category, index) => (
-
+ ;
+
+
+export const ExperienceType = {
+ JOB: "job",
+ INTERNSHIP: "internship",
+ VOLUNTEER: "volunteer",
+ OTHER: "other"
+} as const;
+
diff --git a/src/types/projet.ts b/src/types/projet.ts
index df33e30..2e6654f 100644
--- a/src/types/projet.ts
+++ b/src/types/projet.ts
@@ -3,49 +3,47 @@
// types
export type Projetshort = {
- id: number;
- name: string;
- shortdescriptionfr: string;
- technologies?: string[];
+ id: number;
+ name: string;
+ shortdescriptionfr: string;
+ technologies?: string[];
}
export type Projetfull = {
- id: number;
- name: string;
- shortdescriptionfr: string;
- longdescriptionfr: string;
- shortdescriptionen: string;
- longdescriptionen: string;
- linkGithub?: string;
- linkWeb?: string;
- technologies?: string[];
+ id: number;
+ name: string;
+ shortdescriptionfr: string;
+ longdescriptionfr: string;
+ shortdescriptionen: string;
+ longdescriptionen: string;
+ linkGithub?: string;
+ linkWeb?: string;
+ technologies?: string[];
}
-export type Projets = {
- projets: Projetshort[];
- count: number;
-}
+export type Projets = arrayApi;
-export type ProjetCreate = {
- name: string;
- shortdescriptionfr: string;
+export type ProjetCreate = {
+ name: string;
+ shortdescriptionfr: string;
}
export type ProjetEdit = {
- id: number;
- name: string;
- shortdescriptionfr: string;
- longdescriptionfr: string;
- shortdescriptionen: string;
- longdescriptionen: string;
- linkGithub?: string;
- linkWeb?: string;
- technologies?: string[];
+ id: number;
+ name: string;
+ shortdescriptionfr: string;
+ longdescriptionfr: string;
+ shortdescriptionen: string;
+ longdescriptionen: string;
+ linkGithub?: string;
+ linkWeb?: string;
+ technologies?: string[];
}
import * as yup from "yup";
+import type { arrayApi } from "./types";
export const schemaCreateProjet = yup.object().shape({
name: yup.string().required("Le nom du projet est requis"),
@@ -65,51 +63,51 @@ export const schemaEditProjet = yup.object().shape({
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:
+ 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: "https://portefolio.sylvaintrouilh.fr/",
+ },
+ {
+ 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:
+ 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:
+ 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",
-
-
- },
- ],
- };
\ No newline at end of file
+ linkWeb: "https://sylvaintr.alwaysdata.net/sae/SAE3.01/",
+ linkGithub: "https://github.com/maximeBourciez/VideoHomeShare-Groupe5",
+
+
+ },
+ ],
+};
\ No newline at end of file
diff --git a/src/types/types.ts b/src/types/types.ts
new file mode 100644
index 0000000..16f89db
--- /dev/null
+++ b/src/types/types.ts
@@ -0,0 +1,6 @@
+
+
+export interface arrayApi {
+ data: T[];
+ count: number;
+}
\ No newline at end of file