mirror of
https://github.com/theoludwig/theoludwig.git
synced 2025-10-11 17:06:21 +02:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
338741d252
|
|||
be738a1c8d
|
|||
9c20844d89
|
|||
eceeca8b69
|
|||
92eca82127
|
|||
4502e32250
|
2
.github/workflows/chromatic.yml
vendored
2
.github/workflows/chromatic.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- uses: "pnpm/action-setup@v4.1.0"
|
- uses: "pnpm/action-setup@v4.1.0"
|
||||||
|
|
||||||
- name: "Setup Node.js"
|
- name: "Setup Node.js"
|
||||||
uses: "actions/setup-node@v4.4.0"
|
uses: "actions/setup-node@v5.0.0"
|
||||||
with:
|
with:
|
||||||
node-version: "24.x"
|
node-version: "24.x"
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
- uses: "pnpm/action-setup@v4.1.0"
|
- uses: "pnpm/action-setup@v4.1.0"
|
||||||
|
|
||||||
- name: "Setup Node.js"
|
- name: "Setup Node.js"
|
||||||
uses: "actions/setup-node@v4.4.0"
|
uses: "actions/setup-node@v5.0.0"
|
||||||
with:
|
with:
|
||||||
node-version: "24.x"
|
node-version: "24.x"
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
1
.vscode/react.code-snippets
vendored
1
.vscode/react.code-snippets
vendored
@@ -4,7 +4,6 @@
|
|||||||
"prefix": "rfc",
|
"prefix": "rfc",
|
||||||
"body": [
|
"body": [
|
||||||
"export interface ${1:ComponentName}Props {}",
|
"export interface ${1:ComponentName}Props {}",
|
||||||
"",
|
|
||||||
"export const ${1:ComponentName}: React.FC<${1:ComponentName}Props> = () => {",
|
"export const ${1:ComponentName}: React.FC<${1:ComponentName}Props> = () => {",
|
||||||
" return (",
|
" return (",
|
||||||
" <div>",
|
" <div>",
|
||||||
|
@@ -28,7 +28,7 @@ The commit message guidelines adheres to [Conventional Commits](https://www.conv
|
|||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- [Node.js](https://nodejs.org/) >= v24.0.0 [(`nvm install 24`)](https://nvm.sh)
|
- [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.18.2 [(`npm install --global corepack@0.34.0 && corepack enable`)](https://github.com/nodejs/corepack)
|
||||||
- [Docker](https://www.docker.com/)
|
- [Docker](https://www.docker.com/)
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import typescriptESLint from "typescript-eslint"
|
import typescriptESLint from "typescript-eslint"
|
||||||
|
import { defineConfig } from "eslint/config"
|
||||||
import config from "@repo/config-eslint"
|
import config from "@repo/config-eslint"
|
||||||
|
|
||||||
export default typescriptESLint.config(...config, {
|
export default defineConfig(...config, {
|
||||||
files: ["**/*.ts", "**/*.tsx"],
|
files: ["**/*.ts", "**/*.tsx"],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parser: typescriptESLint.parser,
|
parser: typescriptESLint.parser,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
FROM node:24.7.0-slim AS node-pnpm
|
FROM node:24.10.0-slim AS node-pnpm
|
||||||
ENV PNPM_HOME="/pnpm"
|
ENV PNPM_HOME="/pnpm"
|
||||||
ENV PATH="$PNPM_HOME:$PATH"
|
ENV PATH="$PNPM_HOME:$PATH"
|
||||||
RUN npm install --global corepack@0.34.0 && corepack enable
|
RUN npm install --global corepack@0.34.0 && corepack enable
|
||||||
@@ -9,7 +9,7 @@ WORKDIR /usr/src/app
|
|||||||
|
|
||||||
FROM node-pnpm AS builder
|
FROM node-pnpm AS builder
|
||||||
COPY ./ ./
|
COPY ./ ./
|
||||||
RUN pnpm install --global turbo@2.5.6
|
RUN pnpm install --global turbo@2.5.8
|
||||||
RUN turbo prune @repo/website --docker
|
RUN turbo prune @repo/website --docker
|
||||||
|
|
||||||
FROM node-pnpm AS installer
|
FROM node-pnpm AS installer
|
||||||
@@ -23,7 +23,7 @@ COPY --from=builder /usr/src/app/out/full/ ./
|
|||||||
COPY turbo.json turbo.json
|
COPY turbo.json turbo.json
|
||||||
|
|
||||||
ARG VERSION="0.0.0-develop"
|
ARG VERSION="0.0.0-develop"
|
||||||
RUN pnpm install --global replace-in-files-cli@3.0.0
|
RUN pnpm install --global replace-in-files-cli@4.0.0
|
||||||
RUN VERSION_STRIPPED=${VERSION#v} && replace-in-files --regex='version": *"[^"]*' --replacement='"version": "'"$VERSION_STRIPPED"'"' '**/package.json' '!**/node_modules/**'
|
RUN VERSION_STRIPPED=${VERSION#v} && replace-in-files --regex='version": *"[^"]*' --replacement='"version": "'"$VERSION_STRIPPED"'"' '**/package.json' '!**/node_modules/**'
|
||||||
RUN pnpm --filter=@repo/website... exec turbo run build
|
RUN pnpm --filter=@repo/website... exec turbo run build
|
||||||
|
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import typescriptESLint from "typescript-eslint"
|
import typescriptESLint from "typescript-eslint"
|
||||||
|
import { defineConfig } from "eslint/config"
|
||||||
import config from "@repo/config-eslint"
|
import config from "@repo/config-eslint"
|
||||||
|
|
||||||
export default typescriptESLint.config(...config, {
|
export default defineConfig(...config, {
|
||||||
files: ["**/*.ts", "**/*.tsx"],
|
files: ["**/*.ts", "**/*.tsx"],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parser: typescriptESLint.parser,
|
parser: typescriptESLint.parser,
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
"build": "next build --turbopack",
|
"build": "next build --turbopack",
|
||||||
"start": "next start --port 3000",
|
"start": "next start --port 3000",
|
||||||
"typegen": "next typegen",
|
"typegen": "next typegen",
|
||||||
"lint:eslint": "eslint . --max-warnings 0",
|
"lint:eslint": "eslint app --max-warnings 0",
|
||||||
"lint:typescript": "next typegen && tsc --noEmit"
|
"lint:typescript": "next typegen && tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 945 KiB After Width: | Height: | Size: 1.2 MiB |
@@ -4,9 +4,6 @@
|
|||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
"types": ["@total-typescript/ts-reset", "@repo/i18n/messages.d.ts"],
|
"types": ["@total-typescript/ts-reset", "@repo/i18n/messages.d.ts"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"paths": {
|
|
||||||
"#*": ["./*"]
|
|
||||||
},
|
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "next"
|
"name": "next"
|
||||||
|
@@ -2,42 +2,32 @@ import nextPlugin from "@next/eslint-plugin-next"
|
|||||||
import configConventions from "eslint-config-conventions"
|
import configConventions from "eslint-config-conventions"
|
||||||
import importXPlugin from "eslint-plugin-import-x"
|
import importXPlugin from "eslint-plugin-import-x"
|
||||||
import reactPlugin from "eslint-plugin-react"
|
import reactPlugin from "eslint-plugin-react"
|
||||||
import reactHooksPlugin from "eslint-plugin-react-hooks"
|
import reactHooks from "eslint-plugin-react-hooks"
|
||||||
import typescriptESLint from "typescript-eslint"
|
import { defineConfig, globalIgnores } from "eslint/config"
|
||||||
|
|
||||||
export default typescriptESLint.config(
|
export default defineConfig(
|
||||||
...configConventions,
|
...configConventions,
|
||||||
reactHooksPlugin.configs.recommended,
|
|
||||||
reactPlugin.configs.flat.recommended,
|
reactPlugin.configs.flat.recommended,
|
||||||
{
|
globalIgnores(["**/kysely.config.ts"]),
|
||||||
ignores: [
|
|
||||||
".next",
|
|
||||||
"**/next.config.ts",
|
|
||||||
"**/eslint.config.js",
|
|
||||||
"**/tailwind.config.js",
|
|
||||||
"**/postcss.config.js",
|
|
||||||
"**/kysely.config.ts",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "config-eslint",
|
name: "config-eslint",
|
||||||
settings: {
|
settings: {
|
||||||
react: {
|
react: {
|
||||||
version: "19.1.1",
|
version: "19.2.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
"@next/next": nextPlugin,
|
"@next/next": nextPlugin,
|
||||||
"import-x": importXPlugin,
|
"import-x": importXPlugin,
|
||||||
|
"react-hooks": reactHooks,
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
...nextPlugin.configs.recommended.rules,
|
...nextPlugin.configs.recommended.rules,
|
||||||
...nextPlugin.configs["core-web-vitals"].rules,
|
...nextPlugin.configs["core-web-vitals"].rules,
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
"@next/next/no-html-link-for-pages": "off",
|
"@next/next/no-html-link-for-pages": "off",
|
||||||
"@next/next/no-img-element": "off",
|
"@next/next/no-img-element": "off",
|
||||||
|
|
||||||
"react-hooks/react-compiler": "error",
|
|
||||||
|
|
||||||
"react/jsx-no-target-blank": "off",
|
"react/jsx-no-target-blank": "off",
|
||||||
"react/no-unknown-property": "off",
|
"react/no-unknown-property": "off",
|
||||||
"react/react-in-jsx-scope": "off",
|
"react/react-in-jsx-scope": "off",
|
||||||
|
8
configs/config-eslint/index.d.ts
vendored
8
configs/config-eslint/index.d.ts
vendored
@@ -1,7 +1,5 @@
|
|||||||
import type typescriptESLint from "typescript-eslint"
|
import type { defineConfig } from "eslint/config"
|
||||||
|
|
||||||
declare const eslintConfigConventions: ReturnType<
|
declare const eslintConfig: ReturnType<typeof defineConfig>
|
||||||
typeof typescriptESLint.config
|
|
||||||
>
|
|
||||||
|
|
||||||
export default eslintConfigConventions
|
export default eslintConfig
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import typescriptESLint from "typescript-eslint"
|
import typescriptESLint from "typescript-eslint"
|
||||||
|
import { defineConfig } from "eslint/config"
|
||||||
import config from "@repo/config-eslint"
|
import config from "@repo/config-eslint"
|
||||||
|
|
||||||
export default typescriptESLint.config(...config, {
|
export default defineConfig(...config, {
|
||||||
files: ["**/*.ts", "**/*.tsx"],
|
files: ["**/*.ts", "**/*.tsx"],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parser: typescriptESLint.parser,
|
parser: typescriptESLint.parser,
|
||||||
|
@@ -74,6 +74,10 @@
|
|||||||
@apply font-semibold italic;
|
@apply font-semibold italic;
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 87%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@@ -250,8 +254,8 @@ code .line::before {
|
|||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
.profile-pic img {
|
.profile-pic img {
|
||||||
width: 80px;
|
width: 100px;
|
||||||
height: 80px;
|
height: 100px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
|
"allowArbitraryExtensions": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"incremental": false,
|
"incremental": false,
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
"version": "0.0.0-develop",
|
"version": "0.0.0-develop",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b",
|
"packageManager": "pnpm@10.18.2+sha512.9fb969fa749b3ade6035e0f109f0b8a60b5d08a1a87fdf72e337da90dcc93336e2280ca4e44f2358a649b83c17959e9993e777c2080879f3801e6f0d999ad3dd",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=24.0.0"
|
"node": ">=24.0.0"
|
||||||
},
|
},
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import typescriptESLint from "typescript-eslint"
|
import typescriptESLint from "typescript-eslint"
|
||||||
|
import { defineConfig } from "eslint/config"
|
||||||
import config from "@repo/config-eslint"
|
import config from "@repo/config-eslint"
|
||||||
|
|
||||||
export default typescriptESLint.config(...config, {
|
export default defineConfig(...config, {
|
||||||
files: ["**/*.ts", "**/*.tsx"],
|
files: ["**/*.ts", "**/*.tsx"],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parser: typescriptESLint.parser,
|
parser: typescriptESLint.parser,
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
"@repo/utils": "workspace:*",
|
"@repo/utils": "workspace:*",
|
||||||
"@repo/i18n": "workspace:*",
|
"@repo/i18n": "workspace:*",
|
||||||
"@repo/ui": "workspace:*",
|
"@repo/ui": "workspace:*",
|
||||||
"@repo/react-hooks": "workspace:*",
|
|
||||||
"@giscus/react": "catalog:",
|
"@giscus/react": "catalog:",
|
||||||
"@shikijs/rehype": "catalog:",
|
"@shikijs/rehype": "catalog:",
|
||||||
"@mdx-js/mdx": "catalog:",
|
"@mdx-js/mdx": "catalog:",
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import typescriptESLint from "typescript-eslint"
|
import typescriptESLint from "typescript-eslint"
|
||||||
|
import { defineConfig } from "eslint/config"
|
||||||
import config from "@repo/config-eslint"
|
import config from "@repo/config-eslint"
|
||||||
|
|
||||||
export default typescriptESLint.config(...config, {
|
export default defineConfig(...config, {
|
||||||
files: ["**/*.ts", "**/*.tsx"],
|
files: ["**/*.ts", "**/*.tsx"],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parser: typescriptESLint.parser,
|
parser: typescriptESLint.parser,
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
"en-US": "English",
|
"en-US": "English",
|
||||||
"fr-FR": "French"
|
"fr-FR": "French"
|
||||||
},
|
},
|
||||||
|
"fr-FR-main": "French (primary language)",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"errors": {
|
"errors": {
|
||||||
"error": "Error",
|
"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.",
|
"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"
|
"title": "About"
|
||||||
},
|
},
|
||||||
"description": "Developer Full Stack • Student",
|
"description": "Developer Full Stack • French Student",
|
||||||
"education": {
|
"education": {
|
||||||
"cnam": {
|
"cnam": {
|
||||||
"institution": "Conservatoire National des Arts et Métiers (CNAM), in Eckbolsheim - UIMM Alsace - ITII Alsace",
|
"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",
|
"institution": "IUT Robert Schuman in Illkirch-Graffenstaden",
|
||||||
"study-type": "University Bachelor of Technology (BUT) Computer Science",
|
"study-type": "University Bachelor of Technology (BUT) Computer Science",
|
||||||
"years": {
|
"years": {
|
||||||
|
"title": "2021 - 2024",
|
||||||
"2021-2022": {
|
"2021-2022": {
|
||||||
"courses": {
|
"courses": {
|
||||||
"java": "Object Oriented Development in Java",
|
"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)",
|
"systems-c": "Systems programming in C (Memory allocation, Pointers, Structures)",
|
||||||
"windows-forms": "Windows Forms (.NET Framework) Application Development in C#"
|
"windows-forms": "Windows Forms (.NET Framework) Application Development in C#"
|
||||||
},
|
},
|
||||||
@@ -53,10 +55,10 @@
|
|||||||
},
|
},
|
||||||
"2022-2023": {
|
"2022-2023": {
|
||||||
"courses": {
|
"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",
|
"sql-security": "Securing database access and PL/SQL",
|
||||||
"systems-c": "Systems programming in C (Multi-Thread, Server/Client UDP/TCP)",
|
"systems-c": "Systems programming in <strong>C (Multi-Thread, Server/Client UDP/TCP)</strong>",
|
||||||
"tests": "Development Quality and Automated Testing",
|
"tests": "<strong>Development Quality and Automated Testing</strong>",
|
||||||
"web": "Web development with the Laravel framework in PHP"
|
"web": "Web development with the Laravel framework in PHP"
|
||||||
},
|
},
|
||||||
"description": "2nd year",
|
"description": "2nd year",
|
||||||
@@ -67,7 +69,7 @@
|
|||||||
"ci-cd": "Continuous Integration/Deployment (CI/CD) and Docker",
|
"ci-cd": "Continuous Integration/Deployment (CI/CD) and Docker",
|
||||||
"complexity-algorithms": "Theoretical and Practical Algorithmic Complexity in C++",
|
"complexity-algorithms": "Theoretical and Practical Algorithmic Complexity in C++",
|
||||||
"no-sql": "NoSQL database (Redis, MongoDB, Cassandra)",
|
"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",
|
"description": "3rd year",
|
||||||
"title": "2023 - 2024"
|
"title": "2023 - 2024"
|
||||||
@@ -89,7 +91,7 @@
|
|||||||
"interests": {
|
"interests": {
|
||||||
"title": "Interests & hobbies",
|
"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>.",
|
"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": "Open-Source Enthusiast"
|
"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": {
|
"work": {
|
||||||
"ircad": {
|
"ircad": {
|
||||||
@@ -97,18 +99,18 @@
|
|||||||
"position": "Full Stack Web Developer Apprentice",
|
"position": "Full Stack Web Developer Apprentice",
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"WebSurg": "Development of WebSurg, a virtual university dedicated to medical-surgical training, built with React.js/Next.js and API Platform with Symfony.",
|
"WebSurg": "Development of WebSurg, a virtual university dedicated to medical-surgical training, built with React.js/Next.js and API Platform with Symfony.",
|
||||||
"Figma": "Integration of Figma mockups for WebSurg, website dedicated to medical-surgical training.",
|
"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 (anatomicals models, medications, staff training, etc.).",
|
"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-logs": "History and traceability of data modifications (what? who? when?) in IRCAD Core.",
|
||||||
"feature-permissions": "Advanced permissions system and OAuth2 authentication, with read, write, and delete access restricted for specific users of the IRCAD Core application.",
|
"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": "Search engine with filters, sorting, and customizable display for each user of IRCAD Core.",
|
"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 TypeScript Monorepo with Turborepo, and automatic deployment (CI/CD) with Docker Compose, self-hosted internally."
|
"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": {
|
"numerize": {
|
||||||
"duration": "3 months",
|
"duration": "3 months",
|
||||||
"position": "Full Stack Web Developer Intern",
|
"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"
|
"title": "Work experiences"
|
||||||
}
|
}
|
||||||
@@ -174,7 +176,10 @@
|
|||||||
"others": "Others",
|
"others": "Others",
|
||||||
"programming-languages": "Programming languages",
|
"programming-languages": "Programming languages",
|
||||||
"software-tools": "Software and tools",
|
"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",
|
"en-US": "Anglais",
|
||||||
"fr-FR": "Français"
|
"fr-FR": "Français"
|
||||||
},
|
},
|
||||||
|
"fr-FR-main": "Français (langue principale)",
|
||||||
"loading": "Chargement...",
|
"loading": "Chargement...",
|
||||||
"errors": {
|
"errors": {
|
||||||
"error": "Erreur",
|
"error": "Erreur",
|
||||||
@@ -41,10 +42,11 @@
|
|||||||
"institution": "IUT Robert Schuman à Illkirch-Graffenstaden",
|
"institution": "IUT Robert Schuman à Illkirch-Graffenstaden",
|
||||||
"study-type": "Bachelor Universitaire de Technologie (BUT) Informatique",
|
"study-type": "Bachelor Universitaire de Technologie (BUT) Informatique",
|
||||||
"years": {
|
"years": {
|
||||||
|
"title": "2021 - 2024",
|
||||||
"2021-2022": {
|
"2021-2022": {
|
||||||
"courses": {
|
"courses": {
|
||||||
"java": "Développement Orientée Objet en Java",
|
"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)",
|
"systems-c": "Programmation systèmes en C (Allocation mémoire, Pointeurs, Structures)",
|
||||||
"windows-forms": "Développement d'application Windows Forms (.NET Framework) en C#"
|
"windows-forms": "Développement d'application Windows Forms (.NET Framework) en C#"
|
||||||
},
|
},
|
||||||
@@ -53,10 +55,10 @@
|
|||||||
},
|
},
|
||||||
"2022-2023": {
|
"2022-2023": {
|
||||||
"courses": {
|
"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",
|
"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)",
|
"systems-c": "Programmation systèmes en <strong>C (Multi-Thread, Serveur/Client UDP/TCP)</strong>",
|
||||||
"tests": "Qualité de développement et Tests automatisés",
|
"tests": "<strong>Qualité de développement et Tests automatisés</strong>",
|
||||||
"web": "Développement Web avec le framework Laravel en PHP"
|
"web": "Développement Web avec le framework Laravel en PHP"
|
||||||
},
|
},
|
||||||
"description": "2ème année",
|
"description": "2ème année",
|
||||||
@@ -67,7 +69,7 @@
|
|||||||
"ci-cd": "Intégration/Déploiement Continue et Docker",
|
"ci-cd": "Intégration/Déploiement Continue et Docker",
|
||||||
"complexity-algorithms": "Complexité Algorithmique Théorique et Pratique en C++",
|
"complexity-algorithms": "Complexité Algorithmique Théorique et Pratique en C++",
|
||||||
"no-sql": "Base de données NoSQL (Redis, MongoDB, Cassandra)",
|
"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",
|
"description": "3ème année",
|
||||||
"title": "2023 - 2024"
|
"title": "2023 - 2024"
|
||||||
@@ -89,7 +91,7 @@
|
|||||||
"interests": {
|
"interests": {
|
||||||
"title": "Intérêts & loisirs",
|
"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>.",
|
"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": "Enthousiaste de l'Open-Source"
|
"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": {
|
"work": {
|
||||||
"ircad": {
|
"ircad": {
|
||||||
@@ -97,12 +99,12 @@
|
|||||||
"position": "Apprenti Développeur Web Full Stack",
|
"position": "Apprenti Développeur Web Full Stack",
|
||||||
"tasks": {
|
"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.",
|
"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": "Intégration des maquettes Figma pour WebSurg, site web consacrée à la formation médico-chirurgicale.",
|
"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.).",
|
"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-logs": "Historique et traçabilité des modifications des données (quoi? qui? quand?) dans IRCAD Core.",
|
||||||
"feature-permissions": "Système de permissions avancé et authentification OAuth2, avec accès en lecture, écriture et suppression restreint pour des utilisateurs spécifiques de l'application 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": "Moteur de recherche avec filtres, tris et ordre d'affichage personnalisable pour 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 Monorepo TypeScript avec Turborepo, et déploiement automatique (CI/CD) avec Docker Compose, auto-hébergé en interne."
|
"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": {
|
"numerize": {
|
||||||
@@ -174,7 +176,10 @@
|
|||||||
"others": "Autres",
|
"others": "Autres",
|
||||||
"programming-languages": "Langages de programmation",
|
"programming-languages": "Langages de programmation",
|
||||||
"software-tools": "Logiciels et outils",
|
"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)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +0,0 @@
|
|||||||
import typescriptESLint from "typescript-eslint"
|
|
||||||
import config from "@repo/config-eslint"
|
|
||||||
|
|
||||||
export default typescriptESLint.config(...config, {
|
|
||||||
files: ["**/*.ts", "**/*.tsx"],
|
|
||||||
languageOptions: {
|
|
||||||
parser: typescriptESLint.parser,
|
|
||||||
parserOptions: {
|
|
||||||
projectService: true,
|
|
||||||
tsconfigRootDir: import.meta.dirname,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@repo/react-hooks",
|
|
||||||
"version": "0.0.0-develop",
|
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
|
||||||
"./useBoolean": "./src/useBoolean.ts",
|
|
||||||
"./useIsMounted": "./src/useIsMounted.ts"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"lint:eslint": "eslint src --max-warnings 0",
|
|
||||||
"lint:typescript": "tsc --noEmit"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"react": "catalog:",
|
|
||||||
"react-dom": "catalog:"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@repo/config-eslint": "workspace:*",
|
|
||||||
"@repo/config-typescript": "workspace:*",
|
|
||||||
"@types/react": "catalog:",
|
|
||||||
"@types/react-dom": "catalog:",
|
|
||||||
"@total-typescript/ts-reset": "catalog:",
|
|
||||||
"eslint": "catalog:",
|
|
||||||
"playwright": "catalog:",
|
|
||||||
"typescript": "catalog:",
|
|
||||||
"typescript-eslint": "catalog:"
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,15 +0,0 @@
|
|||||||
import { useEffect, useState } from "react"
|
|
||||||
|
|
||||||
export interface UseIsMountedOutput {
|
|
||||||
isMounted: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useIsMounted = (): UseIsMountedOutput => {
|
|
||||||
const [isMounted, setIsMounted] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsMounted(true)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return { isMounted }
|
|
||||||
}
|
|
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@repo/config-typescript/tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
||||||
"types": ["@total-typescript/ts-reset"]
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,7 +1,8 @@
|
|||||||
import typescriptESLint from "typescript-eslint"
|
import typescriptESLint from "typescript-eslint"
|
||||||
|
import { defineConfig } from "eslint/config"
|
||||||
import config from "@repo/config-eslint"
|
import config from "@repo/config-eslint"
|
||||||
|
|
||||||
export default typescriptESLint.config(...config, {
|
export default defineConfig(...config, {
|
||||||
files: ["**/*.ts", "**/*.tsx"],
|
files: ["**/*.ts", "**/*.tsx"],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parser: typescriptESLint.parser,
|
parser: typescriptESLint.parser,
|
||||||
|
@@ -30,7 +30,6 @@
|
|||||||
"@repo/config-tailwind": "workspace:*",
|
"@repo/config-tailwind": "workspace:*",
|
||||||
"@repo/utils": "workspace:*",
|
"@repo/utils": "workspace:*",
|
||||||
"@repo/i18n": "workspace:*",
|
"@repo/i18n": "workspace:*",
|
||||||
"@repo/react-hooks": "workspace:*",
|
|
||||||
"cva": "catalog:",
|
"cva": "catalog:",
|
||||||
"next": "catalog:",
|
"next": "catalog:",
|
||||||
"next-intl": "catalog:",
|
"next-intl": "catalog:",
|
||||||
|
@@ -14,50 +14,86 @@ export const CurriculumVitaeEducation: React.FC<
|
|||||||
years: t("curriculum-vitae.education.cnam.years.2024-2027.title"),
|
years: t("curriculum-vitae.education.cnam.years.2024-2027.title"),
|
||||||
studyType: t("curriculum-vitae.education.cnam.study-type"),
|
studyType: t("curriculum-vitae.education.cnam.study-type"),
|
||||||
institution: t("curriculum-vitae.education.cnam.institution"),
|
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: [],
|
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"),
|
studyType: t("curriculum-vitae.education.iut.study-type"),
|
||||||
institution: t("curriculum-vitae.education.iut.institution"),
|
institution: t("curriculum-vitae.education.iut.institution"),
|
||||||
score: t("curriculum-vitae.education.iut.years.2023-2024.description"),
|
|
||||||
courses: [
|
courses: [
|
||||||
t("curriculum-vitae.education.iut.years.2023-2024.courses.web"),
|
{
|
||||||
t("curriculum-vitae.education.iut.years.2023-2024.courses.ci-cd"),
|
year: t("curriculum-vitae.education.iut.years.2021-2022.description"),
|
||||||
t(
|
title: t("curriculum-vitae.education.iut.years.2021-2022.title"),
|
||||||
"curriculum-vitae.education.iut.years.2023-2024.courses.complexity-algorithms",
|
courses: [
|
||||||
),
|
t("curriculum-vitae.education.iut.years.2021-2022.courses.java"),
|
||||||
t("curriculum-vitae.education.iut.years.2023-2024.courses.no-sql"),
|
t(
|
||||||
],
|
"curriculum-vitae.education.iut.years.2021-2022.courses.systems-c",
|
||||||
},
|
),
|
||||||
{
|
// t(
|
||||||
years: t("curriculum-vitae.education.iut.years.2022-2023.title"),
|
// "curriculum-vitae.education.iut.years.2021-2022.courses.windows-forms",
|
||||||
studyType: t("curriculum-vitae.education.iut.study-type"),
|
// ),
|
||||||
institution: t("curriculum-vitae.education.iut.institution"),
|
t.rich(
|
||||||
score: t("curriculum-vitae.education.iut.years.2022-2023.description"),
|
"curriculum-vitae.education.iut.years.2021-2022.courses.sql",
|
||||||
courses: [
|
{
|
||||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.web"),
|
strong: (children) => {
|
||||||
t("curriculum-vitae.education.iut.years.2022-2023.courses.tests"),
|
return <strong>{children}</strong>
|
||||||
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",
|
],
|
||||||
// ),
|
},
|
||||||
],
|
{
|
||||||
},
|
year: t("curriculum-vitae.education.iut.years.2022-2023.description"),
|
||||||
{
|
title: t("curriculum-vitae.education.iut.years.2022-2023.title"),
|
||||||
years: t("curriculum-vitae.education.iut.years.2021-2022.title"),
|
courses: [
|
||||||
studyType: t("curriculum-vitae.education.iut.study-type"),
|
t("curriculum-vitae.education.iut.years.2022-2023.courses.web"),
|
||||||
institution: t("curriculum-vitae.education.iut.institution"),
|
t.rich(
|
||||||
score: t("curriculum-vitae.education.iut.years.2021-2022.description"),
|
"curriculum-vitae.education.iut.years.2022-2023.courses.tests",
|
||||||
courses: [
|
{
|
||||||
t("curriculum-vitae.education.iut.years.2021-2022.courses.java"),
|
strong: (children) => {
|
||||||
t("curriculum-vitae.education.iut.years.2021-2022.courses.systems-c"),
|
return <strong>{children}</strong>
|
||||||
// t(
|
},
|
||||||
// "curriculum-vitae.education.iut.years.2021-2022.courses.windows-forms",
|
},
|
||||||
// ),
|
),
|
||||||
t("curriculum-vitae.education.iut.years.2021-2022.courses.sql"),
|
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",
|
||||||
|
),
|
||||||
|
t("curriculum-vitae.education.iut.years.2023-2024.courses.no-sql"),
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
@@ -83,9 +119,13 @@ export const CurriculumVitaeEducation: React.FC<
|
|||||||
<strong>{education.studyType}</strong>
|
<strong>{education.studyType}</strong>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="relative m-0">
|
{/* {education.score != null ? (
|
||||||
<strong>{education.score}</strong>
|
<p className="relative m-0">
|
||||||
</p>
|
<strong>{education.score}</strong>
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)} */}
|
||||||
|
|
||||||
<p className="text-muted m-0">{education.institution}</p>
|
<p className="text-muted m-0">{education.institution}</p>
|
||||||
|
|
||||||
@@ -94,13 +134,23 @@ export const CurriculumVitaeEducation: React.FC<
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
{education.courses.length > 0 ? (
|
{education.courses.length > 0 ? (
|
||||||
<ul
|
<ul className="list-none">
|
||||||
style={{
|
{education.courses.map(({ year, title, courses }) => {
|
||||||
paddingInlineStart: 20,
|
return (
|
||||||
}}
|
<li key={year}>
|
||||||
>
|
<span className="font-medium">{year}</span>
|
||||||
{education.courses.map((course) => {
|
<span> ({title})</span>
|
||||||
return <li key={course}>{course}</li>
|
<ul
|
||||||
|
style={{
|
||||||
|
paddingInlineStart: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{courses.map((course, index) => {
|
||||||
|
return <li key={index}>{course}</li>
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
|
@@ -10,9 +10,44 @@ export const CurriculumVitaeInterests: React.FC<
|
|||||||
const t = useTranslations()
|
const t = useTranslations()
|
||||||
|
|
||||||
const interests = [
|
const interests = [
|
||||||
<strong key="open-source">
|
t.rich("curriculum-vitae.interests.open-source", {
|
||||||
{t("curriculum-vitae.interests.open-source")}
|
strong: (children) => {
|
||||||
</strong>,
|
return <strong>{children}</strong>
|
||||||
|
},
|
||||||
|
"link-github": (children) => {
|
||||||
|
return (
|
||||||
|
<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", {
|
t.rich("curriculum-vitae.interests.fusey", {
|
||||||
link: (children) => {
|
link: (children) => {
|
||||||
return (
|
return (
|
||||||
|
@@ -21,16 +21,18 @@ export const CurriculumVitaeProfile: React.FC<
|
|||||||
<Image
|
<Image
|
||||||
className="mx-auto block"
|
className="mx-auto block"
|
||||||
alt={t("meta.title")}
|
alt={t("meta.title")}
|
||||||
src="/images/logo_background.webp"
|
src="/images/logo.webp"
|
||||||
width={400}
|
width={450}
|
||||||
height={400}
|
height={450}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="name-and-profession text-center">
|
<div className="name-and-profession text-center">
|
||||||
<h1 className="h3 my-1!">
|
<h1 className="h3 my-1!">
|
||||||
<strong>{t("meta.title")}</strong>
|
<strong>{t("meta.title")}</strong>
|
||||||
</h1>
|
</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">
|
<h2 className="text-muted h5">
|
||||||
<BirthDate />
|
<BirthDate />
|
||||||
</h2>
|
</h2>
|
||||||
@@ -39,17 +41,13 @@ export const CurriculumVitaeProfile: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<div className="relative px-3">
|
<div className="relative px-3">
|
||||||
<strong className="info">
|
<strong className="info font-bold">
|
||||||
<a className="" href="mailto:contact@theoludwig.fr">
|
<a href="mailto:contact@theoludwig.fr">contact@theoludwig.fr</a>
|
||||||
contact@theoludwig.fr
|
|
||||||
</a>
|
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
<div className="detail">
|
<div className="detail">
|
||||||
<strong className="info">
|
<strong className="info font-bold">
|
||||||
<Link className="" href="/">
|
<Link href="/">https://theoludwig.fr</Link>
|
||||||
https://theoludwig.fr
|
|
||||||
</Link>
|
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -23,7 +23,11 @@ export const CurriculumVitaeSkills: React.FC<
|
|||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
category: "others",
|
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
|
] as const
|
||||||
|
|
||||||
@@ -45,6 +49,23 @@ export const CurriculumVitaeSkills: React.FC<
|
|||||||
return (
|
return (
|
||||||
<p key={skillName} className="label label-keyword">
|
<p key={skillName} className="label label-keyword">
|
||||||
{skillName}
|
{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>
|
</p>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
@@ -17,12 +17,28 @@ export const CurriculumVitaeWork: React.FC<CurriculumVitaeWorkProps> = () => {
|
|||||||
duration: t("curriculum-vitae.work.ircad.duration"),
|
duration: t("curriculum-vitae.work.ircad.duration"),
|
||||||
tasks: [
|
tasks: [
|
||||||
// t("curriculum-vitae.work.ircad.tasks.WebSurg"),
|
// t("curriculum-vitae.work.ircad.tasks.WebSurg"),
|
||||||
t("curriculum-vitae.work.ircad.tasks.Figma"),
|
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.IRCAD-Core"),
|
||||||
t("curriculum-vitae.work.ircad.tasks.feature-logs"),
|
t("curriculum-vitae.work.ircad.tasks.feature-logs"),
|
||||||
t("curriculum-vitae.work.ircad.tasks.feature-permissions"),
|
t.rich("curriculum-vitae.work.ircad.tasks.feature-permissions", {
|
||||||
t("curriculum-vitae.work.ircad.tasks.feature-search"),
|
strong: (children) => {
|
||||||
t("curriculum-vitae.work.ircad.tasks.feature-architecture"),
|
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>
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -74,8 +90,8 @@ export const CurriculumVitaeWork: React.FC<CurriculumVitaeWorkProps> = () => {
|
|||||||
}}
|
}}
|
||||||
className="space-y-1"
|
className="space-y-1"
|
||||||
>
|
>
|
||||||
{workExperience.tasks.map((task) => {
|
{workExperience.tasks.map((task, index) => {
|
||||||
return <li key={task}>{task}</li>
|
return <li key={index}>{task}</li>
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
|
@@ -6,9 +6,9 @@ import { LOCALES } from "@repo/utils/constants"
|
|||||||
import { useLocale } from "next-intl"
|
import { useLocale } from "next-intl"
|
||||||
import { useEffect, useRef } from "react"
|
import { useEffect, useRef } from "react"
|
||||||
|
|
||||||
import { useBoolean } from "@repo/react-hooks/useBoolean"
|
|
||||||
import { Arrow } from "./Arrow.tsx"
|
import { Arrow } from "./Arrow.tsx"
|
||||||
import { LocaleFlag } from "./LocaleFlag.tsx"
|
import { LocaleFlag } from "./LocaleFlag.tsx"
|
||||||
|
import { useBoolean } from "../../../hooks/useBoolean.ts"
|
||||||
|
|
||||||
export interface LocalesProps {}
|
export interface LocalesProps {}
|
||||||
|
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { classNames } from "@repo/config-tailwind/classNames"
|
import { classNames } from "@repo/config-tailwind/classNames"
|
||||||
import { useIsMounted } from "@repo/react-hooks/useIsMounted"
|
|
||||||
import type { Theme } from "@repo/utils/constants"
|
import type { Theme } from "@repo/utils/constants"
|
||||||
import { THEME_DEFAULT } from "@repo/utils/constants"
|
import { THEME_DEFAULT } from "@repo/utils/constants"
|
||||||
import {
|
import {
|
||||||
ThemeProvider as NextThemeProvider,
|
ThemeProvider as NextThemeProvider,
|
||||||
useTheme as useNextTheme,
|
useTheme as useNextTheme,
|
||||||
} from "next-themes"
|
} from "next-themes"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
export interface ThemeProviderProps extends React.PropsWithChildren {
|
export interface ThemeProviderProps extends React.PropsWithChildren {
|
||||||
forcedTheme?: Theme
|
forcedTheme?: Theme
|
||||||
@@ -35,7 +35,12 @@ export interface UseThemeOutput {
|
|||||||
|
|
||||||
export const useTheme = (): UseThemeOutput => {
|
export const useTheme = (): UseThemeOutput => {
|
||||||
const { setTheme, theme: themeData } = useNextTheme()
|
const { setTheme, theme: themeData } = useNextTheme()
|
||||||
const { isMounted } = useIsMounted()
|
const [isMounted, setIsMounted] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
|
setIsMounted(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
const theme = isMounted ? (themeData as Theme) : THEME_DEFAULT
|
const theme = isMounted ? (themeData as Theme) : THEME_DEFAULT
|
||||||
|
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import typescriptESLint from "typescript-eslint"
|
import typescriptESLint from "typescript-eslint"
|
||||||
|
import { defineConfig } from "eslint/config"
|
||||||
import config from "@repo/config-eslint"
|
import config from "@repo/config-eslint"
|
||||||
|
|
||||||
export default typescriptESLint.config(...config, {
|
export default defineConfig(...config, {
|
||||||
files: ["**/*.ts", "**/*.tsx"],
|
files: ["**/*.ts", "**/*.tsx"],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parser: typescriptESLint.parser,
|
parser: typescriptESLint.parser,
|
||||||
|
3834
pnpm-lock.yaml
generated
3834
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -7,30 +7,30 @@ catalogMode: "strict"
|
|||||||
|
|
||||||
catalog:
|
catalog:
|
||||||
# Turborepo
|
# Turborepo
|
||||||
"turbo": "2.5.6"
|
"turbo": "2.5.8"
|
||||||
|
|
||||||
# TypeScript
|
# TypeScript
|
||||||
"typescript": "5.9.2"
|
"typescript": "5.9.3"
|
||||||
"@total-typescript/ts-reset": "0.6.1"
|
"@total-typescript/ts-reset": "0.6.1"
|
||||||
"@types/node": "24.3.0"
|
"@types/node": "24.7.2"
|
||||||
|
|
||||||
# Utils
|
# Utils
|
||||||
"mime": "4.0.7"
|
"mime": "4.1.0"
|
||||||
|
|
||||||
# React.js/Next.js
|
# React.js/Next.js
|
||||||
"next": &next "15.5.2"
|
"next": &next "15.5.4"
|
||||||
"next-intl": "4.3.5"
|
"next-intl": "4.3.12"
|
||||||
"next-themes": "0.4.6"
|
"next-themes": "0.4.6"
|
||||||
"react": "19.1.1"
|
"react": "19.2.0"
|
||||||
"react-dom": "19.1.1"
|
"react-dom": "19.2.0"
|
||||||
"@types/react": "19.1.12"
|
"@types/react": "19.2.2"
|
||||||
"@types/react-dom": "19.1.9"
|
"@types/react-dom": "19.2.1"
|
||||||
"react-icons": "5.5.0"
|
"react-icons": "5.5.0"
|
||||||
|
|
||||||
# Blog
|
# Blog
|
||||||
"@giscus/react": "3.1.0"
|
"@giscus/react": "3.1.0"
|
||||||
"gray-matter": "4.0.3"
|
"gray-matter": "4.0.3"
|
||||||
"katex": "0.16.22"
|
"katex": "0.16.23"
|
||||||
"next-mdx-remote": "5.0.0"
|
"next-mdx-remote": "5.0.0"
|
||||||
"@mdx-js/mdx": "3.1.1"
|
"@mdx-js/mdx": "3.1.1"
|
||||||
"rehype-katex": "7.0.1"
|
"rehype-katex": "7.0.1"
|
||||||
@@ -47,16 +47,16 @@ catalog:
|
|||||||
"markdownlint-rule-relative-links": "4.2.0"
|
"markdownlint-rule-relative-links": "4.2.0"
|
||||||
|
|
||||||
# ESLint
|
# ESLint
|
||||||
"globals": "16.3.0"
|
"globals": "16.4.0"
|
||||||
"typescript-eslint": "8.41.0"
|
"typescript-eslint": "8.46.0"
|
||||||
"eslint": "9.34.0"
|
"eslint": "9.37.0"
|
||||||
"eslint-config-conventions": "20.1.3"
|
"eslint-config-conventions": "21.0.3"
|
||||||
"eslint-plugin-promise": "7.2.1"
|
"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"
|
"eslint-plugin-import-x": "4.16.1"
|
||||||
"@next/eslint-plugin-next": *next
|
"@next/eslint-plugin-next": *next
|
||||||
"eslint-plugin-react": "7.37.5"
|
"eslint-plugin-react": "7.37.5"
|
||||||
"eslint-plugin-react-hooks": "6.0.0-rc.1"
|
"eslint-plugin-react-hooks": "7.0.0"
|
||||||
|
|
||||||
# Prettier
|
# Prettier
|
||||||
"prettier": "3.6.2"
|
"prettier": "3.6.2"
|
||||||
@@ -64,29 +64,29 @@ catalog:
|
|||||||
"editorconfig-checker": "6.1.0"
|
"editorconfig-checker": "6.1.0"
|
||||||
|
|
||||||
# Storybook
|
# Storybook
|
||||||
"storybook": &storybook "9.1.3"
|
"storybook": &storybook "9.1.10"
|
||||||
"@storybook/addon-docs": *storybook
|
"@storybook/addon-docs": *storybook
|
||||||
"@storybook/addon-a11y": *storybook
|
"@storybook/addon-a11y": *storybook
|
||||||
"@storybook/nextjs": *storybook
|
"@storybook/nextjs": *storybook
|
||||||
"@storybook/addon-themes": *storybook
|
"@storybook/addon-themes": *storybook
|
||||||
"@storybook/test-runner": "0.23.0"
|
"@storybook/test-runner": "0.23.0"
|
||||||
"@chromatic-com/storybook": "4.1.1"
|
"@chromatic-com/storybook": "4.1.1"
|
||||||
"chromatic": "13.1.4"
|
"chromatic": "13.3.0"
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
"playwright": &playwright "1.55.0"
|
"playwright": &playwright "1.56.0"
|
||||||
"@playwright/test": *playwright
|
"@playwright/test": *playwright
|
||||||
"start-server-and-test": "2.0.13"
|
"start-server-and-test": "2.1.2"
|
||||||
|
|
||||||
# CSS
|
# CSS
|
||||||
"postcss": "8.5.6"
|
"postcss": "8.5.6"
|
||||||
"@tailwindcss/postcss": "4.1.12"
|
"@tailwindcss/postcss": "4.1.14"
|
||||||
"@tailwindcss/typography": "0.5.16"
|
"@tailwindcss/typography": "0.5.19"
|
||||||
"tailwindcss": "4.1.12"
|
"tailwindcss": "4.1.14"
|
||||||
"tailwind-merge": "3.3.1"
|
"tailwind-merge": "3.3.1"
|
||||||
"clsx": "2.1.1"
|
"clsx": "2.1.1"
|
||||||
"cva": "1.0.0-beta.4"
|
"cva": "1.0.0-beta.4"
|
||||||
"@fontsource/montserrat": "5.2.6"
|
"@fontsource/montserrat": "5.2.8"
|
||||||
|
|
||||||
onlyBuiltDependencies:
|
onlyBuiltDependencies:
|
||||||
- "@swc/core"
|
- "@swc/core"
|
||||||
|
Reference in New Issue
Block a user