mirror of
https://github.com/theoludwig/theoludwig.git
synced 2025-05-29 22:37:44 +02:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
fd51609713 | |||
edf16c2562 | |||
94e0d190ae | |||
b1cf7f8517 | |||
a1a715d3b9 | |||
eede46fb41 | |||
e32c53caa1 | |||
361ea37deb | |||
d49a8a7470 | |||
a4996c8251 | |||
b25451e631 | |||
042a861f58 | |||
d76db36dbc | |||
99d9dcf334 | |||
ece5ded1b4 | |||
1514600998 | |||
5f5b328895 | |||
c88887a322 |
@ -3,18 +3,22 @@
|
|||||||
"dockerComposeFile": "./docker-compose.yml",
|
"dockerComposeFile": "./docker-compose.yml",
|
||||||
"service": "workspace",
|
"service": "workspace",
|
||||||
"workspaceFolder": "/workspace",
|
"workspaceFolder": "/workspace",
|
||||||
"settings": {
|
"customizations": {
|
||||||
"remote.autoForwardPorts": false
|
"vscode": {
|
||||||
|
"settings": {
|
||||||
|
"remote.autoForwardPorts": false
|
||||||
|
},
|
||||||
|
"extensions": [
|
||||||
|
"editorconfig.editorconfig",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"bradlc.vscode-tailwindcss",
|
||||||
|
"mikestead.dotenv",
|
||||||
|
"davidanson.vscode-markdownlint",
|
||||||
|
"ms-azuretools.vscode-docker"
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"extensions": [
|
|
||||||
"editorconfig.editorconfig",
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"bradlc.vscode-tailwindcss",
|
|
||||||
"mikestead.dotenv",
|
|
||||||
"davidanson.vscode-markdownlint",
|
|
||||||
"ms-azuretools.vscode-docker"
|
|
||||||
],
|
|
||||||
"forwardPorts": [3000],
|
"forwardPorts": [3000],
|
||||||
"postAttachCommand": ["npm", "install"],
|
"postAttachCommand": ["npm", "install"],
|
||||||
"remoteUser": "node"
|
"remoteUser": "node"
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
.vscode
|
.*
|
||||||
.git
|
!.npmrc
|
||||||
.env
|
|
||||||
build
|
build
|
||||||
.next
|
|
||||||
coverage
|
coverage
|
||||||
node_modules
|
node_modules
|
||||||
tmp
|
|
||||||
temp
|
|
||||||
.DS_Store
|
|
||||||
.lighthouseci
|
|
||||||
.vercel
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
COMPOSE_PROJECT_NAME=divlo.fr
|
COMPOSE_PROJECT_NAME=divlo
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"prettier/prettier": "error",
|
"prettier/prettier": "error",
|
||||||
"unicorn/prefer-node-protocol": "error",
|
|
||||||
"@next/next/no-img-element": "off"
|
"@next/next/no-img-element": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,6 +1,6 @@
|
|||||||
<!-- Please first discuss the change you wish to make via issue before making a change. It might avoid a waste of your time. -->
|
<!-- Please first discuss the change you wish to make via issue before making a change. It might avoid a waste of your time. -->
|
||||||
|
|
||||||
## What changes this PR introduce?
|
# What changes this PR introduce?
|
||||||
|
|
||||||
## List any relevant issue numbers
|
## List any relevant issue numbers
|
||||||
|
|
||||||
|
2
.github/workflows/analyze.yml
vendored
2
.github/workflows/analyze.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
language: ['javascript']
|
language: ['javascript']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: 'actions/checkout@v3.1.0'
|
- uses: 'actions/checkout@v3.5.2'
|
||||||
|
|
||||||
- name: 'Initialize CodeQL'
|
- name: 'Initialize CodeQL'
|
||||||
uses: 'github/codeql-action/init@v2'
|
uses: 'github/codeql-action/init@v2'
|
||||||
|
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@ -10,16 +10,16 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: 'ubuntu-latest'
|
runs-on: 'ubuntu-latest'
|
||||||
steps:
|
steps:
|
||||||
- uses: 'actions/checkout@v3.1.0'
|
- uses: 'actions/checkout@v3.5.2'
|
||||||
|
|
||||||
- name: 'Use Node.js'
|
- name: 'Setup Node.js'
|
||||||
uses: 'actions/setup-node@v3.5.1'
|
uses: 'actions/setup-node@v3.6.0'
|
||||||
with:
|
with:
|
||||||
node-version: '18.x'
|
node-version: '18.x'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: 'Install'
|
- name: 'Install dependencies'
|
||||||
run: 'npm install'
|
run: 'npm clean-install'
|
||||||
|
|
||||||
- name: 'Build'
|
- name: 'Build'
|
||||||
run: 'npm run build'
|
run: 'npm run build'
|
||||||
|
19
.github/workflows/lint.yml
vendored
19
.github/workflows/lint.yml
vendored
@ -10,16 +10,16 @@ jobs:
|
|||||||
lint:
|
lint:
|
||||||
runs-on: 'ubuntu-latest'
|
runs-on: 'ubuntu-latest'
|
||||||
steps:
|
steps:
|
||||||
- uses: 'actions/checkout@v3.1.0'
|
- uses: 'actions/checkout@v3.5.2'
|
||||||
|
|
||||||
- name: 'Use Node.js'
|
- name: 'Setup Node.js'
|
||||||
uses: 'actions/setup-node@v3.5.1'
|
uses: 'actions/setup-node@v3.6.0'
|
||||||
with:
|
with:
|
||||||
node-version: '18.x'
|
node-version: '18.x'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: 'Install'
|
- name: 'Install dependencies'
|
||||||
run: 'npm install'
|
run: 'npm clean-install'
|
||||||
|
|
||||||
- name: 'lint:commit'
|
- name: 'lint:commit'
|
||||||
run: 'npm run lint:commit -- --to "${{ github.sha }}"'
|
run: 'npm run lint:commit -- --to "${{ github.sha }}"'
|
||||||
@ -30,8 +30,8 @@ jobs:
|
|||||||
- name: 'lint:markdown'
|
- name: 'lint:markdown'
|
||||||
run: 'npm run lint:markdown'
|
run: 'npm run lint:markdown'
|
||||||
|
|
||||||
- name: 'lint:typescript'
|
- name: 'lint:eslint'
|
||||||
run: 'npm run lint:typescript'
|
run: 'npm run lint:eslint'
|
||||||
|
|
||||||
- name: 'lint:prettier'
|
- name: 'lint:prettier'
|
||||||
run: 'npm run lint:prettier'
|
run: 'npm run lint:prettier'
|
||||||
@ -40,8 +40,3 @@ jobs:
|
|||||||
uses: 'dotenv-linter/action-dotenv-linter@v2'
|
uses: 'dotenv-linter/action-dotenv-linter@v2'
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.github_token }}
|
github_token: ${{ secrets.github_token }}
|
||||||
|
|
||||||
- name: 'lint:docker'
|
|
||||||
uses: 'hadolint/hadolint-action@v1.6.0'
|
|
||||||
with:
|
|
||||||
dockerfile: './Dockerfile'
|
|
||||||
|
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@ -8,26 +8,26 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
runs-on: 'ubuntu-latest'
|
runs-on: 'ubuntu-latest'
|
||||||
steps:
|
steps:
|
||||||
- uses: 'actions/checkout@v3.1.0'
|
- uses: 'actions/checkout@v3.5.2'
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: 'Import GPG key'
|
- name: 'Import GPG key'
|
||||||
uses: 'crazy-max/ghaction-import-gpg@v4'
|
uses: 'crazy-max/ghaction-import-gpg@v5.3.0'
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
git_user_signingkey: true
|
git_user_signingkey: true
|
||||||
git_commit_gpgsign: true
|
git_commit_gpgsign: true
|
||||||
|
|
||||||
- name: 'Use Node.js'
|
- name: 'Setup Node.js'
|
||||||
uses: 'actions/setup-node@v3.5.1'
|
uses: 'actions/setup-node@v3.6.0'
|
||||||
with:
|
with:
|
||||||
node-version: '18.x'
|
node-version: '18.x'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: 'Install'
|
- name: 'Install dependencies'
|
||||||
run: 'npm install'
|
run: 'npm clean-install'
|
||||||
|
|
||||||
- name: 'Release'
|
- name: 'Release'
|
||||||
run: 'npm run release'
|
run: 'npm run release'
|
||||||
|
44
.github/workflows/test.yml
vendored
44
.github/workflows/test.yml
vendored
@ -10,33 +10,33 @@ jobs:
|
|||||||
test-unit:
|
test-unit:
|
||||||
runs-on: 'ubuntu-latest'
|
runs-on: 'ubuntu-latest'
|
||||||
steps:
|
steps:
|
||||||
- uses: 'actions/checkout@v3.1.0'
|
- uses: 'actions/checkout@v3.5.2'
|
||||||
|
|
||||||
- name: 'Use Node.js'
|
- name: 'Setup Node.js'
|
||||||
uses: 'actions/setup-node@v3.5.1'
|
uses: 'actions/setup-node@v3.6.0'
|
||||||
with:
|
with:
|
||||||
node-version: '18.x'
|
node-version: '18.x'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: 'Install'
|
- name: 'Install dependencies'
|
||||||
run: 'npm install'
|
run: 'npm clean-install'
|
||||||
|
|
||||||
- name: 'Unit Test'
|
- name: 'Unit Test'
|
||||||
run: 'npm run test:unit'
|
run: 'npm run test:unit'
|
||||||
|
|
||||||
test-lighthouse:
|
test-e2e:
|
||||||
runs-on: 'ubuntu-latest'
|
runs-on: 'ubuntu-latest'
|
||||||
steps:
|
steps:
|
||||||
- uses: 'actions/checkout@v3.1.0'
|
- uses: 'actions/checkout@v3.5.0'
|
||||||
|
|
||||||
- name: 'Use Node.js'
|
- name: 'Setup Node.js'
|
||||||
uses: 'actions/setup-node@v3.5.1'
|
uses: 'actions/setup-node@v3.6.0'
|
||||||
with:
|
with:
|
||||||
node-version: '18.x'
|
node-version: '18.x'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: 'Install'
|
- name: 'Install dependencies'
|
||||||
run: 'npm install'
|
run: 'npm clean-install'
|
||||||
|
|
||||||
- name: 'Build'
|
- name: 'Build'
|
||||||
run: 'npm run build'
|
run: 'npm run build'
|
||||||
@ -44,27 +44,5 @@ jobs:
|
|||||||
- name: 'html-w3c-validator'
|
- name: 'html-w3c-validator'
|
||||||
run: 'npm run test:html-w3c-validator'
|
run: 'npm run test:html-w3c-validator'
|
||||||
|
|
||||||
- name: 'Lighthouse'
|
|
||||||
run: 'npm run test:lighthouse'
|
|
||||||
env:
|
|
||||||
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
|
|
||||||
|
|
||||||
test-e2e:
|
|
||||||
runs-on: 'ubuntu-latest'
|
|
||||||
steps:
|
|
||||||
- uses: 'actions/checkout@v3.1.0'
|
|
||||||
|
|
||||||
- name: 'Use Node.js'
|
|
||||||
uses: 'actions/setup-node@v3.5.1'
|
|
||||||
with:
|
|
||||||
node-version: '18.x'
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: 'Install'
|
|
||||||
run: 'npm install'
|
|
||||||
|
|
||||||
- name: 'Build'
|
|
||||||
run: 'npm run build'
|
|
||||||
|
|
||||||
- name: 'End To End (e2e) Test'
|
- name: 'End To End (e2e) Test'
|
||||||
run: 'npm run test:e2e'
|
run: 'npm run test:e2e'
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"ci": {
|
|
||||||
"collect": {
|
|
||||||
"startServerCommand": "npm run start",
|
|
||||||
"startServerReadyPattern": "ready on",
|
|
||||||
"startServerReadyTimeout": 20000,
|
|
||||||
"url": [
|
|
||||||
"http://127.0.0.1:3000/",
|
|
||||||
"http://127.0.0.1:3000/blog",
|
|
||||||
"http://127.0.0.1:3000/blog/hello-world"
|
|
||||||
],
|
|
||||||
"numberOfRuns": 1
|
|
||||||
},
|
|
||||||
"assert": {
|
|
||||||
"preset": "lighthouse:recommended",
|
|
||||||
"assertions": {
|
|
||||||
"csp-xss": "warning",
|
|
||||||
"non-composited-animations": "warning",
|
|
||||||
"unused-javascript": "warning",
|
|
||||||
"image-size-responsive": "warning",
|
|
||||||
"unsized-images": "warning",
|
|
||||||
"color-contrast": "warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"upload": {
|
|
||||||
"target": "temporary-public-storage"
|
|
||||||
},
|
|
||||||
"server": {}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"default": true,
|
"default": true,
|
||||||
"MD013": false,
|
"relative-links": true,
|
||||||
|
"extends": "markdownlint/style/prettier",
|
||||||
"MD024": false,
|
"MD024": false,
|
||||||
"MD033": false,
|
"MD033": false
|
||||||
"MD041": false
|
|
||||||
},
|
},
|
||||||
"globs": ["**/*.{md,mdx}"],
|
"globs": ["**/*.{md,mdx}"],
|
||||||
"ignores": ["**/node_modules"]
|
"ignores": ["**/node_modules"],
|
||||||
|
"customRules": ["markdownlint-rule-relative-links"]
|
||||||
}
|
}
|
||||||
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -6,5 +6,9 @@
|
|||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll": true
|
"source.fixAll": true
|
||||||
}
|
},
|
||||||
|
"eslint.options": {
|
||||||
|
"ignorePath": ".gitignore"
|
||||||
|
},
|
||||||
|
"prettier.ignorePath": ".gitignore"
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ representative at an online or offline event.
|
|||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
reported to the community leaders responsible for enforcement at
|
reported to the community leaders responsible for enforcement at
|
||||||
contact@divlo.fr.
|
<contact@divlo.fr>.
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
Thanks a lot for your interest in contributing to **divlo.fr**! 🎉
|
Thanks a lot for your interest in contributing to **divlo.fr**! 🎉
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
**divlo.fr** adopted the [Contributor Covenant](https://www.contributor-covenant.org/) as its Code of Conduct, and we expect project participants to adhere to it. Please read [the full text](./CODE_OF_CONDUCT.md) so that you can understand what actions will and will not be tolerated.
|
||||||
|
|
||||||
## Types of contributions
|
## Types of contributions
|
||||||
|
|
||||||
- Reporting a bug.
|
- Reporting a bug.
|
||||||
@ -21,29 +25,7 @@ If you're adding new features to **divlo.fr**, please include tests.
|
|||||||
|
|
||||||
## Commits
|
## Commits
|
||||||
|
|
||||||
The commit message guidelines respect [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional) and [Semantic Versioning](https://semver.org/) for releases.
|
The commit message guidelines adheres to [Conventional Commits](https://www.conventionalcommits.org/) and [Semantic Versioning](https://semver.org/) for releases.
|
||||||
|
|
||||||
### Types
|
|
||||||
|
|
||||||
Types define which kind of changes you made to the project.
|
|
||||||
|
|
||||||
| Types | Description |
|
|
||||||
| -------- | ------------------------------------------------------------------------------------------------------------ |
|
|
||||||
| feat | A new feature. |
|
|
||||||
| fix | A bug fix. |
|
|
||||||
| docs | Documentation only changes. |
|
|
||||||
| style | Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc). |
|
|
||||||
| refactor | A code change that neither fixes a bug nor adds a feature. |
|
|
||||||
| perf | A code change that improves performance. |
|
|
||||||
| test | Adding missing tests or correcting existing tests. |
|
|
||||||
| build | Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm). |
|
|
||||||
| ci | Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs). |
|
|
||||||
| chore | Other changes that don't modify src or test files. |
|
|
||||||
| revert | Reverts a previous commit. |
|
|
||||||
|
|
||||||
### Scopes
|
|
||||||
|
|
||||||
Scopes define what part of the code changed.
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
@ -86,4 +68,4 @@ docker compose up --build
|
|||||||
|
|
||||||
### Services started
|
### Services started
|
||||||
|
|
||||||
- website : `http://127.0.0.1:3000`
|
- website: `http://127.0.0.1:3000`
|
||||||
|
28
Dockerfile
28
Dockerfile
@ -1,21 +1,21 @@
|
|||||||
FROM node:18.11.0 AS dependencies
|
FROM node:18.16.0 AS builder-dependencies
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/application
|
||||||
COPY ./package*.json ./
|
COPY ./package*.json ./
|
||||||
RUN npm install
|
RUN npm clean-install
|
||||||
|
|
||||||
FROM node:18.11.0 AS builder
|
FROM node:18.16.0 AS builder
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/application
|
||||||
|
COPY --from=builder-dependencies /usr/src/application/node_modules ./node_modules
|
||||||
COPY ./ ./
|
COPY ./ ./
|
||||||
COPY --from=dependencies /usr/src/app/node_modules ./node_modules
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM node:18.11.0 AS runner
|
FROM gcr.io/distroless/nodejs18-debian11:latest AS runner
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/application
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV NEXT_TELEMETRY_DISABLED=1
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
COPY --from=builder /usr/src/app/.next/standalone ./
|
COPY --from=builder /usr/src/application/.next/standalone ./
|
||||||
COPY --from=builder /usr/src/app/.next/static ./.next/static
|
COPY --from=builder /usr/src/application/.next/static ./.next/static
|
||||||
COPY --from=builder /usr/src/app/public ./public
|
COPY --from=builder /usr/src/application/public ./public
|
||||||
COPY --from=builder /usr/src/app/locales ./locales
|
COPY --from=builder /usr/src/application/locales ./locales
|
||||||
COPY --from=builder /usr/src/app/next.config.js ./next.config.js
|
COPY --from=builder /usr/src/application/next.config.js ./next.config.js
|
||||||
CMD ["node", "server.js"]
|
CMD ["./server.js"]
|
||||||
|
20
README.md
20
README.md
@ -1,7 +1,7 @@
|
|||||||
<h1 align="center"><a href="https://divlo.fr/">Divlo</a></h1>
|
<h1 align="center"><a href="https://divlo.fr/">Divlo</a></h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<strong>Developer Full Stack • Passionate about High-Tech</strong>
|
<strong>Developer Full Stack • Open-Source enthusiast</strong>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -25,21 +25,11 @@
|
|||||||
"pronouns": "He/Him",
|
"pronouns": "He/Him",
|
||||||
"birthDate": "31/03/2003",
|
"birthDate": "31/03/2003",
|
||||||
"nationality": "Alsace, France",
|
"nationality": "Alsace, France",
|
||||||
"interests": [
|
"interests": ["Open-Source enthusiast", "Passionate about High-Tech"],
|
||||||
"Developer Full Stack",
|
|
||||||
"Passionate about High-Tech",
|
|
||||||
"Open-Source enthusiast"
|
|
||||||
],
|
|
||||||
"skills": {
|
"skills": {
|
||||||
"programmingLanguages": [
|
"programmingLanguages": ["JavaScript/TypeScript", "Python", "C/C++", "PHP"],
|
||||||
"JavaScript",
|
"frontEnd": ["HTML", "CSS", "Tailwind CSS", "React.js/Next.js"],
|
||||||
"TypeScript",
|
"backEnd": ["Laravel", "Node.js", "Fastify", "PostgreSQL"],
|
||||||
"Python",
|
|
||||||
"C/C++",
|
|
||||||
"PHP"
|
|
||||||
],
|
|
||||||
"frontEnd": ["HTML", "CSS", "Tailwind CSS", "React.js (+ Next.js)"],
|
|
||||||
"backEnd": ["Laravel", "Node.js", "Fastify", "Prisma", "PostgreSQL"],
|
|
||||||
"tools": ["GNU/Linux", "Ubuntu", "Visual Studio Code", "Git", "Docker"]
|
"tools": ["GNU/Linux", "Ubuntu", "Visual Studio Code", "Git", "Docker"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ export const Language: React.FC = () => {
|
|||||||
<ul
|
<ul
|
||||||
data-cy='languages-list'
|
data-cy='languages-list'
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'absolute top-14 z-10 mt-3 mr-4 flex w-24 list-none flex-col items-center justify-center rounded-lg bg-white p-0 shadow-lightFlag dark:bg-black dark:shadow-darkFlag',
|
'absolute top-14 z-10 mr-4 mt-3 flex w-24 list-none flex-col items-center justify-center rounded-lg bg-white p-0 shadow-lightFlag dark:bg-black dark:shadow-darkFlag',
|
||||||
{ hidden: hiddenMenu }
|
{ hidden: hiddenMenu }
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -29,7 +29,7 @@ export const SwitchTheme: React.FC = () => {
|
|||||||
<div
|
<div
|
||||||
data-cy='switch-theme-dark'
|
data-cy='switch-theme-dark'
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'absolute top-0 bottom-0 left-[8px] mt-auto mb-auto h-[10px] w-[14px] leading-[0] transition-opacity duration-[250ms] ease-in-out',
|
'absolute bottom-0 left-[8px] top-0 mb-auto mt-auto h-[10px] w-[14px] leading-[0] transition-opacity duration-[250ms] ease-in-out',
|
||||||
{
|
{
|
||||||
'opacity-100': theme === 'dark',
|
'opacity-100': theme === 'dark',
|
||||||
'opacity-0': theme === 'light'
|
'opacity-0': theme === 'light'
|
||||||
@ -43,7 +43,7 @@ export const SwitchTheme: React.FC = () => {
|
|||||||
<div
|
<div
|
||||||
data-cy='switch-theme-light'
|
data-cy='switch-theme-light'
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'absolute right-[10px] top-0 bottom-0 mt-auto mb-auto h-[10px] w-[10px] leading-[0]',
|
'absolute bottom-0 right-[10px] top-0 mb-auto mt-auto h-[10px] w-[10px] leading-[0]',
|
||||||
{
|
{
|
||||||
'opacity-100': theme === 'light',
|
'opacity-100': theme === 'light',
|
||||||
'opacity-0': theme === 'dark'
|
'opacity-0': theme === 'dark'
|
||||||
|
@ -10,7 +10,7 @@ export const InterestItem: React.FC<InterestItemProps> = (props) => {
|
|||||||
const { fontAwesomeIcon, title } = props
|
const { fontAwesomeIcon, title } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className='interest-item my-2 mx-2 h-8 w-8' title={title}>
|
<li className='interest-item mx-2 my-2 h-8 w-8' title={title}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
className='block h-full w-full text-yellow dark:text-yellow-dark'
|
className='block h-full w-full text-yellow dark:text-yellow-dark'
|
||||||
icon={fontAwesomeIcon}
|
icon={fontAwesomeIcon}
|
||||||
|
@ -4,7 +4,7 @@ export const ProfileDescriptionBottom: React.FC = () => {
|
|||||||
const { t, lang } = useTranslation()
|
const { t, lang } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p className='mt-8 mb-8 text-base font-normal text-gray dark:text-gray-dark'>
|
<p className='mb-8 mt-8 text-base font-normal text-gray dark:text-gray-dark'>
|
||||||
{t('home:about.description-bottom')}
|
{t('home:about.description-bottom')}
|
||||||
{lang === 'fr' && (
|
{lang === 'fr' && (
|
||||||
<>
|
<>
|
||||||
|
@ -12,7 +12,7 @@ export const ProfileItem: React.FC<ProfileItemProps> = (props) => {
|
|||||||
<strong className='float-left block w-28 text-sm font-bold text-black dark:text-white'>
|
<strong className='float-left block w-28 text-sm font-bold text-black dark:text-white'>
|
||||||
{title}
|
{title}
|
||||||
</strong>
|
</strong>
|
||||||
<span className='ml-0 mb-4 block text-sm font-normal text-gray dark:text-gray-dark sm:mb-0 sm:ml-32'>
|
<span className='mb-4 ml-0 block text-sm font-normal text-gray dark:text-gray-dark sm:mb-0 sm:ml-32'>
|
||||||
{link != null ? (
|
{link != null ? (
|
||||||
<a
|
<a
|
||||||
className='text-gray hover:underline dark:text-gray-dark'
|
className='text-gray hover:underline dark:text-gray-dark'
|
||||||
|
@ -27,7 +27,6 @@ export const Skills: React.FC = () => {
|
|||||||
<SkillComponent skill='Laravel' />
|
<SkillComponent skill='Laravel' />
|
||||||
<SkillComponent skill='Node.js' />
|
<SkillComponent skill='Node.js' />
|
||||||
<SkillComponent skill='Fastify' />
|
<SkillComponent skill='Fastify' />
|
||||||
<SkillComponent skill='Prisma' />
|
|
||||||
<SkillComponent skill='PostgreSQL' />
|
<SkillComponent skill='PostgreSQL' />
|
||||||
</SkillsSection>
|
</SkillsSection>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
|
|
||||||
export const RevealFade: React.FC<React.PropsWithChildren<{}>> = (props) => {
|
export const RevealFade: React.FC<React.PropsWithChildren> = (props) => {
|
||||||
const { children } = props
|
const { children } = props
|
||||||
|
|
||||||
const htmlElement = useRef<HTMLDivElement>(null)
|
const htmlElement = useRef<HTMLDivElement>(null)
|
||||||
|
@ -4,7 +4,7 @@ export const SectionHeading: React.FC<SectionHeadingProps> = (props) => {
|
|||||||
const { children, ...rest } = props
|
const { children, ...rest } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<h2 {...rest} className='mt-1 mb-3 text-center text-4xl font-semibold'>
|
<h2 {...rest} className='mb-3 mt-1 text-center text-4xl font-semibold'>
|
||||||
{children}
|
{children}
|
||||||
</h2>
|
</h2>
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Footer } from 'components/Footer'
|
import { Footer } from '@/components/Footer'
|
||||||
|
|
||||||
describe('<Footer />', () => {
|
describe('<Footer />', () => {
|
||||||
it('should render with appropriate link tag version', () => {
|
it('should render with appropriate link tag version', () => {
|
@ -1,11 +1,11 @@
|
|||||||
services:
|
services:
|
||||||
divlo.fr:
|
divlo:
|
||||||
container_name: ${COMPOSE_PROJECT_NAME}
|
container_name: ${COMPOSE_PROJECT_NAME}
|
||||||
image: 'divlo.fr'
|
image: 'divlo'
|
||||||
build:
|
build:
|
||||||
context: './'
|
context: './'
|
||||||
ports:
|
ports:
|
||||||
- '${PORT-3000}:${PORT-3000}'
|
- '${PORT-3000}:${PORT-3000}'
|
||||||
environment:
|
environment:
|
||||||
PORT: ${PORT-3000}
|
PORT: ${PORT-3000}
|
||||||
env_file: './.env'
|
env_file: '.env'
|
||||||
|
@ -3,10 +3,10 @@ import fs from 'node:fs'
|
|||||||
|
|
||||||
import { build } from 'vite'
|
import { build } from 'vite'
|
||||||
|
|
||||||
const jsonResumeThemeCustom = new URL('../', import.meta.url)
|
const jsonResumeThemeCustom = new URL('./', import.meta.url)
|
||||||
const jsonResumeThemeCustomDist = new URL('./dist', jsonResumeThemeCustom)
|
const jsonResumeThemeCustomDist = new URL('./dist', jsonResumeThemeCustom)
|
||||||
const publicResumeOutputURL = new URL(
|
const publicResumeOutputURL = new URL(
|
||||||
'../../public/curriculum-vitae',
|
'../public/curriculum-vitae',
|
||||||
import.meta.url
|
import.meta.url
|
||||||
)
|
)
|
||||||
|
|
@ -6,6 +6,7 @@
|
|||||||
<title><%= locals.basics.name %></title>
|
<title><%= locals.basics.name %></title>
|
||||||
<link rel="icon" type="image/png" href="<%= locals.basics.image %>" />
|
<link rel="icon" type="image/png" href="<%= locals.basics.image %>" />
|
||||||
<link rel="stylesheet" href="./styles/global.css" />
|
<link rel="stylesheet" href="./styles/global.css" />
|
||||||
|
<script defer type="module" src="./scripts/main.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
@ -26,12 +27,15 @@
|
|||||||
<strong><%= locals.basics.name %></strong>
|
<strong><%= locals.basics.name %></strong>
|
||||||
</h3>
|
</h3>
|
||||||
<h5 class="text-muted"><%= locals.basics.label %></h5>
|
<h5 class="text-muted"><%= locals.basics.label %></h5>
|
||||||
|
<h5 class="text-muted">
|
||||||
|
<%= locals.basics.age %> (<span id="year-old"></span> ans)
|
||||||
|
</h5>
|
||||||
|
<h5 class="text-muted">
|
||||||
|
<%= locals.basics.location.address %>
|
||||||
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="contact-details clearfix">
|
<div class="contact-details clearfix">
|
||||||
<div class="detail">
|
|
||||||
<span class="info"><%= locals.basics.phone %></span>
|
|
||||||
</div>
|
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="info">
|
<span class="info">
|
||||||
<a
|
<a
|
||||||
@ -70,61 +74,109 @@
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="detail" id="work-experience">
|
<section class="section-separated">
|
||||||
<div class="icon">
|
<div class="detail" id="education">
|
||||||
<img src="./images/building-columns.svg" alt="work" />
|
<div class="icon">
|
||||||
|
<img src="./images/graduation-cap.svg" alt="graduation" />
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<h4 class="title text-uppercase">Formations</h4>
|
||||||
|
<div class="content">
|
||||||
|
<ul class="list-unstyled clear-margin">
|
||||||
|
<% locals.education.forEach((degree) => { %>
|
||||||
|
<li class="card card-nested">
|
||||||
|
<div class="content">
|
||||||
|
<p class="clear-margin relative">
|
||||||
|
<strong><%= degree.studyType %></strong>
|
||||||
|
</p>
|
||||||
|
<p class="clear-margin relative">
|
||||||
|
<strong><%= degree.score %></strong>
|
||||||
|
</p>
|
||||||
|
<p class="text-muted clear-margin">
|
||||||
|
<%= degree.institution %>
|
||||||
|
</p>
|
||||||
|
<p class="text-muted clear-margin">
|
||||||
|
<small>
|
||||||
|
<%= degree.startDate %> <%= degree.endDate !=
|
||||||
|
null ? " - " + degree.endDate : "" %>
|
||||||
|
</small>
|
||||||
|
</p>
|
||||||
|
<% if (degree.courses != null) { %>
|
||||||
|
<ul class="education-courses">
|
||||||
|
<% degree.courses.forEach((course) => { %>
|
||||||
|
<li><%= course %></li>
|
||||||
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info">
|
|
||||||
<h4 class="title text-uppercase">Expériences</h4>
|
<div class="detail" id="skills">
|
||||||
<ul class="list-unstyled clear-margin">
|
<div class="icon">
|
||||||
<% locals.work.forEach((experience) => { %>
|
<img src="./images/toolbox.svg" alt="toolbox" />
|
||||||
<li class="card card-nested clearfix">
|
</div>
|
||||||
<div class="content">
|
<div class="info">
|
||||||
<p class="clear-margin relative">
|
<h4 class="title text-uppercase">Compétences</h4>
|
||||||
<a href="<%= experience.website %>">
|
<div class="content">
|
||||||
<strong><%= experience.name %></strong>
|
<ul class="list-unstyled clear-margin">
|
||||||
</a>
|
<% locals.skills.forEach((skill) => { %>
|
||||||
</p>
|
<li class="card card-nested card-skills">
|
||||||
<p class="clear-margin relative">
|
<div class="skill-info">
|
||||||
<strong><%- experience.position %></strong>
|
<strong><%= skill.name %></strong>
|
||||||
</p>
|
<div class="space-top labels">
|
||||||
<p class="text-muted">
|
<% skill.keywords.forEach((keyword) => { %>
|
||||||
<small>
|
<p class="label label-keyword"><%= keyword %></p>
|
||||||
<span class="space-right">
|
<% }) %>
|
||||||
<%= date.format(new Date(experience.startDate),
|
</div>
|
||||||
'DD/MM/YYYY') %> - <%= date.format(new
|
</div>
|
||||||
Date(experience.endDate), 'DD/MM/YYYY') %>
|
</li>
|
||||||
</span>
|
<% }) %>
|
||||||
</small>
|
</ul>
|
||||||
</p>
|
</div>
|
||||||
<div class="experience-description">
|
</div>
|
||||||
<p><%- experience.summary %></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<% }) %>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="detail" id="skills">
|
<section class="section-separated">
|
||||||
<div class="icon">
|
<div class="detail" id="work-experience">
|
||||||
<img src="./images/toolbox.svg" alt="toolbox" />
|
<div class="icon">
|
||||||
</div>
|
<img src="./images/building-columns.svg" alt="work" />
|
||||||
<div class="info">
|
</div>
|
||||||
<h4 class="title text-uppercase">Compétences</h4>
|
<div class="info">
|
||||||
<div class="content">
|
<h4 class="title text-uppercase">Expériences</h4>
|
||||||
<ul class="list-unstyled clear-margin">
|
<ul class="list-unstyled clear-margin">
|
||||||
<% locals.skills.forEach((skill) => { %>
|
<% locals.work.filter((experience) =>
|
||||||
<li class="card card-nested card-skills">
|
experience.description == null).forEach((experience) => {
|
||||||
<div class="skill-info">
|
%>
|
||||||
<strong><%= skill.name %></strong>
|
<li class="card card-nested clearfix">
|
||||||
<div class="space-top labels">
|
<div class="content">
|
||||||
<% skill.keywords.forEach((keyword) => { %>
|
<p class="clear-margin relative">
|
||||||
<p class="label label-keyword"><%= keyword %></p>
|
<a href="<%= experience.website %>">
|
||||||
<% }) %>
|
<strong><%= experience.name %></strong>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="clear-margin relative">
|
||||||
|
<strong><%- experience.position %></strong>
|
||||||
|
</p>
|
||||||
|
<p class="text-muted">
|
||||||
|
<small>
|
||||||
|
<span class="space-right">
|
||||||
|
<%= date.format(new Date(experience.startDate),
|
||||||
|
'DD/MM/YYYY') %> - <%= date.format(new
|
||||||
|
Date(experience.endDate), 'DD/MM/YYYY') %> (<%=
|
||||||
|
experience.duration %>)
|
||||||
|
</span>
|
||||||
|
</small>
|
||||||
|
</p>
|
||||||
|
<div class="experience-description">
|
||||||
|
<p><%- experience.summary %></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@ -132,63 +184,57 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
<div class="detail" id="interests">
|
||||||
|
<div class="icon">
|
||||||
|
<img src="./images/heart.svg" alt="heart" />
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<h4 class="title text-uppercase">Intérets</h4>
|
||||||
|
<div class="content">
|
||||||
|
<ul class="list-unstyled clear-margin">
|
||||||
|
<% locals.interests.forEach((interest) => { %>
|
||||||
|
<li class="card card-nested">
|
||||||
|
<p><strong><%= interest.name %></strong></p>
|
||||||
|
</li>
|
||||||
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div class="detail" id="education">
|
<ul class="list-unstyled clear-margin">
|
||||||
<div class="icon">
|
<% locals.work.filter((experience) =>
|
||||||
<img src="./images/graduation-cap.svg" alt="graduation" />
|
experience.description != null).forEach((experience) =>
|
||||||
</div>
|
{ %>
|
||||||
<div class="info">
|
<li class="card card-nested clearfix">
|
||||||
<h4 class="title text-uppercase">Éducation</h4>
|
<div class="content">
|
||||||
<div class="content">
|
<p class="clear-margin relative">
|
||||||
<ul class="list-unstyled clear-margin">
|
<a href="<%= experience.website %>">
|
||||||
<% locals.education.forEach((degree) => { %>
|
<strong><%= experience.name %></strong>
|
||||||
<li class="card card-nested">
|
</a>
|
||||||
<div class="content">
|
</p>
|
||||||
<p class="clear-margin relative">
|
<p class="clear-margin relative">
|
||||||
<strong><%= degree.studyType %></strong>
|
<strong><%- experience.position %></strong>
|
||||||
</p>
|
</p>
|
||||||
<p class="clear-margin relative">
|
<p class="text-muted">
|
||||||
<strong><%= degree.score %></strong>
|
<small>
|
||||||
</p>
|
<span class="space-right">
|
||||||
<p class="text-muted clear-margin">
|
<%= date.format(new
|
||||||
<%= degree.institution %>
|
Date(experience.startDate), 'DD/MM/YYYY') %> -
|
||||||
</p>
|
<%= date.format(new Date(experience.endDate),
|
||||||
<p class="text-muted clear-margin">
|
'DD/MM/YYYY') %> (<%= experience.duration %>)
|
||||||
<small>
|
</span>
|
||||||
<%= degree.startDate %> <%= degree.endDate != null
|
</small>
|
||||||
? " - " + degree.endDate : "" %>
|
</p>
|
||||||
</small>
|
<div class="experience-description">
|
||||||
</p>
|
<p><%- experience.summary %></p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</div>
|
||||||
<% }) %>
|
</li>
|
||||||
</ul>
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<div class="detail" id="interests">
|
|
||||||
<div class="icon">
|
|
||||||
<img src="./images/heart.svg" alt="heart" />
|
|
||||||
</div>
|
|
||||||
<div class="info">
|
|
||||||
<h4 class="title text-uppercase">Intérets</h4>
|
|
||||||
<div class="content">
|
|
||||||
<ul class="list-unstyled clear-margin">
|
|
||||||
<% locals.interests.forEach((interest) => { %>
|
|
||||||
<li class="card card-nested">
|
|
||||||
<p><strong><%= interest.name %></strong></p>
|
|
||||||
</li>
|
|
||||||
<% }) %>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
1911
jsonresume-theme-custom/package-lock.json
generated
1911
jsonresume-theme-custom/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -9,12 +9,13 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"jsonc-parser": "3.2.0",
|
||||||
"modern-normalize": "1.1.0"
|
"modern-normalize": "1.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "18.11.9",
|
"@types/node": "20.1.4",
|
||||||
"date-and-time": "2.4.1",
|
"date-and-time": "3.0.0",
|
||||||
"vite": "3.2.3",
|
"vite": "4.3.5",
|
||||||
"vite-plugin-html": "3.2.0"
|
"vite-plugin-html": "3.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
jsonresume-theme-custom/scripts/main.js
Normal file
5
jsonresume-theme-custom/scripts/main.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { DIVLO_BIRTHDAY, getAge } from '../../utils/getAge.ts'
|
||||||
|
|
||||||
|
const yearOld = document.getElementById('year-old')
|
||||||
|
|
||||||
|
yearOld.textContent = getAge(DIVLO_BIRTHDAY).toString()
|
@ -209,7 +209,6 @@ h5 {
|
|||||||
font-size: 75%;
|
font-size: 75%;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
@ -217,8 +216,6 @@ h5 {
|
|||||||
}
|
}
|
||||||
.label-keyword {
|
.label-keyword {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: #7eb0db;
|
|
||||||
color: white;
|
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border: 1px solid #357ebd;
|
border: 1px solid #357ebd;
|
||||||
@ -227,3 +224,6 @@ h5 {
|
|||||||
.label-keyword p {
|
.label-keyword p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
.section-separated {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
|
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
|
import { parse as JSONCParser } from 'jsonc-parser'
|
||||||
import { createHtmlPlugin } from 'vite-plugin-html'
|
import { createHtmlPlugin } from 'vite-plugin-html'
|
||||||
import date from 'date-and-time'
|
import date from 'date-and-time'
|
||||||
|
|
||||||
const jsonResumeURL = new URL('../resume.json', import.meta.url)
|
const jsonResumeURL = new URL('../resume.jsonc', import.meta.url)
|
||||||
const dataResumeStringJSON = await fs.promises.readFile(jsonResumeURL, {
|
const dataResumeStringJSON = await fs.promises.readFile(jsonResumeURL, {
|
||||||
encoding: 'utf-8'
|
encoding: 'utf-8'
|
||||||
})
|
})
|
||||||
const resume = JSON.parse(dataResumeStringJSON)
|
const resume = JSONCParser(dataResumeStringJSON)
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
/**
|
||||||
|
* Documentation: <https://vitejs.dev/config/>
|
||||||
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
build: {
|
build: {
|
||||||
assetsDir: './'
|
assetsDir: './'
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"i-am": "I am",
|
"i-am": "I am",
|
||||||
"description": "Developer Full Stack • Passionate about High-Tech",
|
"description": "Developer Full Stack • Open-Source enthusiast",
|
||||||
"full-name": "Full name",
|
"full-name": "Full name",
|
||||||
"birth-date": "Birth date",
|
"birth-date": "Birth date",
|
||||||
"years-old": "years old",
|
"years-old": "years old",
|
||||||
"nationality": "Nationality",
|
"nationality": "Nationality",
|
||||||
"description-bottom": "I am self-taught in Computer Science by following online trainings and I am also a student at the university following the French training \"BUT Informatique\" (second year)."
|
"description-bottom": "I am a student in computer science following the French training \"BUT Informatique\" and I am also a self-taught."
|
||||||
},
|
},
|
||||||
"interests": {
|
"interests": {
|
||||||
"title": "Interests",
|
"title": "Interests",
|
||||||
@ -16,12 +16,12 @@
|
|||||||
"description": "Computer programming is my main hobby, I love it! <br/> Mostly web development for the moment but I'm programming in others programming language too."
|
"description": "Computer programming is my main hobby, I love it! <br/> Mostly web development for the moment but I'm programming in others programming language too."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Passionate about High-Tech",
|
"title": "Open-Source enthusiast",
|
||||||
"description": "I always wondered how the future would be. Every day I want to wake up and think that the future will be great and better than the past. Technologies improve gradually over time, which is very useful in many areas."
|
"description": "For me, everyone should work, solve problems, build things and think together.<br/> The website is open-source on <a class='text-yellow dark:text-yellow-dark hover:underline' href='https://github.com/Divlo/Divlo' target='_blank' rel='noopener noreferrer'>GitHub</a>."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Open-Source enthusiast",
|
"title": "Passionate about High-Tech",
|
||||||
"description": "For me, everyone should work, solve problems, build things and think together. Long live open source, whenever you can share your work, do it! <br/> The website is open-source on <a class='text-yellow dark:text-yellow-dark hover:underline' href='https://github.com/Divlo/Divlo' target='_blank' rel='noopener noreferrer'>github</a>."
|
"description": "I always wondered how the future would be. Every day I want to wake up and think that the future will be great and better than the past. Technologies improve gradually over time, which is very useful in many areas."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"i-am": "Je suis",
|
"i-am": "Je suis",
|
||||||
"description": "Développeur Full Stack • Passionné de High-Tech",
|
"description": "Développeur Full Stack • Enthousiaste de l'Open-Source",
|
||||||
"full-name": "Prénom NOM",
|
"full-name": "Prénom NOM",
|
||||||
"birth-date": "Date de naissance",
|
"birth-date": "Date de naissance",
|
||||||
"years-old": "ans",
|
"years-old": "ans",
|
||||||
"nationality": "Nationalité",
|
"nationality": "Nationalité",
|
||||||
"description-bottom": "Je me forme en autodidacte dans l'informatique en suivant des formations en ligne et je suis aussi un étudiant à l'université suivant la formation \"BUT Informatique\" (deuxième année)."
|
"description-bottom": "Je suis étudiant à l'université suivant la formation \"BUT Informatique\" et me forme en autodidacte dans l'informatique en suivant des formations en ligne."
|
||||||
},
|
},
|
||||||
"interests": {
|
"interests": {
|
||||||
"title": "Intérêts",
|
"title": "Intérêts",
|
||||||
@ -16,12 +16,12 @@
|
|||||||
"description": "La programmation informatique est mon loisir principal, j'adore! <br/> Principalement du développement Web pour le moment, mais je programme aussi dans d'autres langages de programmation."
|
"description": "La programmation informatique est mon loisir principal, j'adore! <br/> Principalement du développement Web pour le moment, mais je programme aussi dans d'autres langages de programmation."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Passionné de High-Tech",
|
"title": "Enthousiaste de l'Open-Source",
|
||||||
"description": "Je me suis toujours demandé comment l'avenir serait. Chaque jour, je veux me réveiller et penser que l'avenir sera formidable et meilleur que le passé. Les technolgies s'améliorent progressivement avec le temps, ce qui est très utile dans de nombreux domaines."
|
"description": "Pour moi, tout le monde devrait travailler, résoudre des problèmes, construire des choses et réfléchir ensemble. <br/> Le site est open-source sur <a class='text-yellow dark:text-yellow-dark hover:underline' href='https://github.com/Divlo/Divlo' target='_blank' rel='noopener noreferrer'>GitHub</a>."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Enthousiaste de l'Open-Source",
|
"title": "Passionné de High-Tech",
|
||||||
"description": "Pour moi, tout le monde devrait travailler, résoudre des problèmes, construire des choses et réfléchir ensemble. Longue vie à l'open-source, chaque fois que vous pouvez partagez votre travail, faites-le! <br/> Le site est open-source sur <a class='text-yellow dark:text-yellow-dark hover:underline' href='https://github.com/Divlo/Divlo' target='_blank' rel='noopener noreferrer'>github</a>."
|
"description": "Je me suis toujours demandé comment l'avenir serait. Chaque jour, je veux me réveiller et penser que l'avenir sera formidable et meilleur que le passé. Les technolgies s'améliorent progressivement avec le temps, ce qui est très utile dans de nombreux domaines."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,7 @@ const nextPWA = require('next-pwa')({
|
|||||||
disable: process.env.NODE_ENV !== 'production',
|
disable: process.env.NODE_ENV !== 'production',
|
||||||
dest: 'public'
|
dest: 'public'
|
||||||
})
|
})
|
||||||
const nextTranslate = require('next-translate')
|
const nextTranslate = require('next-translate-plugin')
|
||||||
|
|
||||||
/** @type {import("next").NextConfig} */
|
/** @type {import("next").NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
|
31467
package-lock.json
generated
31467
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
96
package.json
96
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "divlo",
|
"name": "divlo",
|
||||||
"version": "2.5.1",
|
"version": "2.6.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -14,85 +14,85 @@
|
|||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"build": "npm run resume:build && next build",
|
"build": "npm run resume:build && next build",
|
||||||
"export": "next export",
|
|
||||||
"lint:commit": "commitlint",
|
"lint:commit": "commitlint",
|
||||||
"lint:editorconfig": "editorconfig-checker",
|
"lint:editorconfig": "editorconfig-checker",
|
||||||
"lint:markdown": "markdownlint-cli2",
|
"lint:markdown": "markdownlint-cli2",
|
||||||
"lint:typescript": "eslint \"**/*.{js,jsx,ts,tsx}\" --ignore-path \".gitignore\"",
|
"lint:eslint": "eslint \".\" --ignore-path \".gitignore\"",
|
||||||
"lint:prettier": "prettier \".\" --check --ignore-path \".gitignore\"",
|
"lint:prettier": "prettier \".\" --check --ignore-path \".gitignore\"",
|
||||||
"lint:staged": "lint-staged",
|
"lint:staged": "lint-staged",
|
||||||
"test:unit": "cypress run --component",
|
"test:unit": "cypress run --component",
|
||||||
"test:html-w3c-validator": "start-server-and-test \"start\" \"http://127.0.0.1:3000\" \"html-w3c-validator\"",
|
"test:html-w3c-validator": "start-server-and-test \"start\" \"http://127.0.0.1:3000\" \"html-w3c-validator\"",
|
||||||
"test:lighthouse": "lhci autorun",
|
|
||||||
"test:e2e": "start-server-and-test \"start\" \"http://127.0.0.1:3000\" \"cypress run\"",
|
"test:e2e": "start-server-and-test \"start\" \"http://127.0.0.1:3000\" \"cypress run\"",
|
||||||
"test:dev": "start-server-and-test \"dev\" \"http://127.0.0.1:3000\" \"cypress open\"",
|
"test:dev": "start-server-and-test \"dev\" \"http://127.0.0.1:3000\" \"cypress open\"",
|
||||||
"resume:build": "node ./jsonresume-theme-custom/scripts/build.js",
|
"resume:build": "node ./jsonresume-theme-custom/build.js",
|
||||||
"release": "semantic-release",
|
"release": "semantic-release",
|
||||||
"deploy": "vercel",
|
"deploy": "vercel",
|
||||||
"postinstall": "husky install"
|
"postinstall": "husky install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/montserrat": "4.5.13",
|
"@fontsource/montserrat": "4.5.14",
|
||||||
"@fortawesome/fontawesome-svg-core": "6.2.0",
|
"@fortawesome/fontawesome-svg-core": "6.4.0",
|
||||||
"@fortawesome/free-brands-svg-icons": "6.2.0",
|
"@fortawesome/free-brands-svg-icons": "6.4.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "6.2.0",
|
"@fortawesome/free-solid-svg-icons": "6.4.0",
|
||||||
"@fortawesome/react-fontawesome": "0.2.0",
|
"@fortawesome/react-fontawesome": "0.2.0",
|
||||||
"@giscus/react": "2.2.2",
|
"@giscus/react": "2.2.8",
|
||||||
"clsx": "1.2.1",
|
"clsx": "1.2.1",
|
||||||
"date-and-time": "2.4.1",
|
"date-and-time": "3.0.0",
|
||||||
"gray-matter": "4.0.3",
|
"gray-matter": "4.0.3",
|
||||||
"html-react-parser": "3.0.4",
|
"html-react-parser": "3.0.16",
|
||||||
"next": "13.0.2",
|
"next": "13.4.2",
|
||||||
"next-mdx-remote": "4.2.0",
|
"next-mdx-remote": "4.4.1",
|
||||||
"next-pwa": "5.6.0",
|
"next-pwa": "5.6.0",
|
||||||
"next-themes": "0.2.1",
|
"next-themes": "0.2.1",
|
||||||
"next-translate": "1.6.0",
|
"next-translate": "2.0.5",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"read-pkg": "7.1.0",
|
"read-pkg": "8.0.0",
|
||||||
"rehype-raw": "6.1.1",
|
"rehype-raw": "6.1.1",
|
||||||
"rehype-slug": "5.1.0",
|
"rehype-slug": "5.1.0",
|
||||||
"remark-gfm": "3.0.1",
|
"remark-gfm": "3.0.1",
|
||||||
"sharp": "0.31.2",
|
"sharp": "0.32.1",
|
||||||
"shiki": "0.11.1",
|
"shiki": "0.14.2",
|
||||||
"unified": "10.1.2",
|
"unified": "10.1.2",
|
||||||
"unist-util-visit": "4.1.1",
|
"unist-util-visit": "4.1.2",
|
||||||
"universal-cookie": "4.0.4"
|
"universal-cookie": "4.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "17.2.0",
|
"@commitlint/cli": "17.6.3",
|
||||||
"@commitlint/config-conventional": "17.2.0",
|
"@commitlint/config-conventional": "17.6.3",
|
||||||
"@lhci/cli": "0.9.0",
|
"@saithodev/semantic-release-backmerge": "3.2.0",
|
||||||
"@saithodev/semantic-release-backmerge": "2.1.2",
|
|
||||||
"@semantic-release/git": "10.0.1",
|
"@semantic-release/git": "10.0.1",
|
||||||
"@tailwindcss/typography": "0.5.8",
|
"@tailwindcss/typography": "0.5.9",
|
||||||
"@types/node": "18.11.9",
|
"@tsconfig/strictest": "2.0.1",
|
||||||
"@types/react": "18.0.25",
|
"@types/node": "20.1.4",
|
||||||
|
"@types/react": "18.2.6",
|
||||||
"@types/unist": "2.0.6",
|
"@types/unist": "2.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
"@typescript-eslint/eslint-plugin": "5.59.5",
|
||||||
"autoprefixer": "10.4.13",
|
"autoprefixer": "10.4.14",
|
||||||
"cypress": "10.11.0",
|
"cypress": "12.12.0",
|
||||||
"editorconfig-checker": "4.0.2",
|
"editorconfig-checker": "5.0.1",
|
||||||
"eslint": "8.27.0",
|
"eslint": "8.40.0",
|
||||||
"eslint-config-conventions": "5.0.0",
|
"eslint-config-conventions": "9.0.0",
|
||||||
"eslint-config-next": "13.0.2",
|
"eslint-config-next": "13.4.2",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint-config-prettier": "8.8.0",
|
||||||
"eslint-plugin-import": "2.26.0",
|
"eslint-plugin-import": "2.27.5",
|
||||||
"eslint-plugin-prettier": "4.2.1",
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
"eslint-plugin-promise": "6.1.1",
|
"eslint-plugin-promise": "6.1.1",
|
||||||
"eslint-plugin-unicorn": "44.0.2",
|
"eslint-plugin-unicorn": "47.0.0",
|
||||||
"html-w3c-validator": "1.2.1",
|
"html-w3c-validator": "1.3.0",
|
||||||
"husky": "8.0.2",
|
"husky": "8.0.3",
|
||||||
"jsonresume-theme-custom": "file:./jsonresume-theme-custom",
|
"jsonresume-theme-custom": "file:./jsonresume-theme-custom",
|
||||||
"lint-staged": "13.0.3",
|
"lint-staged": "13.2.2",
|
||||||
"markdownlint-cli2": "0.5.1",
|
"markdownlint-cli2": "0.7.1",
|
||||||
"postcss": "8.4.18",
|
"markdownlint-rule-relative-links": "1.2.0",
|
||||||
"prettier": "2.7.1",
|
"next-translate-plugin": "2.0.5",
|
||||||
"prettier-plugin-tailwindcss": "0.1.13",
|
"postcss": "8.4.23",
|
||||||
"semantic-release": "19.0.5",
|
"prettier": "2.8.8",
|
||||||
"start-server-and-test": "1.14.0",
|
"prettier-plugin-tailwindcss": "0.2.8",
|
||||||
"tailwindcss": "3.2.2",
|
"semantic-release": "21.0.2",
|
||||||
"typescript": "4.8.4",
|
"start-server-and-test": "2.0.0",
|
||||||
"vercel": "28.4.15"
|
"tailwindcss": "3.3.2",
|
||||||
|
"typescript": "5.0.4",
|
||||||
|
"vercel": "29.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,11 @@ const BlogPostPage: NextPage<BlogPostPageProps> = (props) => {
|
|||||||
const { src, alt, ...props } = properties
|
const { src, alt, ...props } = properties
|
||||||
let source = src
|
let source = src
|
||||||
source = src?.replace('../public/', '/')
|
source = src?.replace('../public/', '/')
|
||||||
return <img src={source} alt={alt} {...props} />
|
return (
|
||||||
|
<span className='flex flex-col items-center justify-center'>
|
||||||
|
<img src={source} alt={alt} {...props} />
|
||||||
|
</span>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
a: (props) => {
|
a: (props) => {
|
||||||
if (props.href?.startsWith('#') ?? false) {
|
if (props.href?.startsWith('#') ?? false) {
|
||||||
@ -78,7 +82,7 @@ const BlogPostPage: NextPage<BlogPostPageProps> = (props) => {
|
|||||||
export const getStaticProps: GetStaticProps<BlogPostPageProps> = async (
|
export const getStaticProps: GetStaticProps<BlogPostPageProps> = async (
|
||||||
context
|
context
|
||||||
) => {
|
) => {
|
||||||
const slug = context?.params?.slug
|
const slug = context?.params?.['slug']
|
||||||
const { getPostBySlug } = await import('utils/blog')
|
const { getPostBySlug } = await import('utils/blog')
|
||||||
const post = await getPostBySlug(slug)
|
const post = await getPostBySlug(slug)
|
||||||
if (post == null || (post != null && !post.frontmatter.isPublished)) {
|
if (post == null || (post != null && !post.frontmatter.isPublished)) {
|
||||||
|
@ -35,18 +35,11 @@ In this section, I will explain what technologies I used to make this blog, and
|
|||||||
|
|
||||||
The code of this website is open source on [GitHub](https://github.com/Divlo/Divlo), so you can see the code and contribute to it.
|
The code of this website is open source on [GitHub](https://github.com/Divlo/Divlo), so you can see the code and contribute to it.
|
||||||
|
|
||||||
I decided to keep things simple, here are the 2 main features missing on my blog:
|
|
||||||
|
|
||||||
- Comments (you can interact with me on my Twitter account)
|
|
||||||
- Views counter
|
|
||||||
|
|
||||||
That not mean that these features will never be implemented, but to avoid the need of a database now, I dropped out these features.
|
|
||||||
|
|
||||||
### Technologies
|
### Technologies
|
||||||
|
|
||||||
- [Next.js](https://nextjs.org/)
|
- [Next.js](https://nextjs.org/)
|
||||||
|
|
||||||
It allows to have a server-side rendered website, that means that it is faster and easier to have a good SEO (Search Engine Optimization) than a SPA (Single Page Application).
|
It allows to have a server-side rendered website, that means that it is faster and easier to have a good <abbr title="Search Engine Optimization">SEO</abbr> than a <abbr title="Single Page Application">SPA</abbr>.
|
||||||
|
|
||||||
- [MDX](https://mdxjs.com/)
|
- [MDX](https://mdxjs.com/)
|
||||||
|
|
||||||
|
@ -53,9 +53,7 @@ Since the project is mainly developed during free time (mainly on weekends), the
|
|||||||
|
|
||||||
- The **client** part, called **frontend**, what **the user sees on the screen**, such as forms, buttons and all the **graphic elements** with which the user can interact from a browser.
|
- The **client** part, called **frontend**, what **the user sees on the screen**, such as forms, buttons and all the **graphic elements** with which the user can interact from a browser.
|
||||||
|
|
||||||
<span className='flex flex-col items-center justify-center'>
|

|
||||||

|
|
||||||
</span>
|
|
||||||
|
|
||||||
This design allows the separation between the client and the server, as long as they both structure their communication according to the <abbr title="Representational state transfer">REST</abbr> architectural guidelines, using the <abbr title="Hypertext Transfer Protocol">HTTP</abbr> protocol, they will be able to communicate with each other, which makes it possible to work independently on the backend and on the frontend using different technologies and skills, really useful in teamwork.
|
This design allows the separation between the client and the server, as long as they both structure their communication according to the <abbr title="Representational state transfer">REST</abbr> architectural guidelines, using the <abbr title="Hypertext Transfer Protocol">HTTP</abbr> protocol, they will be able to communicate with each other, which makes it possible to work independently on the backend and on the frontend using different technologies and skills, really useful in teamwork.
|
||||||
|
|
||||||
|
118
resume.json
118
resume.json
@ -1,118 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json",
|
|
||||||
"meta": {
|
|
||||||
"theme": "custom"
|
|
||||||
},
|
|
||||||
"basics": {
|
|
||||||
"name": "Théo LUDWIG",
|
|
||||||
"label": "Développeur Full Stack • Passionné de High-Tech",
|
|
||||||
"image": "https://divlo.fr/images/logo_orange.png",
|
|
||||||
"email": "contact@divlo.fr",
|
|
||||||
"location": {},
|
|
||||||
"url": "https://divlo.fr",
|
|
||||||
"summary": "Je me forme en autodidacte dans l'informatique en suivant des formations en ligne et je suis aussi un étudiant à l'université suivant la formation \"BUT Informatique\" (deuxième année). <br/> Je mets en pratique tout ce que j'apprends et réalise de nombreux projets."
|
|
||||||
},
|
|
||||||
"education": [
|
|
||||||
{
|
|
||||||
"startDate": "2022",
|
|
||||||
"studyType": "Diplôme du Bachelor Universitaire de Technologie (BUT) Informatique",
|
|
||||||
"institution": "IUT Robert Schuman à Illkirch-Graffenstaden",
|
|
||||||
"score": "En cours"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"startDate": "2019",
|
|
||||||
"endDate": "2021",
|
|
||||||
"studyType": "Diplôme du 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 site web en React.js et Strapi afin de répondre <a href=\"https://www.nuitdelinfo.com/nuitinfo/_media/infos:la_nuit_de_l_info_2021_-_sujet.pdf\">au sujet de la Nuit de l'Info 2021</a>.<br /> TOP 1 France: Défi de l'entreprise <a href=\"https://www.nuitdelinfo.com/inscription/defis/300\">ToolPad</a>.",
|
|
||||||
"website": "https://www.nuitdelinfo.com/",
|
|
||||||
"name": "La Nuit de l'info 2021",
|
|
||||||
"position": "Participation avec l'équipe <a href=\"https://www.nuitdelinfo.com/inscription/equipes/46\">Who are We</a>",
|
|
||||||
"startDate": "2021-12-02",
|
|
||||||
"endDate": "2021-12-03"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"summary": "Agent administratif en vue de faire face au sucroît temporaire d'activités liés à la numérisation des plans des postes sources <br /> actuellement sous format papier calque suite à la libération des locaux des archives.",
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"summary": "Hackathon développement d'une landing page et web scraping.",
|
|
||||||
"website": "https://www.wildcodeschool.fr/",
|
|
||||||
"name": "Wild Code School",
|
|
||||||
"location": "32 Rue du Bass. d'Austerlitz, 67100 Strasbourg",
|
|
||||||
"position": "Stage initiation métier développeur web",
|
|
||||||
"startDate": "2019-06-24",
|
|
||||||
"endDate": "2019-06-28"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"summary": "Développement d'un site web pour trouver un restaurant à la pause repas.",
|
|
||||||
"website": "https://www.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"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"summary": "Apprentissage du métier \"Chargé de communication\" et des logiciels de graphisme tels que \"Adobe Photoshop\".",
|
|
||||||
"website": "https://www.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"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"interests": [
|
|
||||||
{
|
|
||||||
"name": "Développeur Full Stack"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Passionné de High-Tech"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Enthousiaste de l'Open-Source"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"skills": [
|
|
||||||
{
|
|
||||||
"keywords": ["JavaScript", "TypeScript", "Python", "C/C++", "PHP"],
|
|
||||||
"name": "Langages de programmation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keywords": ["HTML", "CSS", "Tailwind CSS", "React.js (+ Next.js)"],
|
|
||||||
"name": "Front-end"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keywords": ["Laravel", "Node.js", "Fastify", "PostgreSQL"],
|
|
||||||
"name": "Back-end"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keywords": [
|
|
||||||
"GNU/Linux",
|
|
||||||
"Ubuntu",
|
|
||||||
"Visual Studio Code",
|
|
||||||
"git",
|
|
||||||
"Docker"
|
|
||||||
],
|
|
||||||
"name": "Logiciels et outils"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
160
resume.jsonc
Normal file
160
resume.jsonc
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json",
|
||||||
|
"meta": {
|
||||||
|
"theme": "custom"
|
||||||
|
},
|
||||||
|
"basics": {
|
||||||
|
"name": "Théo LUDWIG",
|
||||||
|
"label": "Développeur Full Stack • Étudiant",
|
||||||
|
"image": "https://divlo.fr/images/logo_orange.png",
|
||||||
|
"email": "contact@divlo.fr",
|
||||||
|
"age": "31/03/2003",
|
||||||
|
"location": {
|
||||||
|
"address": "Alsace, France"
|
||||||
|
},
|
||||||
|
"url": "https://divlo.fr",
|
||||||
|
"summary": "Je suis étudiant à l'université suivant la formation \"BUT Informatique\" et me forme en autodidacte dans l'informatique en suivant des formations en ligne. <br/> Je mets en pratique tout ce que j'apprends et réalise de nombreux projets (disponible sur <a href=\"https://divlo.fr\">divlo.fr</a>)."
|
||||||
|
},
|
||||||
|
"education": [
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"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",
|
||||||
|
"Projet développement d'une application web en React.js en équipe de 3 personnes pendant 3 mois"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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 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",
|
||||||
|
"startDate": "2023-04-11",
|
||||||
|
"endDate": "2023-06-23",
|
||||||
|
"duration": "3 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://www.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://www.toolpad.fr/\">ToolPad</a>.",
|
||||||
|
"website": "https://www.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://www.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://www.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": "Front-end"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keywords": ["Laravel", "Node.js", "Fastify", "PostgreSQL"],
|
||||||
|
"name": "Back-end"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keywords": [
|
||||||
|
"GNU/Linux",
|
||||||
|
"Ubuntu",
|
||||||
|
"Visual Studio Code",
|
||||||
|
"Git",
|
||||||
|
"Docker"
|
||||||
|
],
|
||||||
|
"name": "Logiciels et outils"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keywords": ["Permis B", "Anglais"],
|
||||||
|
"name": "Autres"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,25 +1,24 @@
|
|||||||
{
|
{
|
||||||
|
"extends": "@tsconfig/strictest/tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "node",
|
"lib": ["dom", "dom.iterable", "ESNext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"checkJs": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"sourceMap": true,
|
|
||||||
"removeComments": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"strict": true,
|
|
||||||
"types": ["cypress"],
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"esModuleInterop": true,
|
"paths": {
|
||||||
"forceConsistentCasingInFileNames": true,
|
"@/*": ["./*"]
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
},
|
||||||
"skipLibCheck": true,
|
"types": ["cypress"],
|
||||||
|
"noEmit": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"jsx": "preserve",
|
||||||
"incremental": true
|
"incremental": true,
|
||||||
|
"exactOptionalPropertyTypes": false,
|
||||||
|
"verbatimModuleSyntax": false,
|
||||||
|
"isolatedModules": true
|
||||||
},
|
},
|
||||||
"exclude": ["dist", ".next", "out", "next.config.js"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,9 @@ export const getPosts = async (): Promise<PostMetadata[]> => {
|
|||||||
const postsWithTime = await Promise.all(
|
const postsWithTime = await Promise.all(
|
||||||
posts.map(async (postFilename) => {
|
posts.map(async (postFilename) => {
|
||||||
const [slug, extension] = postFilename.split('.')
|
const [slug, extension] = postFilename.split('.')
|
||||||
|
if (slug == null || extension == null) {
|
||||||
|
throw new Error('Invalid postFilename.')
|
||||||
|
}
|
||||||
const blogPostPath = path.join(POSTS_PATH, `${slug}.${extension}`)
|
const blogPostPath = path.join(POSTS_PATH, `${slug}.${extension}`)
|
||||||
const blogPostContent = await fs.promises.readFile(blogPostPath, {
|
const blogPostContent = await fs.promises.readFile(blogPostPath, {
|
||||||
encoding: 'utf8'
|
encoding: 'utf8'
|
||||||
|
Reference in New Issue
Block a user