340 lines
11 KiB
TypeScript
340 lines
11 KiB
TypeScript
/* ============================================================
|
||
Contenu du portfolio — SOURCE DE VÉRITÉ unique.
|
||
Pour modifier le site (textes, projets, expériences…),
|
||
c'est ici que ça se passe. Tout est typé.
|
||
============================================================ */
|
||
|
||
export type ProjectStatus = "live" | "dev" | "archived" | "oss";
|
||
|
||
export interface Project {
|
||
id: string;
|
||
title: string;
|
||
subtitle: string;
|
||
desc: string;
|
||
stack: string[];
|
||
status: ProjectStatus;
|
||
link?: string;
|
||
img?: string;
|
||
/** Variante du logo pour le thème sombre (logo « blanc »). Si absent, `img` est utilisé partout. */
|
||
imgDark?: string;
|
||
}
|
||
|
||
export interface Experience {
|
||
company: string;
|
||
logo: string;
|
||
role: string;
|
||
period: string;
|
||
duration: string;
|
||
desc: string;
|
||
missions: string[];
|
||
stack: string[];
|
||
current?: boolean;
|
||
}
|
||
|
||
export interface Formation {
|
||
title: string;
|
||
school: string;
|
||
period: string;
|
||
desc?: string;
|
||
}
|
||
|
||
export interface StackGroup {
|
||
label: string;
|
||
items: string[]; // noms de technos, résolus en icônes par <TechIcon/>
|
||
}
|
||
|
||
export interface Interest {
|
||
name: string;
|
||
note: string;
|
||
icon: "gamepad" | "film" | "music" | "code";
|
||
}
|
||
|
||
export const profile = {
|
||
name: "Johan Leroy",
|
||
role: "Développeur fullstack",
|
||
lede:
|
||
"Je conçois et développe des applications web performantes, propres et scalables, du front jusqu'à la mise en production. Basé à Nantes, profil « builder » : open-source, auto-hébergement, projets perso.",
|
||
bio:
|
||
"Développeur fullstack passionné par les technologies modernes, j'aime construire des produits complets : penser l'architecture, soigner l'expérience, déployer et maintenir. J'expérimente en continu via mes projets perso, mes services auto-hébergés et l'open-source.",
|
||
};
|
||
|
||
export const social = {
|
||
email: "contact@johanleroy.fr",
|
||
github: "https://github.com/JohanLeroy",
|
||
gitea: "https://git.lidge.fr/johanleroy",
|
||
linkedin: "https://www.linkedin.com/in/johan-leroy-472409180/",
|
||
soundcloud: "https://soundcloud.com/skuuuu",
|
||
};
|
||
|
||
/* URL du bouton « café » — à remplacer par le service retenu
|
||
(Buy Me a Coffee, Ko-fi, Liberapay, PayPal.me, Stripe…). */
|
||
export const coffeeUrl = "https://buymeacoffee.com/johanleroy";
|
||
|
||
export const stats = [
|
||
{ n: "~3 ans", l: "d'expérience pro" },
|
||
{ n: "6+", l: "projets livrés" },
|
||
{ n: "8+", l: "services auto-hébergés" },
|
||
{ n: "OSS", l: "contributeur open-source" },
|
||
];
|
||
|
||
/* Liste unique, dans l'ordre de développement (du plus récent au plus ancien). */
|
||
export const projects: Project[] = [
|
||
{
|
||
id: "kys",
|
||
title: "Keep Your Smile",
|
||
subtitle: "Site e-commerce",
|
||
desc:
|
||
"Site client e-commerce de la marque de vêtement Keep Your Smile",
|
||
stack: ["Next.js", "NestJS 11"],
|
||
status: "dev",
|
||
link: "https://keepyoursmile.fr",
|
||
img: "/images/projets/kys.png",
|
||
imgDark: "/images/projets/kys-dark.png",
|
||
},
|
||
{
|
||
id: "techos",
|
||
title: "Techos",
|
||
subtitle: "Plateforme hébergement & domaines",
|
||
desc:
|
||
"Plateforme client + admin de gestion d'hébergement web et cloud pour une association, bâtie sur WHMCS.",
|
||
stack: ["Next.js 16", "React 19", "NestJS 11", "Tailwind 4", "shadcn/ui", "Redis", "WHMCS API"],
|
||
status: "dev",
|
||
link: "https://techos-asso.fr",
|
||
img: "/images/projets/techos.png",
|
||
imgDark: "/images/projets/techos-dark.png",
|
||
},
|
||
{
|
||
id: "ksauce",
|
||
title: "Bot Discord KSauce",
|
||
subtitle: "Bot communautaire Discord",
|
||
desc:
|
||
"Communauté KSauce : commandes, modération et fonctionnalités sur-mesure.",
|
||
stack: ["Node.js", "TypeScript", "Discord.js"],
|
||
status: "dev",
|
||
link: "https://ksauce.lidge.fr/",
|
||
img: "/images/projets/ksauce.png",
|
||
imgDark: "/images/projets/ksauce-dark.png",
|
||
},
|
||
{
|
||
id: "amarea",
|
||
title: "Amarea Tattoo",
|
||
subtitle: "Site vitrine",
|
||
desc:
|
||
"Vitrine + gestion pour une tatoueuse indépendante. Architecture découplée SPA + API REST avec gestion de médias.",
|
||
stack: ["Vue 3", "Vite", "NestJS 11", "TypeORM", "MariaDB", "Tailwind 4"],
|
||
status: "live",
|
||
link: "https://amarea-tattoo.fr",
|
||
img: "/images/projets/amarea.png",
|
||
},
|
||
{
|
||
id: "arboretum",
|
||
title: "Arboretum",
|
||
subtitle: "IDE nouvelle génération",
|
||
desc:
|
||
"Outil IDE open-source : piloter des worktrees git et des sessions d'agents IA.",
|
||
stack: ["Node.js", "Vue 3", "TypeScript", "npx"],
|
||
status: "oss",
|
||
link: "https://git-arboretum.com/",
|
||
img: "/images/projets/arboretum.png",
|
||
imgDark: "/images/projets/arboretum-dark.png",
|
||
},
|
||
{
|
||
id: "lplv",
|
||
title: "Liberty Pour La Vie",
|
||
subtitle: "Site associatif",
|
||
desc: "Gestion complet pour l'association Liberty Pour La Vie pour facilité l'adoption d'un chat.",
|
||
stack: ["Java", "Spring Boot", "Thymeleaf", "Bootstrap", "MariaDB"],
|
||
status: "live",
|
||
link: "https://libertypourlavie.fr/",
|
||
img: "/images/projets/lplv.png",
|
||
},
|
||
{
|
||
id: "mvg",
|
||
title: "Mon Voisin Geek",
|
||
subtitle: "Site vitrine",
|
||
desc: "Vitrine de service informatique de voisinage. Projet désormais fermé.",
|
||
stack: ["Angular", "Tailwind CSS", "Express.js", "MariaDB"],
|
||
status: "archived",
|
||
img: "/images/projets/monvoisingeek.png",
|
||
},
|
||
];
|
||
|
||
export const experiences: Experience[] = [
|
||
{
|
||
company: "Qualiobee",
|
||
logo: "/images/entreprises/qualiobee.png",
|
||
role: "Développeur fullstack",
|
||
period: "Depuis 2025",
|
||
duration: "1 an",
|
||
desc:
|
||
"Édition d'un SaaS de gestion pour organismes de formation (conformité Qualiopi) : , extranet et intégration facturation et LMS.",
|
||
missions: [
|
||
"Développement fullstack : API NestJS + front Nuxt",
|
||
"Architecture serverless AWS (Lambda, S3)",
|
||
"Intégration Pennylane & Tiime (devis, factures, synchronisation clients)",
|
||
"Synchronisation calendrier ICS",
|
||
"Extranet apprenant",
|
||
"Intégration LMS",
|
||
],
|
||
stack: ["NestJS", "Nuxt 2", "TypeORM", "MariaDB", "AWS", "Terraform"],
|
||
current: true,
|
||
},
|
||
{
|
||
company: "Almeria",
|
||
logo: "/images/entreprises/almeria.png",
|
||
role: "Développeur",
|
||
period: "2023–2025",
|
||
duration: "2 ans",
|
||
desc: "Conception et développement d'applications web et de logiciels d'intégration ERP.",
|
||
missions: [
|
||
"Analyse, conception & développement d'applications web",
|
||
"Création de logiciels d'intégration ERP",
|
||
"Maintenance & évolution des solutions",
|
||
],
|
||
stack: ["Angular", "Express.js", "NestJS", "SQL Server", "MariaDB"],
|
||
},
|
||
{
|
||
company: "CEREMA",
|
||
logo: "/images/entreprises/cerema.png",
|
||
role: "Technicien évaluation, mesure trafic & mobilité",
|
||
period: "2021–2023",
|
||
duration: "2 ans",
|
||
desc: "Installation de capteurs et analyse de données de mobilité.",
|
||
missions: [
|
||
"Pose de capteurs provisoires (mobilité)",
|
||
"Optimisation des données et outils numériques",
|
||
"Analyse du trafic & de la mobilité",
|
||
],
|
||
stack: ["Analyse de données", "PHP", "MariaDB"],
|
||
},
|
||
{
|
||
company: "Réseau SNCF",
|
||
logo: "/images/entreprises/sncf.png",
|
||
role: "Agent télécom",
|
||
period: "2019–2021",
|
||
duration: "2 ans",
|
||
desc:
|
||
"Maintenance des équipements de sonorisation, d'affichage et de téléphonie sur le réseau ferroviaire.",
|
||
missions: [
|
||
"Maintenance préventive & corrective",
|
||
"Affichage dynamique en gare",
|
||
"Téléphonie sur les voies",
|
||
],
|
||
stack: ["Télécoms", "Maintenance", "Audio", "Affichage"],
|
||
},
|
||
{
|
||
company: "Novoferm",
|
||
logo: "/images/entreprises/novoferm.png",
|
||
role: "Stage développeur",
|
||
period: "2019",
|
||
duration: "7 semaines",
|
||
desc:
|
||
"Application web de récupération de fichiers SharePoint ; réparation et configuration de postes, imprimantes et téléphones.",
|
||
missions: [],
|
||
stack: [],
|
||
},
|
||
{
|
||
company: "Pivot Point",
|
||
logo: "/images/entreprises/pivotpoint.png",
|
||
role: "Stage développeur",
|
||
period: "2018",
|
||
duration: "1 semaine",
|
||
desc: "Développement d'un site internet en PHP.",
|
||
missions: [],
|
||
stack: ["PHP"],
|
||
},
|
||
{
|
||
company: "Actemium",
|
||
logo: "/images/entreprises/actemium.png",
|
||
role: "Stage développeur",
|
||
period: "2018",
|
||
duration: "1 semaine",
|
||
desc: "Développement d'applications Windows d'automatisation.",
|
||
missions: [],
|
||
stack: [],
|
||
},
|
||
];
|
||
|
||
export const formation: Formation[] = [
|
||
{
|
||
title: "Expert en Architecture et Développement Logiciel",
|
||
school: "Campus ENI, Nantes",
|
||
period: "Depuis 2025",
|
||
desc: "Architecture logicielle avancée, conception de systèmes distribués, DevOps, cloud, sécurité, performance et pilotage de projets techniques",
|
||
},
|
||
{
|
||
title: "Concepteur Développeur d'Applications",
|
||
school: "Campus ENI, Nantes",
|
||
period: "2023-2025",
|
||
desc: "Conception & dév d'applications web/mobiles, architecture logicielle, gestion de projet.",
|
||
},
|
||
{
|
||
title: "BTS Systèmes Numériques",
|
||
school: "Saint Félix La Salle, Nantes",
|
||
period: "2021–2023",
|
||
desc: "Systèmes électroniques & informatiques, informatique industrielle & réseaux.",
|
||
},
|
||
{
|
||
title: "BAC PRO Systèmes Numériques",
|
||
school: "Saint Félix La Salle, Nantes",
|
||
period: "2018–2021",
|
||
desc: "Systèmes électroniques & réseaux, installation & maintenance.",
|
||
},
|
||
{
|
||
title: "Diplôme National du Brevet",
|
||
school: "Saint Père en Retz",
|
||
period: "avant 2018",
|
||
},
|
||
];
|
||
|
||
/* Grille unique, groupée par catégorie (technos « cœur » d'abord, puis le reste). */
|
||
export const stack: StackGroup[] = [
|
||
{ label: "Langages", items: ["TypeScript", "JavaScript", "Java", "Python", "PHP", "C#", "C++", "Dart", "Kotlin", "HTML5", "CSS3"] },
|
||
{ label: "Frameworks & libs", items: ["NestJS", "Nuxt", "Next.js", "React", "Vue.js", "Angular", "Astro", "Tailwind CSS", "Spring", "Symfony", "Express.js", "FastAPI", "Flutter", "GraphQL", "Socket.io", "Chart.js", "Discord.js", "Vuetify", "Bootstrap"] },
|
||
{ label: "Bases de données & ORM", items: ["MariaDB", "MySQL", "PostgreSQL", "Redis", "MongoDB", "SQLite", "TypeORM", "Prisma", "Sequelize"] },
|
||
{ label: "Outils & DevOps", items: ["Docker", "Git", "Gitea", "GitLab", "GitHub Actions", "AWS", "Terraform", "NGINX", "Apache", "Vite", "pnpm", "Plesk"] },
|
||
{ label: "Tests & Qualité", items: ["Jest", "Vitest", "Playwright", "ESLint", "Prettier"] },
|
||
{ label: "Services & outils", items: ["Firebase", "Swagger", "Postman", "Stripe", "HubSpot"] },
|
||
{ label: "IDE & Éditeurs", items: ["WebStorm", "PhpStorm", "PyCharm", "IntelliJ IDEA", "VS Code", "Claude Code"] },
|
||
{ label: "Systèmes", items: ["Debian", "Ubuntu", "Windows", "macOS"] },
|
||
];
|
||
|
||
export const softTech = [
|
||
"Dév. Frontend",
|
||
"Dév. Backend",
|
||
"Analyse & traitement de données",
|
||
"Mise en production",
|
||
"Analyse de BDD",
|
||
"Modélisation UML",
|
||
"Maquettage",
|
||
"Sécurité",
|
||
];
|
||
|
||
export const softHuman = [
|
||
"Gestion de projet",
|
||
"Travail en équipe",
|
||
"Communication",
|
||
"Autonomie",
|
||
"Organisation",
|
||
"Adaptation",
|
||
"Veille techno",
|
||
];
|
||
|
||
export const interests: Interest[] = [
|
||
{ name: "Jeux vidéo", note: "// détente", icon: "gamepad" },
|
||
{ name: "Films & Séries", note: "// veille culture", icon: "film" },
|
||
{ name: "Musique électronique / DJing", note: "// loisir", icon: "music" },
|
||
{ name: "Projets personnels", note: "// always building", icon: "code" },
|
||
];
|
||
|
||
/* Texte tapé dans le mockup terminal du hero (état final = contenu no-JS). */
|
||
export const terminalText = `$ whoami
|
||
johan-leroy
|
||
$ cat about.json
|
||
{
|
||
"name": "Johan Leroy",
|
||
"role": "Développeur fullstack",
|
||
"stack": ["TypeScript", "NestJS", "Nuxt", "Next.js"],
|
||
"location": "Nantes, France"
|
||
}`;
|