mirror of
				https://github.com/theoludwig/theoludwig.git
				synced 2025-10-14 20:23:25 +02:00 
			
		
		
		
	feat: components structure Curriculum Vitae
This commit is contained in:
		| @@ -1,4 +1,5 @@ | ||||
| import type { LocaleProps } from "@repo/i18n/config" | ||||
| import { CurriculumVitae } from "@repo/ui/CurriculumVitae" | ||||
| import { unstable_setRequestLocale } from "next-intl/server" | ||||
|  | ||||
| interface CurriculumVitaeProps extends LocaleProps {} | ||||
| @@ -9,11 +10,7 @@ const CurriculumVitaePage: React.FC<CurriculumVitaeProps> = (props) => { | ||||
|   // Enable static rendering | ||||
|   unstable_setRequestLocale(params.locale) | ||||
|  | ||||
|   return ( | ||||
|     <main> | ||||
|       <h1>CurriculumVitae</h1> | ||||
|     </main> | ||||
|   ) | ||||
|   return <CurriculumVitae /> | ||||
| } | ||||
|  | ||||
| export default CurriculumVitaePage | ||||
|   | ||||
| @@ -160,3 +160,227 @@ code .line:last-child { | ||||
|   white-space: normal !important; | ||||
|   width: 100% !important; | ||||
| } | ||||
|  | ||||
| .container-fluid { | ||||
|   padding-right: 15px; | ||||
|   padding-left: 15px; | ||||
|   margin-right: auto; | ||||
|   margin-left: auto; | ||||
| } | ||||
| .curriculum-vitae { | ||||
|   background: #f0f0f0; | ||||
|   color: #333; | ||||
|   line-height: 1.42857143; | ||||
|   font-size: 14px; | ||||
|  | ||||
|   hr { | ||||
|     margin-top: 15px; | ||||
|     margin-bottom: 15px; | ||||
|     border: 0; | ||||
|     border-top: 1px solid #eee; | ||||
|   } | ||||
|   p { | ||||
|     margin: 0; | ||||
|   } | ||||
|   strong { | ||||
|     font-weight: 600; | ||||
|   } | ||||
|   a { | ||||
|     color: #337ab7; | ||||
|     text-decoration: none; | ||||
|   } | ||||
|   a:focus, | ||||
|   a:hover { | ||||
|     color: #23527c; | ||||
|     text-decoration: underline; | ||||
|   } | ||||
|   .link-disguise { | ||||
|     color: inherit; | ||||
|   } | ||||
|   .link-disguise:hover { | ||||
|     color: inherit; | ||||
|   } | ||||
|   .h1, | ||||
|   .h2, | ||||
|   .h3, | ||||
|   h1, | ||||
|   h2, | ||||
|   h3 { | ||||
|     margin-top: 20px; | ||||
|     margin-bottom: 10px; | ||||
|   } | ||||
|   .h4, | ||||
|   .h5, | ||||
|   .h6, | ||||
|   h4, | ||||
|   h5, | ||||
|   h6 { | ||||
|     margin-top: 10px; | ||||
|     margin-bottom: 10px; | ||||
|   } | ||||
|   .h1, | ||||
|   .h2, | ||||
|   .h3, | ||||
|   .h4, | ||||
|   .h5, | ||||
|   .h6, | ||||
|   h1, | ||||
|   h2, | ||||
|   h3, | ||||
|   h4, | ||||
|   h5, | ||||
|   h6 { | ||||
|     font-family: inherit; | ||||
|     font-weight: 500; | ||||
|     line-height: 1.1; | ||||
|     color: inherit; | ||||
|   } | ||||
|   .h3, | ||||
|   h3 { | ||||
|     font-size: 24px; | ||||
|   } | ||||
|   .h4, | ||||
|   h4 { | ||||
|     font-size: 18px; | ||||
|   } | ||||
|   .h5, | ||||
|   h5 { | ||||
|     font-size: 14px; | ||||
|   } | ||||
|   .row { | ||||
|     margin-right: -15px; | ||||
|     margin-left: -15px; | ||||
|   } | ||||
|   .clear-margin { | ||||
|     margin: 0; | ||||
|   } | ||||
|   .relative { | ||||
|     position: relative; | ||||
|   } | ||||
|   .center-block { | ||||
|     display: block; | ||||
|     margin-right: auto; | ||||
|     margin-left: auto; | ||||
|   } | ||||
|   .text-muted { | ||||
|     color: #777; | ||||
|   } | ||||
|   .text-uppercase { | ||||
|     text-transform: uppercase; | ||||
|   } | ||||
|   .list-unstyled { | ||||
|     padding-left: 0; | ||||
|     list-style: none; | ||||
|   } | ||||
|  | ||||
|   .main { | ||||
|     padding: 5px; | ||||
|   } | ||||
|   .title { | ||||
|     font-weight: 600; | ||||
|   } | ||||
|  | ||||
|   .profile-card-wrapper { | ||||
|     position: relative; | ||||
|   } | ||||
|  | ||||
|   .card-wrapper { | ||||
|     float: none !important; | ||||
|     padding: 5px; | ||||
|   } | ||||
|  | ||||
|   .profile-card-wrapper .profile-card { | ||||
|     padding: 10px; | ||||
|   } | ||||
|  | ||||
|   .card { | ||||
|     background: white; | ||||
|     border-radius: 3px; | ||||
|     padding: 10px 0; | ||||
|   } | ||||
|   .profile-pic { | ||||
|     padding: 10px 0; | ||||
|   } | ||||
|   .profile-pic img { | ||||
|     width: 100px; | ||||
|     height: 100px; | ||||
|     border-radius: 50%; | ||||
|     vertical-align: middle; | ||||
|     border: 0; | ||||
|   } | ||||
|   .contact-details { | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|   } | ||||
|   .contact-details .detail { | ||||
|     position: relative; | ||||
|     min-height: 1px; | ||||
|     padding: 10px; | ||||
|   } | ||||
|   .social-links { | ||||
|     line-height: 2.5; | ||||
|   } | ||||
|   .experience-description { | ||||
|     margin-top: 10px; | ||||
|   } | ||||
|  | ||||
|   .background-details .detail { | ||||
|     display: table; | ||||
|   } | ||||
|   .background-details .detail .icon, | ||||
|   .background-details .detail .info { | ||||
|     display: table-cell; | ||||
|   } | ||||
|   .background-details .detail .icon { | ||||
|     color: #707070; | ||||
|   } | ||||
|   .background-details .detail .icon { | ||||
|     min-width: 45px; | ||||
|     max-width: 45px; | ||||
|     text-align: center; | ||||
|   } | ||||
|   .icon img { | ||||
|     width: 20px; | ||||
|     height: 20px; | ||||
|   } | ||||
|   .background-details .detail .mobile-title { | ||||
|     display: none; | ||||
|   } | ||||
|   .card-nested { | ||||
|     min-height: 0; | ||||
|   } | ||||
|  | ||||
|   .card-skills { | ||||
|     position: relative; | ||||
|   } | ||||
|   .labels { | ||||
|     line-height: 2; | ||||
|   } | ||||
|   .space-top { | ||||
|     margin-top: 10px; | ||||
|   } | ||||
|   .label { | ||||
|     display: inline; | ||||
|     padding: 0.2em 0.6em 0.3em; | ||||
|     font-size: 75%; | ||||
|     font-weight: 600; | ||||
|     line-height: 1; | ||||
|     text-align: center; | ||||
|     white-space: nowrap; | ||||
|     vertical-align: baseline; | ||||
|     border-radius: 0.25em; | ||||
|   } | ||||
|   .label-keyword { | ||||
|     display: inline-block; | ||||
|     font-size: 0.9em; | ||||
|     padding: 5px; | ||||
|     border: 1px solid #357ebd; | ||||
|     margin-right: 5px; | ||||
|   } | ||||
|   .label-keyword p { | ||||
|     margin: 0; | ||||
|   } | ||||
|   .section-separated { | ||||
|     display: flex; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,181 @@ | ||||
| { | ||||
|   "$schema": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json", | ||||
|   "basics": { | ||||
|     "name": "Théo LUDWIG", | ||||
|     "label": "Développeur Full Stack • Étudiant", | ||||
|     "image": "https://theoludwig.fr/images/logo_background.png", | ||||
|     "email": "contact@theoludwig.fr", | ||||
|     "age": "31/03/2003", | ||||
|     "location": { | ||||
|       "address": "Alsace, France", | ||||
|     }, | ||||
|     "url": "https://theoludwig.fr", | ||||
|     "summary": "Je me demande constamment comment améliorer notre présent, afin de rendre notre futur meilleur, particulièrement grâce aux progrès de l'informatique. <br /> Ma priorité réside dans la création d'expériences utilisateurs (UX) intuitives, répondant aux besoins des utilisateurs de la manière la plus efficace que possible.", | ||||
|   }, | ||||
|   "education": [ | ||||
|     { | ||||
|       "startDate": "2023", | ||||
|       "endDate": "2024", | ||||
|       "studyType": "Bachelor Universitaire de Technologie (BUT) Informatique", | ||||
|       "institution": "IUT Robert Schuman à Illkirch-Graffenstaden", | ||||
|       "score": "3ème année", | ||||
|       "courses": [ | ||||
|         "Développement Web en Node.js et React.js", | ||||
|         "Intégration/Déploiement Continue et Docker", | ||||
|         "Complexité Algorithmique Théorique et Pratique en C++", | ||||
|         // "Projet développement LLM (Large Language Model) et NLP (Natural Language Processing)", | ||||
|         "Base de données NoSQL (Redis, MongoDB, Cassandra)", | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
|       "startDate": "2022", | ||||
|       "endDate": "2023", | ||||
|       "studyType": "Bachelor Universitaire de Technologie (BUT) Informatique", | ||||
|       "institution": "IUT Robert Schuman à Illkirch-Graffenstaden", | ||||
|       "score": "2ème année", | ||||
|       "courses": [ | ||||
|         "Développement Web avec le framework Laravel en PHP", | ||||
|         "Qualité de développement et Tests automatisés", | ||||
|         "Patrons et Principes de conceptions (Code maintenable et réutilisable) en UML", | ||||
|         "Programmation systèmes en C (Multi-Thread, Serveur/Client UDP/TCP)", | ||||
|         "Sécurisation des accès à la base de données et PL/SQL", | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
|       "startDate": "2021", | ||||
|       "endDate": "2022", | ||||
|       "studyType": "Bachelor Universitaire de Technologie (BUT) Informatique", | ||||
|       "institution": "IUT Robert Schuman à Illkirch-Graffenstaden", | ||||
|       "score": "1ère année", | ||||
|       "courses": [ | ||||
|         "Développement Orientée Objet en Java", | ||||
|         "Programmation systèmes en C (Allocation mémoire, Pointeurs, Structures)", | ||||
|         "Développement d'application Windows Forms (.NET Framework) en C#", | ||||
|         "Base de données relationnelles et langage SQL", | ||||
|       ], | ||||
|     }, | ||||
|     { | ||||
|       "startDate": "2019", | ||||
|       "endDate": "2021", | ||||
|       "studyType": "Baccalauréat Général (Mathématiques et Numériques Sciences Informatiques)", | ||||
|       "institution": "Lycée Heinrich Nessel à Haguenau", | ||||
|       "score": "Mention Assez Bien", | ||||
|     }, | ||||
|     // { | ||||
|     //   "startDate": "2014", | ||||
|     //   "endDate": "2018", | ||||
|     //   "studyType": "Diplôme national du brevet", | ||||
|     //   "institution": "Collège Gustave Doré à Hochfelden", | ||||
|     //   "score": "Mention Bien" | ||||
|     // } | ||||
|   ], | ||||
|   "work": [ | ||||
|     { | ||||
|       "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.", | ||||
|       "website": "https://ircad.fr/", | ||||
|       "name": "IRCAD", | ||||
|       "location": "1 Place de l'Hôpital, 67000 Strasbourg", | ||||
|       "position": "Alternant Développeur Web Full Stack", | ||||
|       "startDate": "2023-08-28", | ||||
|       "endDate": "2024-09-02", | ||||
|       "duration": "1 an", | ||||
|     }, | ||||
|     { | ||||
|       "summary": "Développement d'un outil GED (Gestion Électronique de Documents) en React.js, Laravel et GraphQL.", | ||||
|       "website": "https://numerize.com/", | ||||
|       "name": "Numerize", | ||||
|       "location": "4 Rue Sophie Germain, 67720 Hœrdt", | ||||
|       "position": "Stagiaire Développeur Web Full Stack", | ||||
|       "startDate": "2023-04-11", | ||||
|       "endDate": "2023-07-26", | ||||
|       "duration": "4 mois", | ||||
|     }, | ||||
|     // { | ||||
|     //   "summary": "Agent administratif - Numérisation et archivage des plans électriques initialement sous format papier calque.", | ||||
|     //   "website": "https://www.es.fr/", | ||||
|     //   "name": "ÉS (Électricité de Strasbourg)", | ||||
|     //   "location": "5 Rue André Marie Ampère, 67450 Mundolsheim", | ||||
|     //   "position": "Emploi d'été en qualité d'agent administratif", | ||||
|     //   "startDate": "2021-07-07", | ||||
|     //   "endDate": "2021-07-30", | ||||
|     //   "duration": "1 mois" | ||||
|     // }, | ||||
|     { | ||||
|       "summary": "Développement d'un site web pour trouver un restaurant à la pause repas.", | ||||
|       "website": "https://itpartners.fr/", | ||||
|       "name": "Tribe | IT Partners", | ||||
|       "location": "16 Rue du Parc, 67205 Oberhausbergen", | ||||
|       "position": "Stage initiation métier développeur web", | ||||
|       "startDate": "2019-06-17", | ||||
|       "endDate": "2019-06-21", | ||||
|       "duration": "1 semaine", | ||||
|     }, | ||||
|     { | ||||
|       "description": "interests", | ||||
|       "summary": "Développement site web en React.js et Strapi.<br /> Classé n°1 en France sur le Défi de l'entreprise <a href=\"https://toolpad.fr/\">ToolPad</a>.", | ||||
|       "website": "https://nuitdelinfo.com/", | ||||
|       "name": "La Nuit de l'info 2021", | ||||
|       "position": "Participation en équipe de 5 personnes", | ||||
|       "startDate": "2021-12-02", | ||||
|       "endDate": "2021-12-03", | ||||
|       "duration": "1 semaine", | ||||
|     }, | ||||
|     { | ||||
|       "description": "interests", | ||||
|       "summary": "Hackathon développement d'une landing page et web scraping.", | ||||
|       "website": "https://wildcodeschool.fr/", | ||||
|       "name": "Wild Code School", | ||||
|       "location": "32 Rue du Bass. d'Austerlitz, 67100 Strasbourg", | ||||
|       "position": "Initiation métier Développeur web", | ||||
|       "startDate": "2019-06-24", | ||||
|       "endDate": "2019-06-28", | ||||
|       "duration": "1 semaine", | ||||
|     }, | ||||
|     // { | ||||
|     //   "summary": "Apprentissage du métier \"Chargé de communication\" et des logiciels de graphisme tels que \"Adobe Photoshop\".", | ||||
|     //   "website": "https://es.fr/", | ||||
|     //   "name": "ÉS (Électricité de Strasbourg)", | ||||
|     //   "location": "26 Bd du Président-Wilson, 67000 Strasbourg", | ||||
|     //   "position": "Stage de découverte (3ème)", | ||||
|     //   "startDate": "2018-02-19", | ||||
|     //   "endDate": "2018-02-23", | ||||
|     //   "duration": "1 semaine" | ||||
|     // } | ||||
|   ], | ||||
|   "interests": [ | ||||
|     { | ||||
|       "name": "Enthousiaste de l'Open-Source", | ||||
|     }, | ||||
|     { | ||||
|       "name": "Passionné de High-Tech", | ||||
|     }, | ||||
|   ], | ||||
|   "skills": [ | ||||
|     { | ||||
|       "keywords": ["JavaScript/TypeScript", "Python", "C/C++", "PHP"], | ||||
|       "name": "Langages de programmation", | ||||
|     }, | ||||
|     { | ||||
|       "keywords": ["HTML", "CSS", "Tailwind CSS", "React.js/Next.js"], | ||||
|       "name": "Frontend", | ||||
|     }, | ||||
|     { | ||||
|       "keywords": ["Laravel", "Node.js", "Fastify", "PostgreSQL"], | ||||
|       "name": "Backend", | ||||
|     }, | ||||
|     { | ||||
|       "keywords": [ | ||||
|         "GNU/Linux", | ||||
|         "Arch Linux", | ||||
|         "Visual Studio Code", | ||||
|         "Git", | ||||
|         "Docker", | ||||
|       ], | ||||
|       "name": "Logiciels et outils", | ||||
|     }, | ||||
|     { | ||||
|       "keywords": ["Permis B", "Anglais"], | ||||
|       "name": "Autres", | ||||
|     }, | ||||
|   ], | ||||
| } | ||||
| @@ -7,6 +7,9 @@ | ||||
|     "en-US": "English", | ||||
|     "fr-FR": "French" | ||||
|   }, | ||||
|   "common": { | ||||
|     "others": "Others" | ||||
|   }, | ||||
|   "footer": { | ||||
|     "all-rights-reserved": "All rights reserved" | ||||
|   }, | ||||
| @@ -54,7 +57,8 @@ | ||||
|       "programming-languages": "Programming languages", | ||||
|       "frontend": "Frontend", | ||||
|       "backend": "Backend", | ||||
|       "software-tools": "Software and tools" | ||||
|       "software-tools": "Software and tools", | ||||
|       "driving-license": "Permis B" | ||||
|     }, | ||||
|     "portfolio": { | ||||
|       "title": "Portfolio", | ||||
| @@ -71,5 +75,110 @@ | ||||
|       "title": "Open-Source", | ||||
|       "description": "Most famous open source projects I contributed to." | ||||
|     } | ||||
|   }, | ||||
|   "curriculum-vitae": { | ||||
|     "description": "Développeur Full Stack • Étudiant", | ||||
|     "about": { | ||||
|       "title": "À propos", | ||||
|       "description": "Je me demande constamment comment améliorer notre présent, afin de rendre notre futur meilleur, particulièrement grâce aux progrès de l'informatique. <br></br> Ma priorité réside dans la création d'expériences utilisateurs (UX) intuitives, répondant aux besoins des utilisateurs de la manière la plus efficace que possible." | ||||
|     }, | ||||
|     "education": { | ||||
|       "title": "Formations", | ||||
|       "iut": { | ||||
|         "study-type": "Bachelor Universitaire de Technologie (BUT) Informatique", | ||||
|         "institution": "IUT Robert Schuman à Illkirch-Graffenstaden", | ||||
|         "years": { | ||||
|           "2023-2024": { | ||||
|             "title": "2023 - 2024", | ||||
|             "description": "3ème année", | ||||
|             "courses": { | ||||
|               "web": "Développement Web en Node.js et React.js", | ||||
|               "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)" | ||||
|             } | ||||
|           }, | ||||
|           "2022-2023": { | ||||
|             "title": "2022 - 2023", | ||||
|             "description": "2ème année", | ||||
|             "courses": { | ||||
|               "web": "Développement Web avec le framework Laravel en PHP", | ||||
|               "tests": "Qualité de développement et Tests automatisés", | ||||
|               "clean-code": "Patrons et Principes de conceptions (Code maintenable et réutilisable) en UML", | ||||
|               "systems-c": "Programmation systèmes en C (Multi-Thread, Serveur/Client UDP/TCP)", | ||||
|               "sql-security": "Sécurisation des accès à la base de données et PL/SQL" | ||||
|             } | ||||
|           }, | ||||
|           "2021-2022": { | ||||
|             "title": "2021 - 2022", | ||||
|             "description": "1ère année", | ||||
|             "courses": { | ||||
|               "java": "Développement Orientée Objet en Java", | ||||
|               "systems-c": "Programmation systèmes en C (Allocation mémoire, Pointeurs, Structures)", | ||||
|               "windows-forms": "Développement d'application Windows Forms (.NET Framework) en C#", | ||||
|               "sql": "Base de données relationnelles et langage SQL" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "lycee": { | ||||
|         "study-type": "Baccalauréat Général (Mathématiques et Numériques Sciences Informatiques)", | ||||
|         "institution": "Lycée Heinrich Nessel à Haguenau", | ||||
|         "score": "Mention Assez Bien", | ||||
|         "years": { | ||||
|           "2019-2021": { | ||||
|             "title": "2019 - 2021" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "work": { | ||||
|       "title": "Expériences", | ||||
|       "ircad": { | ||||
|         "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.", | ||||
|         "website": "https://ircad.fr/", | ||||
|         "name": "IRCAD", | ||||
|         "location": "1 Place de l'Hôpital, 67000 Strasbourg", | ||||
|         "position": "Alternant Développeur Web Full Stack", | ||||
|         "startDate": "2023-08-28", | ||||
|         "endDate": "2024-09-02", | ||||
|         "duration": "1 an" | ||||
|       }, | ||||
|       "numerize": { | ||||
|         "summary": "Développement d'un outil GED (Gestion Électronique de Documents) en React.js, Laravel et GraphQL.", | ||||
|         "website": "https://numerize.com/", | ||||
|         "name": "Numerize", | ||||
|         "location": "4 Rue Sophie Germain, 67720 Hœrdt", | ||||
|         "position": "Stagiaire Développeur Web Full Stack", | ||||
|         "startDate": "2023-04-11", | ||||
|         "endDate": "2023-07-26", | ||||
|         "duration": "4 mois" | ||||
|       } | ||||
|     }, | ||||
|     "interests-work": { | ||||
|       "nuit-info-2021": { | ||||
|         "summary": "Développement site web en React.js et Strapi.<br></br> Classé n°1 en France sur le Défi de l'entreprise <toolpad-link>ToolPad</toolpad-link>.", | ||||
|         "website": "https://nuitdelinfo.com/", | ||||
|         "name": "La Nuit de l'info 2021", | ||||
|         "position": "Participation en équipe de 5 personnes", | ||||
|         "startDate": "2021-12-02", | ||||
|         "endDate": "2021-12-03", | ||||
|         "duration": "1 semaine" | ||||
|       }, | ||||
|       "wild-code-school": { | ||||
|         "summary": "Hackathon développement d'une landing page et web scraping.", | ||||
|         "website": "https://wildcodeschool.fr/", | ||||
|         "name": "Wild Code School", | ||||
|         "location": "32 Rue du Bassin d'Austerlitz, 67100 Strasbourg", | ||||
|         "position": "Initiation métier Développeur web", | ||||
|         "startDate": "2019-06-24", | ||||
|         "endDate": "2019-06-28", | ||||
|         "duration": "1 semaine" | ||||
|       } | ||||
|     }, | ||||
|     "interests": { | ||||
|       "open-source": "Enthousiaste de l'Open-Source", | ||||
|       "high-tech": "Passionné de High-Tech" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,9 @@ | ||||
|     "en-US": "Anglais", | ||||
|     "fr-FR": "Français" | ||||
|   }, | ||||
|   "common": { | ||||
|     "others": "Autres" | ||||
|   }, | ||||
|   "footer": { | ||||
|     "all-rights-reserved": "Tous droits réservés" | ||||
|   }, | ||||
| @@ -54,7 +57,8 @@ | ||||
|       "programming-languages": "Langages de programmation", | ||||
|       "frontend": "Frontend", | ||||
|       "backend": "Backend", | ||||
|       "software-tools": "Logiciels et outils" | ||||
|       "software-tools": "Logiciels et outils", | ||||
|       "driving-license": "Permis B" | ||||
|     }, | ||||
|     "portfolio": { | ||||
|       "title": "Portfolio", | ||||
| @@ -71,5 +75,110 @@ | ||||
|       "title": "Open-Source", | ||||
|       "description": "Projets open source les plus célèbres auxquels j'ai contribué." | ||||
|     } | ||||
|   }, | ||||
|   "curriculum-vitae": { | ||||
|     "description": "Développeur Full Stack • Étudiant", | ||||
|     "about": { | ||||
|       "title": "À propos", | ||||
|       "description": "Je me demande constamment comment améliorer notre présent, afin de rendre notre futur meilleur, particulièrement grâce aux progrès de l'informatique. <br></br> Ma priorité réside dans la création d'expériences utilisateurs (UX) intuitives, répondant aux besoins des utilisateurs de la manière la plus efficace que possible." | ||||
|     }, | ||||
|     "education": { | ||||
|       "title": "Formations", | ||||
|       "iut": { | ||||
|         "study-type": "Bachelor Universitaire de Technologie (BUT) Informatique", | ||||
|         "institution": "IUT Robert Schuman à Illkirch-Graffenstaden", | ||||
|         "years": { | ||||
|           "2023-2024": { | ||||
|             "title": "2023 - 2024", | ||||
|             "description": "3ème année", | ||||
|             "courses": { | ||||
|               "web": "Développement Web en Node.js et React.js", | ||||
|               "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)" | ||||
|             } | ||||
|           }, | ||||
|           "2022-2023": { | ||||
|             "title": "2022 - 2023", | ||||
|             "description": "2ème année", | ||||
|             "courses": { | ||||
|               "web": "Développement Web avec le framework Laravel en PHP", | ||||
|               "tests": "Qualité de développement et Tests automatisés", | ||||
|               "clean-code": "Patrons et Principes de conceptions (Code maintenable et réutilisable) en UML", | ||||
|               "systems-c": "Programmation systèmes en C (Multi-Thread, Serveur/Client UDP/TCP)", | ||||
|               "sql-security": "Sécurisation des accès à la base de données et PL/SQL" | ||||
|             } | ||||
|           }, | ||||
|           "2021-2022": { | ||||
|             "title": "2021 - 2022", | ||||
|             "description": "1ère année", | ||||
|             "courses": { | ||||
|               "java": "Développement Orientée Objet en Java", | ||||
|               "systems-c": "Programmation systèmes en C (Allocation mémoire, Pointeurs, Structures)", | ||||
|               "windows-forms": "Développement d'application Windows Forms (.NET Framework) en C#", | ||||
|               "sql": "Base de données relationnelles et langage SQL" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "lycee": { | ||||
|         "study-type": "Baccalauréat Général (Mathématiques et Numériques Sciences Informatiques)", | ||||
|         "institution": "Lycée Heinrich Nessel à Haguenau", | ||||
|         "score": "Mention Assez Bien", | ||||
|         "years": { | ||||
|           "2019-2021": { | ||||
|             "title": "2019 - 2021" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "work": { | ||||
|       "title": "Expériences", | ||||
|       "ircad": { | ||||
|         "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.", | ||||
|         "website": "https://ircad.fr/", | ||||
|         "name": "IRCAD", | ||||
|         "location": "1 Place de l'Hôpital, 67000 Strasbourg", | ||||
|         "position": "Alternant Développeur Web Full Stack", | ||||
|         "startDate": "2023-08-28", | ||||
|         "endDate": "2024-09-02", | ||||
|         "duration": "1 an" | ||||
|       }, | ||||
|       "numerize": { | ||||
|         "summary": "Développement d'un outil GED (Gestion Électronique de Documents) en React.js, Laravel et GraphQL.", | ||||
|         "website": "https://numerize.com/", | ||||
|         "name": "Numerize", | ||||
|         "location": "4 Rue Sophie Germain, 67720 Hœrdt", | ||||
|         "position": "Stagiaire Développeur Web Full Stack", | ||||
|         "startDate": "2023-04-11", | ||||
|         "endDate": "2023-07-26", | ||||
|         "duration": "4 mois" | ||||
|       } | ||||
|     }, | ||||
|     "interests-work": { | ||||
|       "nuit-info-2021": { | ||||
|         "summary": "Développement site web en React.js et Strapi. <br></br> Classé n°1 en France sur le Défi de l'entreprise <toolpad-link>ToolPad</toolpad-link>.", | ||||
|         "website": "https://nuitdelinfo.com/", | ||||
|         "name": "La Nuit de l'info 2021", | ||||
|         "position": "Participation en équipe de 5 personnes", | ||||
|         "startDate": "2021-12-02", | ||||
|         "endDate": "2021-12-03", | ||||
|         "duration": "1 semaine" | ||||
|       }, | ||||
|       "wild-code-school": { | ||||
|         "summary": "Hackathon développement d'une landing page et web scraping.", | ||||
|         "website": "https://wildcodeschool.fr/", | ||||
|         "name": "Wild Code School", | ||||
|         "location": "32 Rue du Bassin d'Austerlitz, 67100 Strasbourg", | ||||
|         "position": "Initiation métier Développeur web", | ||||
|         "startDate": "2019-06-24", | ||||
|         "endDate": "2019-06-28", | ||||
|         "duration": "1 semaine" | ||||
|       } | ||||
|     }, | ||||
|     "interests": { | ||||
|       "open-source": "Enthousiaste de l'Open-Source", | ||||
|       "high-tech": "Passionné de High-Tech" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
|   "private": true, | ||||
|   "type": "module", | ||||
|   "exports": { | ||||
|     "./CurriculumVitae": "./src/CurriculumVitae/CurriculumVitae.tsx", | ||||
|     "./Design/Button": "./src/Design/Button/Button.tsx", | ||||
|     "./Design/Link": "./src/Design/Link/Link.tsx", | ||||
|     "./Design/Spinner": "./src/Design/Spinner/Spinner.tsx", | ||||
|   | ||||
							
								
								
									
										16
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitae.stories.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitae.stories.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| import type { Meta, StoryObj } from "@storybook/react" | ||||
|  | ||||
| import { CurriculumVitae as CurriculumVitaeComponent } from "./CurriculumVitae" | ||||
|  | ||||
| const meta = { | ||||
|   title: "Curriculum Vitae/CurriculumVitae", | ||||
|   component: CurriculumVitaeComponent, | ||||
| } satisfies Meta<typeof CurriculumVitaeComponent> | ||||
|  | ||||
| export default meta | ||||
|  | ||||
| type Story = StoryObj<typeof meta> | ||||
|  | ||||
| export const CurriculumVitae: Story = { | ||||
|   args: {}, | ||||
| } | ||||
							
								
								
									
										40
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitae.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitae.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| import { CurriculumVitaeAbout } from "./CurriculumVitaeAbout" | ||||
| import { CurriculumVitaeEducation } from "./CurriculumVitaeEducation" | ||||
| import { CurriculumVitaeInterests } from "./CurriculumVitaeInterests" | ||||
| import { CurriculumVitaeProfile } from "./CurriculumVitaeProfile" | ||||
| import { CurriculumVitaeSkills } from "./CurriculumVitaeSkills" | ||||
| import { CurriculumVitaeWork } from "./CurriculumVitaeWork" | ||||
|  | ||||
| export interface CurriculumVitaeProps {} | ||||
|  | ||||
| export const CurriculumVitae: React.FC<CurriculumVitaeProps> = () => { | ||||
|   return ( | ||||
|     <main className="curriculum-vitae container-fluid"> | ||||
|       <div className="row main clearfix"> | ||||
|         <section className="col-md-3 card-wrapper profile-card-wrapper affix"> | ||||
|           <CurriculumVitaeProfile /> | ||||
|  | ||||
|           <div className="card background-card"> | ||||
|             <div className="background-details"> | ||||
|               <CurriculumVitaeAbout /> | ||||
|  | ||||
|               <hr /> | ||||
|  | ||||
|               <section className="section-separated"> | ||||
|                 <CurriculumVitaeEducation /> | ||||
|                 <CurriculumVitaeSkills /> | ||||
|               </section> | ||||
|  | ||||
|               <hr /> | ||||
|  | ||||
|               <section className="section-separated"> | ||||
|                 <CurriculumVitaeWork /> | ||||
|                 <CurriculumVitaeInterests /> | ||||
|               </section> | ||||
|             </div> | ||||
|           </div> | ||||
|         </section> | ||||
|       </div> | ||||
|     </main> | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										19
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeAbout.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeAbout.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| import { useTranslations } from "next-intl" | ||||
| import { FaUser } from "react-icons/fa" | ||||
| import { CurriculumVitaeSection } from "./CurriculumVitaeSection" | ||||
|  | ||||
| export interface CurriculumVitaeAboutProps {} | ||||
|  | ||||
| export const CurriculumVitaeAbout: React.FC<CurriculumVitaeAboutProps> = () => { | ||||
|   const t = useTranslations() | ||||
|  | ||||
|   return ( | ||||
|     <CurriculumVitaeSection | ||||
|       id="about" | ||||
|       title={t("curriculum-vitae.about.title")} | ||||
|       icon={<FaUser size={24} />} | ||||
|     > | ||||
|       <p>{t.rich("curriculum-vitae.about.description")}</p> | ||||
|     </CurriculumVitaeSection> | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										21
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeEducation.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeEducation.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| import { useTranslations } from "next-intl" | ||||
| import { FaGraduationCap } from "react-icons/fa" | ||||
| import { CurriculumVitaeSection } from "./CurriculumVitaeSection" | ||||
|  | ||||
| export interface CurriculumVitaeEducationProps {} | ||||
|  | ||||
| export const CurriculumVitaeEducation: React.FC< | ||||
|   CurriculumVitaeEducationProps | ||||
| > = () => { | ||||
|   const t = useTranslations() | ||||
|  | ||||
|   return ( | ||||
|     <CurriculumVitaeSection | ||||
|       id="education" | ||||
|       title={t("curriculum-vitae.education.title")} | ||||
|       icon={<FaGraduationCap size={24} />} | ||||
|     > | ||||
|       <p>Test</p> | ||||
|     </CurriculumVitaeSection> | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										21
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeInterests.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeInterests.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| import { useTranslations } from "next-intl" | ||||
| import { FaHeart } from "react-icons/fa" | ||||
| import { CurriculumVitaeSection } from "./CurriculumVitaeSection" | ||||
|  | ||||
| export interface CurriculumVitaeInterestsProps {} | ||||
|  | ||||
| export const CurriculumVitaeInterests: React.FC< | ||||
|   CurriculumVitaeInterestsProps | ||||
| > = () => { | ||||
|   const t = useTranslations() | ||||
|  | ||||
|   return ( | ||||
|     <CurriculumVitaeSection | ||||
|       id="interests" | ||||
|       title={t("home.interests.title")} | ||||
|       icon={<FaHeart size={24} />} | ||||
|     > | ||||
|       <p>Test</p> | ||||
|     </CurriculumVitaeSection> | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										55
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeProfile.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeProfile.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| import { Link } from "@repo/i18n/navigation" | ||||
| import { useTranslations } from "next-intl" | ||||
| import Image from "next/image" | ||||
| import { BirthDate } from "../Home/About/AboutList/BirthDate" | ||||
|  | ||||
| export interface CurriculumVitaeProfileProps {} | ||||
|  | ||||
| export const CurriculumVitaeProfile: React.FC< | ||||
|   CurriculumVitaeProfileProps | ||||
| > = () => { | ||||
|   const t = useTranslations() | ||||
|  | ||||
|   return ( | ||||
|     <div className="card profile-card"> | ||||
|       <div className="profile-pic-container"> | ||||
|         <div className="profile-pic"> | ||||
|           <Image | ||||
|             className="media-object img-circle center-block" | ||||
|             alt={t("meta.title")} | ||||
|             src="/images/logo_background.webp" | ||||
|             width={800} | ||||
|             height={800} | ||||
|           /> | ||||
|         </div> | ||||
|         <div className="name-and-profession text-center"> | ||||
|           <h3> | ||||
|             <strong>{t("meta.title")}</strong> | ||||
|           </h3> | ||||
|           <h5 className="text-muted">{t("curriculum-vitae.description")}</h5> | ||||
|           <h5 className="text-muted"> | ||||
|             <BirthDate /> | ||||
|           </h5> | ||||
|           <h5 className="text-muted">{t("home.about.nationality.value")}</h5> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div className="contact-details clearfix"> | ||||
|         <div className="detail"> | ||||
|           <span className="info"> | ||||
|             <a className="link-disguise" href="mailto:contact@theoludwig.fr"> | ||||
|               contact@theoludwig.fr | ||||
|             </a> | ||||
|           </span> | ||||
|         </div> | ||||
|         <div className="detail"> | ||||
|           <span className="info"> | ||||
|             <Link className="link-disguise" href="/"> | ||||
|               https://theoludwig.fr/ | ||||
|             </Link> | ||||
|           </span> | ||||
|         </div> | ||||
|       </div> | ||||
|       <hr /> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										23
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeSection.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeSection.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| export interface CurriculumVitaeSectionProps extends React.PropsWithChildren { | ||||
|   id: string | ||||
|   icon: React.ReactNode | ||||
|   title: string | ||||
| } | ||||
|  | ||||
| export const CurriculumVitaeSection: React.FC<CurriculumVitaeSectionProps> = ( | ||||
|   props, | ||||
| ) => { | ||||
|   const { id, icon, title, children } = props | ||||
|  | ||||
|   return ( | ||||
|     <section className="detail" id={id}> | ||||
|       <div className="icon">{icon}</div> | ||||
|  | ||||
|       <div className="info"> | ||||
|         <h4 className="title text-uppercase">{title}</h4> | ||||
|  | ||||
|         <div className="content">{children}</div> | ||||
|       </div> | ||||
|     </section> | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										21
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeSkills.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeSkills.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| import { useTranslations } from "next-intl" | ||||
| import { FaToolbox } from "react-icons/fa" | ||||
| import { CurriculumVitaeSection } from "./CurriculumVitaeSection" | ||||
|  | ||||
| export interface CurriculumVitaeSkillsProps {} | ||||
|  | ||||
| export const CurriculumVitaeSkills: React.FC< | ||||
|   CurriculumVitaeSkillsProps | ||||
| > = () => { | ||||
|   const t = useTranslations() | ||||
|  | ||||
|   return ( | ||||
|     <CurriculumVitaeSection | ||||
|       id="skills" | ||||
|       title={t("home.skills.title")} | ||||
|       icon={<FaToolbox size={24} />} | ||||
|     > | ||||
|       <p>Test</p> | ||||
|     </CurriculumVitaeSection> | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										19
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeWork.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								packages/ui/src/CurriculumVitae/CurriculumVitaeWork.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| import { useTranslations } from "next-intl" | ||||
| import { MdWork } from "react-icons/md" | ||||
| import { CurriculumVitaeSection } from "./CurriculumVitaeSection" | ||||
|  | ||||
| export interface CurriculumVitaeWorkProps {} | ||||
|  | ||||
| export const CurriculumVitaeWork: React.FC<CurriculumVitaeWorkProps> = () => { | ||||
|   const t = useTranslations() | ||||
|  | ||||
|   return ( | ||||
|     <CurriculumVitaeSection | ||||
|       id="work-experience" | ||||
|       title={t("curriculum-vitae.work.title")} | ||||
|       icon={<MdWork size={24} />} | ||||
|     > | ||||
|       <p>Test</p> | ||||
|     </CurriculumVitaeSection> | ||||
|   ) | ||||
| } | ||||
| @@ -1,6 +1,6 @@ | ||||
| import type { Meta, StoryObj } from "@storybook/react" | ||||
| import { expect, fn, userEvent, within } from "@storybook/test" | ||||
| import { FaCheck } from "react-icons/fa6" | ||||
| import { FaCheck } from "react-icons/fa" | ||||
|  | ||||
| import type { ButtonLinkProps } from "./Button" | ||||
| import { Button } from "./Button" | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| export interface AboutItemProps { | ||||
|   label: string | ||||
|   value: string | ||||
|   value: React.ReactNode | ||||
|   link?: string | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| "use client" | ||||
|  | ||||
| import { BIRTH_DATE } from "@repo/utils/constants" | ||||
| import { getAge, getISODate } from "@repo/utils/dates" | ||||
| import { useTranslations } from "next-intl" | ||||
| import { useMemo } from "react" | ||||
| import { AboutItem } from "./AboutItem" | ||||
|  | ||||
| export interface AboutItemBirthDateProps {} | ||||
|  | ||||
| export const AboutItemBirthDate: React.FC<AboutItemBirthDateProps> = () => { | ||||
|   const t = useTranslations() | ||||
|  | ||||
|   const age = useMemo(() => { | ||||
|     return getAge(BIRTH_DATE) | ||||
|   }, []) | ||||
|  | ||||
|   return ( | ||||
|     <AboutItem | ||||
|       label={t("home.about.birth-date.label")} | ||||
|       value={t("home.about.birth-date.value", { | ||||
|         age, | ||||
|         birthDate: getISODate(BIRTH_DATE), | ||||
|       })} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { useTranslations } from "next-intl" | ||||
| import { AboutItem } from "./AboutItem" | ||||
| import { AboutItemBirthDate } from "./AboutItemBirthDate" | ||||
| import { BirthDate } from "./BirthDate" | ||||
|  | ||||
| export interface AboutListProps {} | ||||
|  | ||||
| @@ -13,7 +13,10 @@ export const AboutList: React.FC<AboutListProps> = () => { | ||||
|         label={t("home.about.pronouns.label")} | ||||
|         value={t("home.about.pronouns.value")} | ||||
|       /> | ||||
|       <AboutItemBirthDate /> | ||||
|       <AboutItem | ||||
|         label={t("home.about.birth-date.label")} | ||||
|         value={<BirthDate />} | ||||
|       /> | ||||
|       <AboutItem | ||||
|         label={t("home.about.nationality.label")} | ||||
|         value={t("home.about.nationality.value")} | ||||
|   | ||||
							
								
								
									
										21
									
								
								packages/ui/src/Home/About/AboutList/BirthDate.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								packages/ui/src/Home/About/AboutList/BirthDate.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| "use client" | ||||
|  | ||||
| import { BIRTH_DATE, BIRTH_DATE_STRING } from "@repo/utils/constants" | ||||
| import { getAge } from "@repo/utils/dates" | ||||
| import { useTranslations } from "next-intl" | ||||
| import { useMemo } from "react" | ||||
|  | ||||
| export interface BirthDateProps {} | ||||
|  | ||||
| export const BirthDate: React.FC<BirthDateProps> = () => { | ||||
|   const t = useTranslations() | ||||
|  | ||||
|   const age = useMemo(() => { | ||||
|     return getAge(BIRTH_DATE) | ||||
|   }, []) | ||||
|  | ||||
|   return t("home.about.birth-date.value", { | ||||
|     age, | ||||
|     birthDate: BIRTH_DATE_STRING, | ||||
|   }) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user