Compare commits
6 Commits
49896bbaeb
...
v4.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
eceeca8b69
|
|||
|
92eca82127
|
|||
|
4502e32250
|
|||
|
d6f2d9e3a6
|
|||
|
eeb5e8872f
|
|||
|
7d22880905
|
@@ -28,7 +28,7 @@ The commit message guidelines adheres to [Conventional Commits](https://www.conv
|
||||
### Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/) >= v24.0.0 [(`nvm install 24`)](https://nvm.sh)
|
||||
- [pnpm](https://pnpm.io/) v10.15.0 [(`npm install --global corepack@0.34.0 && corepack enable`)](https://github.com/nodejs/corepack)
|
||||
- [pnpm](https://pnpm.io/) v10.15.1 [(`npm install --global corepack@0.34.0 && corepack enable`)](https://github.com/nodejs/corepack)
|
||||
- [Docker](https://www.docker.com/)
|
||||
|
||||
### Installation
|
||||
|
||||
31
README.md
@@ -8,7 +8,7 @@
|
||||
<a href="https://github.com/theoludwig"><img alt="GitHub" src="https://img.shields.io/badge/-GitHub-5A5A5A?style=flat&labelColor=5A5A5A&logo=github&logoColor=white"/></a>
|
||||
<a href="https://gitlab.com/theoludwig"><img alt="GitLab" src="https://img.shields.io/badge/-GitLab-303030?style=flat&labelColor=303030&logo=gitlab&logoColor=white"/></a>
|
||||
<a href="https://www.npmjs.com/~theoludwig"><img alt="npm" src="https://img.shields.io/badge/-npm-c4302b?style=flat&labelColor=c4302b&logo=npm&logoColor=white"/></a>
|
||||
<a href="https://twitter.com/theoludwig_"><img alt="Twitter" src="https://img.shields.io/badge/-Twitter-1ca0f1?style=flat&labelColor=1ca0f1&logo=x&logoColor=white"/></a>
|
||||
<a href="https://twitter.com/theoludwig_"><img alt="X/Twitter" src="https://img.shields.io/badge/-Twitter-1ca0f1?style=flat&labelColor=1ca0f1&logo=x&logoColor=white"/></a>
|
||||
<a href="https://www.youtube.com/@theo_ludwig"><img alt="YouTube" src="https://img.shields.io/badge/-YouTube-c4302b?style=flat&labelColor=c4302b&logo=youtube&logoColor=white"/></a>
|
||||
<a href="https://www.twitch.tv/theoludwig"><img alt="Twitch" src="https://img.shields.io/badge/-Twitch-9147FF?style=flat&labelColor=9147FF&logo=twitch&logoColor=white"/></a>
|
||||
<a href="https://theoludwig.fr/"><img alt="Website" src="https://img.shields.io/badge/-Website-181818?style=flat&labelColor=181818&logo=Google-Chrome&logoColor=white"/></a>
|
||||
@@ -27,21 +27,28 @@
|
||||
"nationality": "Alsace, France",
|
||||
"interests": ["Developer Full Stack", "Open-Source Enthusiast"],
|
||||
"skills": {
|
||||
"programmingLanguages": [
|
||||
"JavaScript/TypeScript",
|
||||
"Python",
|
||||
"C/C++",
|
||||
"PHP"
|
||||
"software-development": [
|
||||
"TypeScript",
|
||||
"React.js (+ Next.js)",
|
||||
"Tailwind CSS",
|
||||
"Node.js",
|
||||
"tRPC/oRPC",
|
||||
"PostgreSQL"
|
||||
],
|
||||
"frontend": ["HTML/CSS", "Tailwind CSS", "React.js/Next.js"],
|
||||
"backend": ["Laravel", "Node.js", "Fastify", "PostgreSQL"],
|
||||
"tools": [
|
||||
"sys-admin": [
|
||||
"Docker",
|
||||
"Proxmox",
|
||||
"Caddy",
|
||||
"GitHub Actions",
|
||||
"GitLab CI/CD"
|
||||
],
|
||||
"software-tools": [
|
||||
"GNU/Linux",
|
||||
"Arch Linux",
|
||||
"Visual Studio Code",
|
||||
"Git",
|
||||
"Docker"
|
||||
]
|
||||
"Git"
|
||||
],
|
||||
"systems-programming": ["C/C++", "Rust", "Go"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
BIN
apps/website/public/images/skills/Caddy.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
apps/website/public/images/skills/GitHub-dark.webp
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
apps/website/public/images/skills/GitHub-light.webp
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
apps/website/public/images/skills/GitLab.webp
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
apps/website/public/images/skills/Go.webp
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
apps/website/public/images/skills/Proxmox-dark.webp
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
apps/website/public/images/skills/Proxmox-light.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
apps/website/public/images/skills/Rust.webp
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
apps/website/public/images/skills/tRPC.webp
Normal file
|
After Width: | Height: | Size: 17 KiB |
@@ -74,6 +74,10 @@
|
||||
@apply font-semibold italic;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 87%;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.0.0-develop",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b",
|
||||
"packageManager": "pnpm@10.15.1+sha512.34e538c329b5553014ca8e8f4535997f96180a1d0f614339357449935350d924e22f8614682191264ec33d1462ac21561aff97f6bb18065351c162c7e8f6de67",
|
||||
"engines": {
|
||||
"node": ">=24.0.0"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@ Keep in mind that I will not translate the posts in French, all the posts will b
|
||||
|
||||
I plan to publish new posts when I have something new to share. There's no schedule, so stay tuned!
|
||||
|
||||
To stay informed of new blog post and to ask questions, feel free to follow me on Twitter: [@theoludwig\_](https://twitter.com/theoludwig_).
|
||||
To stay informed of new blog post and to ask questions, feel free to follow me on X/Twitter: [@theoludwig\_](https://twitter.com/theoludwig_).
|
||||
|
||||
## Project based learning
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ The idea is that a user can create an account to authenticate with an email addr
|
||||
|
||||
## History
|
||||
|
||||
The idea for the project has existed since May 13, 2020, symbolized by a [publication on Twitter](https://twitter.com/theoludwig_/status/1260638175246135296) by the creator: Théo LUDWIG.
|
||||
The idea for the project has existed since May 13, 2020, symbolized by a [publication on X/Twitter](https://twitter.com/theoludwig_/status/1260638175246135296) by the creator: Théo LUDWIG.
|
||||
|
||||
The main goal is to put into **practice knowledge in web development** and computer science in general on a concrete project that can **easily evolve over time** where you can add many features.
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"en-US": "English",
|
||||
"fr-FR": "French"
|
||||
},
|
||||
"fr-FR-main": "French (primary language)",
|
||||
"loading": "Loading...",
|
||||
"errors": {
|
||||
"error": "Error",
|
||||
@@ -24,7 +25,7 @@
|
||||
"description": "I constantly wonder how to improve our present, to make our future better, particularly thanks to the advancements in computer science. <br></br> My priority is to craft intuitive user experiences (UX), that meet the needs of the users in the most efficient way possible.",
|
||||
"title": "About"
|
||||
},
|
||||
"description": "Developer Full Stack • Student",
|
||||
"description": "Developer Full Stack • French Student",
|
||||
"education": {
|
||||
"cnam": {
|
||||
"institution": "Conservatoire National des Arts et Métiers (CNAM), in Eckbolsheim - UIMM Alsace - ITII Alsace",
|
||||
@@ -41,10 +42,11 @@
|
||||
"institution": "IUT Robert Schuman in Illkirch-Graffenstaden",
|
||||
"study-type": "University Bachelor of Technology (BUT) Computer Science",
|
||||
"years": {
|
||||
"title": "2021 - 2024",
|
||||
"2021-2022": {
|
||||
"courses": {
|
||||
"java": "Object Oriented Development in Java",
|
||||
"sql": "Relational database and SQL language",
|
||||
"sql": "<strong>Relational database and SQL language</strong>",
|
||||
"systems-c": "Systems programming in C (Memory allocation, Pointers, Structures)",
|
||||
"windows-forms": "Windows Forms (.NET Framework) Application Development in C#"
|
||||
},
|
||||
@@ -53,10 +55,10 @@
|
||||
},
|
||||
"2022-2023": {
|
||||
"courses": {
|
||||
"clean-code": "Design Patterns and Principles (Maintainable and Reusable Code) in UML",
|
||||
"clean-code": "Design Patterns and Principles (<strong>Maintainable and Reusable Code</strong>) in UML",
|
||||
"sql-security": "Securing database access and PL/SQL",
|
||||
"systems-c": "Systems programming in C (Multi-Thread, Server/Client UDP/TCP)",
|
||||
"tests": "Development Quality and Automated Testing",
|
||||
"systems-c": "Systems programming in <strong>C (Multi-Thread, Server/Client UDP/TCP)</strong>",
|
||||
"tests": "<strong>Development Quality and Automated Testing</strong>",
|
||||
"web": "Web development with the Laravel framework in PHP"
|
||||
},
|
||||
"description": "2nd year",
|
||||
@@ -67,7 +69,7 @@
|
||||
"ci-cd": "Continuous Integration/Deployment (CI/CD) and Docker",
|
||||
"complexity-algorithms": "Theoretical and Practical Algorithmic Complexity in C++",
|
||||
"no-sql": "NoSQL database (Redis, MongoDB, Cassandra)",
|
||||
"web": "Web development in Node.js and React.js"
|
||||
"web": "<strong>Web development in Node.js and React.js</strong>"
|
||||
},
|
||||
"description": "3rd year",
|
||||
"title": "2023 - 2024"
|
||||
@@ -87,19 +89,28 @@
|
||||
"title": "Studies"
|
||||
},
|
||||
"interests": {
|
||||
"fusey": "<link>Fusey (fusey.gg)</link>: website I'm developing for the game ARK that tracks the number of players connected to the servers in real time and has over ~5,000 visitors each month, ~100,000 members on Discord, and ~120,000 followers on X/Twitter.",
|
||||
"open-source": "Open-Source Enthusiast"
|
||||
"title": "Interests & hobbies",
|
||||
"fusey": "<link>Fusey (fusey.gg)</link>: website I'm developing for the game ARK that tracks the number of players connected to the servers in real time and has <strong>over ~5,000 visitors each month, ~100,000 members on Discord, and ~120,000 followers on X/Twitter</strong>.",
|
||||
"open-source": "<strong>Open-Source Contributor</strong>: publishing and <strong>contributing to npm packages</strong> and <link-github>GitHub</link-github> repositories, notably with <link-markdownlint>markdownlint-rule-relative-links</link-markdownlint> (around 20,000 downloads per week), contributions to <link-leon>Leon</link-leon>, an open-source personal assistant, among other projects."
|
||||
},
|
||||
"work": {
|
||||
"ircad": {
|
||||
"duration": "4 years",
|
||||
"position": "Full Stack Web Developer Apprentice",
|
||||
"summary": "Development of WebSurg, a virtual university dedicated to medical-surgical training, in React.js/Next.js and API Platform with Symfony."
|
||||
"tasks": {
|
||||
"WebSurg": "Development of WebSurg, a virtual university dedicated to medical-surgical training, built with React.js/Next.js and API Platform with Symfony.",
|
||||
"Figma": "<strong>Integration of Figma mockups</strong> for WebSurg, website dedicated to medical-surgical training.",
|
||||
"IRCAD-Core": "IRCAD Core, an internal tool for managing medical training sessions and their requirements (anatomical models, medications, staff training, etc.).",
|
||||
"feature-logs": "History and traceability of data modifications (what? who? when?) in IRCAD Core.",
|
||||
"feature-permissions": "<strong>Advanced permissions system and OAuth2 authentication</strong>, with read, write, and delete access restricted for specific users of the IRCAD Core application.",
|
||||
"feature-search": "<strong>Search engine with filters, sorting, and customizable display</strong> for each user of IRCAD Core.",
|
||||
"feature-architecture": "IRCAD Core project architecture in <strong>TypeScript Monorepo</strong> with Turborepo, <strong>automatic deployment (CI/CD)</strong> and <strong>self-hosted internally with Docker Compose</strong>."
|
||||
}
|
||||
},
|
||||
"numerize": {
|
||||
"duration": "3 months",
|
||||
"position": "Full Stack Web Developer Intern",
|
||||
"summary": "Development of an DMS (Document Management System) tool in React.js, Laravel and GraphQL."
|
||||
"summary": "Development of a DMS (Document Management System) tool in React.js, Laravel and GraphQL."
|
||||
},
|
||||
"title": "Work experiences"
|
||||
}
|
||||
@@ -155,13 +166,20 @@
|
||||
"title": "Projects"
|
||||
},
|
||||
"skills": {
|
||||
"software-development": "Software Development",
|
||||
"sys-admin": "SysAdmin",
|
||||
"systems-programming": "Systems Programming",
|
||||
|
||||
"backend": "Backend",
|
||||
"driving-license": "Driving license",
|
||||
"frontend": "Frontend",
|
||||
"others": "Others",
|
||||
"programming-languages": "Programming languages",
|
||||
"software-tools": "Software and tools",
|
||||
"title": "Skills"
|
||||
"title": "Skills",
|
||||
|
||||
"rust-advent-of-code": " (as hobby and <link-aoc>Advent Of Code</link-aoc>)",
|
||||
"go-basics": " (basics and fundamentals)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"en-US": "Anglais",
|
||||
"fr-FR": "Français"
|
||||
},
|
||||
"fr-FR-main": "Français (langue principale)",
|
||||
"loading": "Chargement...",
|
||||
"errors": {
|
||||
"error": "Erreur",
|
||||
@@ -41,10 +42,11 @@
|
||||
"institution": "IUT Robert Schuman à Illkirch-Graffenstaden",
|
||||
"study-type": "Bachelor Universitaire de Technologie (BUT) Informatique",
|
||||
"years": {
|
||||
"title": "2021 - 2024",
|
||||
"2021-2022": {
|
||||
"courses": {
|
||||
"java": "Développement Orientée Objet en Java",
|
||||
"sql": "Base de données relationnelles et langage SQL",
|
||||
"sql": "<strong>Base de données relationnelles et langage SQL</strong>",
|
||||
"systems-c": "Programmation systèmes en C (Allocation mémoire, Pointeurs, Structures)",
|
||||
"windows-forms": "Développement d'application Windows Forms (.NET Framework) en C#"
|
||||
},
|
||||
@@ -53,10 +55,10 @@
|
||||
},
|
||||
"2022-2023": {
|
||||
"courses": {
|
||||
"clean-code": "Patrons et Principes de conceptions (Code maintenable et réutilisable) en UML",
|
||||
"clean-code": "Patrons et Principes de conceptions (<strong>Code maintenable et réutilisable</strong>) en UML",
|
||||
"sql-security": "Sécurisation des accès à la base de données et PL/SQL",
|
||||
"systems-c": "Programmation systèmes en C (Multi-Thread, Serveur/Client UDP/TCP)",
|
||||
"tests": "Qualité de développement et Tests automatisés",
|
||||
"systems-c": "Programmation systèmes en <strong>C (Multi-Thread, Serveur/Client UDP/TCP)</strong>",
|
||||
"tests": "<strong>Qualité de développement et Tests automatisés</strong>",
|
||||
"web": "Développement Web avec le framework Laravel en PHP"
|
||||
},
|
||||
"description": "2ème année",
|
||||
@@ -67,7 +69,7 @@
|
||||
"ci-cd": "Intégration/Déploiement Continue et Docker",
|
||||
"complexity-algorithms": "Complexité Algorithmique Théorique et Pratique en C++",
|
||||
"no-sql": "Base de données NoSQL (Redis, MongoDB, Cassandra)",
|
||||
"web": "Développement Web en Node.js et React.js"
|
||||
"web": "<strong>Développement Web en Node.js et React.js</strong>"
|
||||
},
|
||||
"description": "3ème année",
|
||||
"title": "2023 - 2024"
|
||||
@@ -87,14 +89,23 @@
|
||||
"title": "Études"
|
||||
},
|
||||
"interests": {
|
||||
"fusey": "<link>Fusey (fusey.gg)</link> : site web que je développe pour le jeu ARK qui permet de suivre en temps réel le nombre de joueurs connectés sur les serveurs et a plus de ~5 000 visiteurs chaque mois, ~100 000 membres sur Discord et ~120 000 followers sur X/Twitter.",
|
||||
"open-source": "Enthousiaste de l'Open-Source"
|
||||
"title": "Intérêts & loisirs",
|
||||
"fusey": "<link>Fusey (fusey.gg)</link> : site web que je développe pour le jeu ARK qui permet de suivre en temps réel le nombre de joueurs connectés sur les serveurs et a plus de <strong>~5 000 visiteurs chaque mois, ~100 000 membres sur Discord et ~120 000 followers sur X/Twitter</strong>.",
|
||||
"open-source": "<strong>Contributeur de l'Open-Source</strong> : publications et <strong>contributions sur npm</strong> et <link-github>GitHub</link-github> avec notamment <link-markdownlint>markdownlint-rule-relative-links</link-markdownlint> avec ~20 000 téléchargements par semaine, contributions à <link-leon>Leon</link-leon>, un assistant personnel open source, parmis d'autres projets."
|
||||
},
|
||||
"work": {
|
||||
"ircad": {
|
||||
"duration": "4 ans",
|
||||
"position": "Apprenti Développeur Web Full Stack",
|
||||
"summary": "Développement de WebSurg, une université virtuelle consacrée à la formation médico-chirurgicale, en React.js/Next.js et API Platform avec Symfony."
|
||||
"tasks": {
|
||||
"WebSurg": "Développement de WebSurg, une université virtuelle consacrée à la formation médico-chirurgicale, en React.js/Next.js et API Platform avec Symfony.",
|
||||
"Figma": "<strong>Intégration des maquettes Figma</strong> pour WebSurg, site web consacrée à la formation médico-chirurgicale.",
|
||||
"IRCAD-Core": "IRCAD Core, outil interne de gestion des formations médicales et leurs besoins (modèles anatomiques, médicaments, formations du personnel, etc.).",
|
||||
"feature-logs": "Historique et traçabilité des modifications des données (quoi? qui? quand?) dans IRCAD Core.",
|
||||
"feature-permissions": "<strong>Système de permissions avancé</strong> et authentification OAuth2, avec accès en lecture, écriture et suppression restreint pour des utilisateurs spécifiques de l'application IRCAD Core.",
|
||||
"feature-search": "<strong>Moteur de recherche avec filtres, tris et ordre d'affichage personnalisable</strong> pour IRCAD Core.",
|
||||
"feature-architecture": "Architecture du projet IRCAD Core en <strong>Monorepo TypeScript</strong> avec Turborepo, <strong>déploiement automatique (CI/CD)</strong> et <strong>hébergé en interne avec Docker Compose</strong>."
|
||||
}
|
||||
},
|
||||
"numerize": {
|
||||
"duration": "3 mois",
|
||||
@@ -155,13 +166,20 @@
|
||||
"title": "Projets"
|
||||
},
|
||||
"skills": {
|
||||
"software-development": "Développement informatique",
|
||||
"sys-admin": "SysAdmin",
|
||||
"systems-programming": "Programmation Système",
|
||||
|
||||
"backend": "Backend",
|
||||
"driving-license": "Permis B",
|
||||
"frontend": "Frontend",
|
||||
"others": "Autres",
|
||||
"programming-languages": "Langages de programmation",
|
||||
"software-tools": "Logiciels et outils",
|
||||
"title": "Compétences"
|
||||
"title": "Compétences",
|
||||
|
||||
"rust-advent-of-code": " (pendant temps libre et <link-aoc>Advent Of Code</link-aoc>)",
|
||||
"go-basics": " (bases et fondamentaux)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,16 +14,79 @@ export const CurriculumVitaeEducation: React.FC<
|
||||
years: t("curriculum-vitae.education.cnam.years.2024-2027.title"),
|
||||
studyType: t("curriculum-vitae.education.cnam.study-type"),
|
||||
institution: t("curriculum-vitae.education.cnam.institution"),
|
||||
score: t("curriculum-vitae.education.cnam.years.2024-2027.description"),
|
||||
// score: t("curriculum-vitae.education.cnam.years.2024-2027.description"),
|
||||
courses: [],
|
||||
},
|
||||
{
|
||||
years: t("curriculum-vitae.education.iut.years.2023-2024.title"),
|
||||
years: t("curriculum-vitae.education.iut.years.title"),
|
||||
studyType: t("curriculum-vitae.education.iut.study-type"),
|
||||
institution: t("curriculum-vitae.education.iut.institution"),
|
||||
score: t("curriculum-vitae.education.iut.years.2023-2024.description"),
|
||||
courses: [
|
||||
t("curriculum-vitae.education.iut.years.2023-2024.courses.web"),
|
||||
{
|
||||
year: t("curriculum-vitae.education.iut.years.2021-2022.description"),
|
||||
title: t("curriculum-vitae.education.iut.years.2021-2022.title"),
|
||||
courses: [
|
||||
t("curriculum-vitae.education.iut.years.2021-2022.courses.java"),
|
||||
t(
|
||||
"curriculum-vitae.education.iut.years.2021-2022.courses.systems-c",
|
||||
),
|
||||
// t(
|
||||
// "curriculum-vitae.education.iut.years.2021-2022.courses.windows-forms",
|
||||
// ),
|
||||
t.rich(
|
||||
"curriculum-vitae.education.iut.years.2021-2022.courses.sql",
|
||||
{
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
year: t("curriculum-vitae.education.iut.years.2022-2023.description"),
|
||||
title: t("curriculum-vitae.education.iut.years.2022-2023.title"),
|
||||
courses: [
|
||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.web"),
|
||||
t.rich(
|
||||
"curriculum-vitae.education.iut.years.2022-2023.courses.tests",
|
||||
{
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
},
|
||||
),
|
||||
t.rich(
|
||||
"curriculum-vitae.education.iut.years.2022-2023.courses.clean-code",
|
||||
{
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
},
|
||||
),
|
||||
// t("curriculum-vitae.education.iut.years.2022-2023.courses.sql-security"),
|
||||
t.rich(
|
||||
"curriculum-vitae.education.iut.years.2022-2023.courses.systems-c",
|
||||
{
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
year: t("curriculum-vitae.education.iut.years.2023-2024.description"),
|
||||
title: t("curriculum-vitae.education.iut.years.2023-2024.title"),
|
||||
courses: [
|
||||
t.rich(
|
||||
"curriculum-vitae.education.iut.years.2023-2024.courses.web",
|
||||
{
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
},
|
||||
),
|
||||
t("curriculum-vitae.education.iut.years.2023-2024.courses.ci-cd"),
|
||||
t(
|
||||
"curriculum-vitae.education.iut.years.2023-2024.courses.complexity-algorithms",
|
||||
@@ -31,42 +94,15 @@ export const CurriculumVitaeEducation: React.FC<
|
||||
t("curriculum-vitae.education.iut.years.2023-2024.courses.no-sql"),
|
||||
],
|
||||
},
|
||||
{
|
||||
years: t("curriculum-vitae.education.iut.years.2022-2023.title"),
|
||||
studyType: t("curriculum-vitae.education.iut.study-type"),
|
||||
institution: t("curriculum-vitae.education.iut.institution"),
|
||||
score: t("curriculum-vitae.education.iut.years.2022-2023.description"),
|
||||
courses: [
|
||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.web"),
|
||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.tests"),
|
||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.clean-code"),
|
||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.systems-c"),
|
||||
t(
|
||||
"curriculum-vitae.education.iut.years.2022-2023.courses.sql-security",
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
years: t("curriculum-vitae.education.iut.years.2021-2022.title"),
|
||||
studyType: t("curriculum-vitae.education.iut.study-type"),
|
||||
institution: t("curriculum-vitae.education.iut.institution"),
|
||||
score: t("curriculum-vitae.education.iut.years.2021-2022.description"),
|
||||
courses: [
|
||||
t("curriculum-vitae.education.iut.years.2021-2022.courses.java"),
|
||||
t("curriculum-vitae.education.iut.years.2021-2022.courses.systems-c"),
|
||||
t(
|
||||
"curriculum-vitae.education.iut.years.2021-2022.courses.windows-forms",
|
||||
),
|
||||
t("curriculum-vitae.education.iut.years.2021-2022.courses.sql"),
|
||||
],
|
||||
},
|
||||
{
|
||||
years: t("curriculum-vitae.education.lycee.years.2019-2021.title"),
|
||||
studyType: t("curriculum-vitae.education.lycee.study-type"),
|
||||
institution: t("curriculum-vitae.education.lycee.institution"),
|
||||
score: t("curriculum-vitae.education.lycee.score"),
|
||||
courses: [],
|
||||
},
|
||||
// {
|
||||
// years: t("curriculum-vitae.education.lycee.years.2019-2021.title"),
|
||||
// studyType: t("curriculum-vitae.education.lycee.study-type"),
|
||||
// institution: t("curriculum-vitae.education.lycee.institution"),
|
||||
// score: t("curriculum-vitae.education.lycee.score"),
|
||||
// courses: [],
|
||||
// },
|
||||
]
|
||||
|
||||
return (
|
||||
@@ -79,14 +115,17 @@ export const CurriculumVitaeEducation: React.FC<
|
||||
{educations.map((education) => {
|
||||
return (
|
||||
<li key={education.years} className="card card-nested">
|
||||
<div className="content">
|
||||
<p className="relative m-0">
|
||||
<strong>{education.studyType}</strong>
|
||||
</p>
|
||||
|
||||
{/* {education.score != null ? (
|
||||
<p className="relative m-0">
|
||||
<strong>{education.score}</strong>
|
||||
</p>
|
||||
) : (
|
||||
<></>
|
||||
)} */}
|
||||
|
||||
<p className="text-muted m-0">{education.institution}</p>
|
||||
|
||||
@@ -95,19 +134,28 @@ export const CurriculumVitaeEducation: React.FC<
|
||||
</p>
|
||||
|
||||
{education.courses.length > 0 ? (
|
||||
<ul className="list-none">
|
||||
{education.courses.map(({ year, title, courses }) => {
|
||||
return (
|
||||
<li key={year}>
|
||||
<span className="font-medium">{year}</span>
|
||||
<span> ({title})</span>
|
||||
<ul
|
||||
style={{
|
||||
paddingInlineStart: 20,
|
||||
}}
|
||||
>
|
||||
{education.courses.map((course) => {
|
||||
return <li key={course}>{course}</li>
|
||||
{courses.map((course, index) => {
|
||||
return <li key={index}>{course}</li>
|
||||
})}
|
||||
</ul>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -10,31 +10,69 @@ export const CurriculumVitaeInterests: React.FC<
|
||||
const t = useTranslations()
|
||||
|
||||
const interests = [
|
||||
t("curriculum-vitae.interests.open-source"),
|
||||
t.rich("curriculum-vitae.interests.fusey", {
|
||||
link: (children) => {
|
||||
t.rich("curriculum-vitae.interests.open-source", {
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
"link-github": (children) => {
|
||||
return (
|
||||
<a href="https://fusey.gg" target="_blank">
|
||||
<a
|
||||
href="https://github.com/theoludwig"
|
||||
target="_blank"
|
||||
className="font-semibold"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
},
|
||||
"link-leon": (children) => {
|
||||
return (
|
||||
<a
|
||||
href="https://github.com/leon-ai/leon"
|
||||
target="_blank"
|
||||
className="font-semibold"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
},
|
||||
"link-markdownlint": (children) => {
|
||||
return (
|
||||
<a
|
||||
href="https://www.npmjs.com/package/markdownlint-rule-relative-links"
|
||||
target="_blank"
|
||||
className="font-semibold"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
},
|
||||
}),
|
||||
t.rich("curriculum-vitae.interests.fusey", {
|
||||
link: (children) => {
|
||||
return (
|
||||
<a href="https://fusey.gg" target="_blank" className="font-semibold">
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
},
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
return (
|
||||
<CurriculumVitaeSection
|
||||
id="interests"
|
||||
title={t("home.interests.title")}
|
||||
title={t("curriculum-vitae.interests.title")}
|
||||
icon={<FaHeart size={24} />}
|
||||
>
|
||||
<ul className="list-unstyled m-0">
|
||||
{interests.map((interest, index) => {
|
||||
return (
|
||||
<li key={index} className="card card-nested">
|
||||
<p>
|
||||
<strong>{interest}</strong>
|
||||
</p>
|
||||
<li key={index} className="card card-nested max-w-2xl">
|
||||
<p>{interest}</p>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -30,7 +30,9 @@ export const CurriculumVitaeProfile: React.FC<
|
||||
<h1 className="h3 my-1!">
|
||||
<strong>{t("meta.title")}</strong>
|
||||
</h1>
|
||||
<h2 className="text-muted h5">{t("curriculum-vitae.description")}</h2>
|
||||
<h2 className="text-muted h5 font-semibold">
|
||||
{t("curriculum-vitae.description")}
|
||||
</h2>
|
||||
<h2 className="text-muted h5">
|
||||
<BirthDate />
|
||||
</h2>
|
||||
@@ -39,17 +41,13 @@ export const CurriculumVitaeProfile: React.FC<
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<div className="relative px-3">
|
||||
<strong className="info">
|
||||
<a className="" href="mailto:contact@theoludwig.fr">
|
||||
contact@theoludwig.fr
|
||||
</a>
|
||||
<strong className="info font-bold">
|
||||
<a href="mailto:contact@theoludwig.fr">contact@theoludwig.fr</a>
|
||||
</strong>
|
||||
</div>
|
||||
<div className="detail">
|
||||
<strong className="info">
|
||||
<Link className="" href="/">
|
||||
https://theoludwig.fr
|
||||
</Link>
|
||||
<strong className="info font-bold">
|
||||
<Link href="/">https://theoludwig.fr</Link>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,11 @@ export const CurriculumVitaeSkills: React.FC<
|
||||
}),
|
||||
{
|
||||
category: "others",
|
||||
skillNames: [t("locales.en-US"), t("home.skills.driving-license")],
|
||||
skillNames: [
|
||||
t("fr-FR-main"),
|
||||
t("locales.en-US"),
|
||||
t("home.skills.driving-license"),
|
||||
],
|
||||
},
|
||||
] as const
|
||||
|
||||
@@ -45,6 +49,23 @@ export const CurriculumVitaeSkills: React.FC<
|
||||
return (
|
||||
<p key={skillName} className="label label-keyword">
|
||||
{skillName}
|
||||
{skillName === "Rust"
|
||||
? t.rich("home.skills.rust-advent-of-code", {
|
||||
"link-aoc": (children) => {
|
||||
return (
|
||||
<a
|
||||
href="https://github.com/theoludwig/advent_of_code"
|
||||
target="_blank"
|
||||
className="no-underline"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
},
|
||||
})
|
||||
: skillName === "Go"
|
||||
? t("home.skills.go-basics")
|
||||
: ""}
|
||||
</p>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -9,13 +9,37 @@ export const CurriculumVitaeWork: React.FC<CurriculumVitaeWorkProps> = () => {
|
||||
|
||||
const workExperiences = [
|
||||
{
|
||||
summary: t("curriculum-vitae.work.ircad.summary"),
|
||||
website: "https://ircad.fr/",
|
||||
name: "IRCAD",
|
||||
location: "1 Place de l'Hôpital, FR-67000 Strasbourg",
|
||||
position: t("curriculum-vitae.work.ircad.position"),
|
||||
dates: "28/08/2023 - 31/08/2027",
|
||||
duration: t("curriculum-vitae.work.ircad.duration"),
|
||||
tasks: [
|
||||
// t("curriculum-vitae.work.ircad.tasks.WebSurg"),
|
||||
t.rich("curriculum-vitae.work.ircad.tasks.Figma", {
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
}),
|
||||
t("curriculum-vitae.work.ircad.tasks.IRCAD-Core"),
|
||||
t("curriculum-vitae.work.ircad.tasks.feature-logs"),
|
||||
t.rich("curriculum-vitae.work.ircad.tasks.feature-permissions", {
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
}),
|
||||
t.rich("curriculum-vitae.work.ircad.tasks.feature-search", {
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
}),
|
||||
t.rich("curriculum-vitae.work.ircad.tasks.feature-architecture", {
|
||||
strong: (children) => {
|
||||
return <strong>{children}</strong>
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
summary: t("curriculum-vitae.work.numerize.summary"),
|
||||
@@ -25,6 +49,7 @@ export const CurriculumVitaeWork: React.FC<CurriculumVitaeWorkProps> = () => {
|
||||
position: t("curriculum-vitae.work.numerize.position"),
|
||||
dates: "11/04/2023 - 26/07/2023",
|
||||
duration: t("curriculum-vitae.work.numerize.duration"),
|
||||
tasks: [],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -50,16 +75,36 @@ export const CurriculumVitaeWork: React.FC<CurriculumVitaeWorkProps> = () => {
|
||||
<strong>{workExperience.position}</strong>
|
||||
</p>
|
||||
|
||||
<p className="text-muted">
|
||||
<p className="text-muted m-0">
|
||||
<small>
|
||||
<span className="space-right">
|
||||
{workExperience.dates} ({workExperience.duration})
|
||||
</span>
|
||||
</small>
|
||||
</p>
|
||||
|
||||
{workExperience.tasks.length > 0 ? (
|
||||
<ul
|
||||
style={{
|
||||
paddingInlineStart: 20,
|
||||
}}
|
||||
className="space-y-1"
|
||||
>
|
||||
{workExperience.tasks.map((task, index) => {
|
||||
return <li key={index}>{task}</li>
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{workExperience.summary != null ? (
|
||||
<div className="mt-2">
|
||||
<p>{workExperience.summary}</p>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -2,9 +2,9 @@ import { Icon } from "./Icon.tsx"
|
||||
|
||||
export const TwitterIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
return (
|
||||
<Icon {...props}>
|
||||
<title>Twitter</title>
|
||||
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z" />
|
||||
<Icon {...props} viewBox="0 0 1200 1227">
|
||||
<title>X/Twitter</title>
|
||||
<path d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z" />
|
||||
</Icon>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export const SocialMediaList: React.FC<SocialMediaListProps> = () => {
|
||||
|
||||
<SocialMediaItem
|
||||
link="https://twitter.com/theoludwig_"
|
||||
ariaLabel="Twitter"
|
||||
ariaLabel="X/Twitter"
|
||||
>
|
||||
<TwitterIcon />
|
||||
</SocialMediaItem>
|
||||
|
||||
@@ -110,29 +110,64 @@ export const SKILLS = {
|
||||
link: "https://www.docker.com/",
|
||||
image: "/images/skills/Docker.webp",
|
||||
},
|
||||
"tRPC/oRPC": {
|
||||
link: "https://trpc.io/",
|
||||
image: "/images/skills/tRPC.webp",
|
||||
},
|
||||
Rust: {
|
||||
link: "https://www.rust-lang.org/",
|
||||
image: "/images/skills/Rust.webp",
|
||||
},
|
||||
Caddy: {
|
||||
link: "https://caddyserver.com/",
|
||||
image: "/images/skills/Caddy.webp",
|
||||
},
|
||||
Proxmox: {
|
||||
link: "https://www.proxmox.com/",
|
||||
image: {
|
||||
light: "/images/skills/Proxmox-light.webp",
|
||||
dark: "/images/skills/Proxmox-dark.webp",
|
||||
},
|
||||
},
|
||||
"GitHub Actions": {
|
||||
link: "https://github.com/features/actions",
|
||||
image: {
|
||||
light: "/images/skills/GitHub-light.webp",
|
||||
dark: "/images/skills/GitHub-dark.webp",
|
||||
},
|
||||
},
|
||||
"GitLab CI/CD": {
|
||||
link: "https://docs.gitlab.com/ci",
|
||||
image: "/images/skills/GitLab.webp",
|
||||
},
|
||||
Go: {
|
||||
link: "https://go.dev/",
|
||||
image: "/images/skills/Go.webp",
|
||||
},
|
||||
} as const
|
||||
|
||||
export type SkillName = keyof typeof SKILLS
|
||||
|
||||
export const SKILL_CATEGORIES = [
|
||||
"programming-languages",
|
||||
"frontend",
|
||||
"backend",
|
||||
"software-development",
|
||||
"sys-admin",
|
||||
"systems-programming",
|
||||
"software-tools",
|
||||
] as const
|
||||
export type SkillCategory = (typeof SKILL_CATEGORIES)[number]
|
||||
|
||||
export const SKILL_NAMES_BY_CATEGORY = {
|
||||
"programming-languages": ["TypeScript", "Python", "C/C++", "PHP"],
|
||||
frontend: ["HTML", "CSS", "Tailwind CSS", "React.js (+ Next.js)"],
|
||||
backend: ["Laravel", "Node.js", "Fastify", "PostgreSQL"],
|
||||
"software-tools": [
|
||||
"GNU/Linux",
|
||||
"Arch Linux",
|
||||
"Visual Studio Code",
|
||||
"Git",
|
||||
"Docker",
|
||||
"software-development": [
|
||||
"TypeScript",
|
||||
"React.js (+ Next.js)",
|
||||
"Tailwind CSS",
|
||||
"Node.js",
|
||||
"tRPC/oRPC",
|
||||
"PostgreSQL",
|
||||
],
|
||||
"sys-admin": ["Docker", "Proxmox", "Caddy", "GitHub Actions", "GitLab CI/CD"],
|
||||
"software-tools": ["GNU/Linux", "Arch Linux", "Visual Studio Code", "Git"],
|
||||
"systems-programming": ["C/C++", "Rust", "Go"],
|
||||
} as const satisfies {
|
||||
[key in SkillCategory]: SkillName[]
|
||||
}
|
||||
|
||||
1879
pnpm-lock.yaml
generated
@@ -12,14 +12,14 @@ catalog:
|
||||
# TypeScript
|
||||
"typescript": "5.9.2"
|
||||
"@total-typescript/ts-reset": "0.6.1"
|
||||
"@types/node": "24.3.0"
|
||||
"@types/node": "24.3.1"
|
||||
|
||||
# Utils
|
||||
"mime": "4.0.7"
|
||||
|
||||
# React.js/Next.js
|
||||
"next": &next "15.5.2"
|
||||
"next-intl": "4.3.5"
|
||||
"next-intl": "4.3.7"
|
||||
"next-themes": "0.4.6"
|
||||
"react": "19.1.1"
|
||||
"react-dom": "19.1.1"
|
||||
@@ -48,11 +48,11 @@ catalog:
|
||||
|
||||
# ESLint
|
||||
"globals": "16.3.0"
|
||||
"typescript-eslint": "8.41.0"
|
||||
"eslint": "9.34.0"
|
||||
"eslint-config-conventions": "20.1.3"
|
||||
"typescript-eslint": "8.43.0"
|
||||
"eslint": "9.35.0"
|
||||
"eslint-config-conventions": "20.2.0"
|
||||
"eslint-plugin-promise": "7.2.1"
|
||||
"eslint-plugin-unicorn": "60.0.0"
|
||||
"eslint-plugin-unicorn": "61.0.2"
|
||||
"eslint-plugin-import-x": "4.16.1"
|
||||
"@next/eslint-plugin-next": *next
|
||||
"eslint-plugin-react": "7.37.5"
|
||||
@@ -64,7 +64,7 @@ catalog:
|
||||
"editorconfig-checker": "6.1.0"
|
||||
|
||||
# Storybook
|
||||
"storybook": &storybook "9.1.3"
|
||||
"storybook": &storybook "9.1.5"
|
||||
"@storybook/addon-docs": *storybook
|
||||
"@storybook/addon-a11y": *storybook
|
||||
"@storybook/nextjs": *storybook
|
||||
@@ -76,17 +76,17 @@ catalog:
|
||||
# Testing
|
||||
"playwright": &playwright "1.55.0"
|
||||
"@playwright/test": *playwright
|
||||
"start-server-and-test": "2.0.13"
|
||||
"start-server-and-test": "2.1.0"
|
||||
|
||||
# CSS
|
||||
"postcss": "8.5.6"
|
||||
"@tailwindcss/postcss": "4.1.12"
|
||||
"@tailwindcss/postcss": "4.1.13"
|
||||
"@tailwindcss/typography": "0.5.16"
|
||||
"tailwindcss": "4.1.12"
|
||||
"tailwindcss": "4.1.13"
|
||||
"tailwind-merge": "3.3.1"
|
||||
"clsx": "2.1.1"
|
||||
"cva": "1.0.0-beta.4"
|
||||
"@fontsource/montserrat": "5.2.6"
|
||||
"@fontsource/montserrat": "5.2.7"
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- "@swc/core"
|
||||
|
||||