1
1
mirror of https://github.com/theoludwig/theoludwig.git synced 2025-05-29 22:37:44 +02:00

Compare commits

..

12 Commits

58 changed files with 10082 additions and 16955 deletions

View File

@ -1,5 +1,3 @@
version: '3.0'
services:
workspace:
build:

View File

@ -6,12 +6,10 @@
},
"env": {
"node": true,
"browser": true,
"jest": true
"browser": true
},
"rules": {
"prettier/prettier": "error",
"unicorn/prefer-node-protocol": "off",
"@typescript-eslint/no-misused-promises": "off"
"unicorn/prefer-node-protocol": "error"
}
}

3
.gitignore vendored
View File

@ -11,8 +11,7 @@ out
# production
build
dist
public/*.html
jsonresume-theme-custom/theme/index.html
public/curriculum-vitae
# PWA
public/workbox-*.js
public/sw.js

View File

@ -4,5 +4,5 @@
"http://localhost:3000/blog",
"http://localhost:3000/blog/hello-world"
],
"files": ["./public/curriculum-vitae.html"]
"files": ["./public/curriculum-vitae/index.html"]
}

View File

@ -1,10 +1,6 @@
{
"*": ["editorconfig-checker"],
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"eslint --fix",
"jest --findRelatedTests"
],
"*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix"],
"*.{css,scss,sass,json,jsonc,yml,yaml}": ["prettier --write"],
"*.{md,mdx}": ["prettier --write", "markdownlint --dot --fix"]
"*.{md,mdx}": ["prettier --write", "markdownlint-cli2 --fix"]
}

11
.markdownlint-cli2.jsonc Normal file
View File

@ -0,0 +1,11 @@
{
"config": {
"default": true,
"MD013": false,
"MD024": false,
"MD033": false,
"MD041": false
},
"globs": ["**/*.{md,mdx}"],
"ignores": ["**/node_modules"]
}

View File

@ -1,7 +0,0 @@
{
"default": true,
"MD013": false,
"MD024": false,
"MD033": false,
"MD041": false
}

View File

@ -81,7 +81,7 @@ npm run dev
```sh
# Setup and run all the services for you
docker-compose up --build
docker compose up --build
```
### Services started

View File

@ -1,15 +1,15 @@
FROM node:16.14.2 AS dependencies
FROM node:16.16.0 AS dependencies
WORKDIR /usr/src/app
COPY ./package*.json ./
RUN npm install
FROM node:16.14.2 AS builder
FROM node:16.16.0 AS builder
WORKDIR /usr/src/app
COPY ./ ./
COPY --from=dependencies /usr/src/app/node_modules ./node_modules
RUN npm run build
FROM node:16.14.2 AS runner
FROM node:16.16.0 AS runner
WORKDIR /usr/src/app
ENV NODE_ENV=production
COPY --from=builder /usr/src/app/next.config.js ./next.config.js

View File

@ -27,6 +27,7 @@ export const Footer: React.FC<FooterProps> = (props) => {
<p className='mt-1'>
Version{' '}
<a
data-cy='version-link'
className='text-yellow hover:underline dark:text-yellow-dark'
href={versionLink}
target='_blank'

View File

@ -1,7 +1,7 @@
import { useCallback, useEffect, useState } from 'react'
import { useCallback, useEffect, useState, useRef } from 'react'
import useTranslation from 'next-translate/useTranslation'
import setLanguage from 'next-translate/setLanguage'
import classNames from 'classnames'
import classNames from 'clsx'
import i18n from 'i18n.json'
@ -11,31 +11,37 @@ import { LanguageFlag } from './LanguageFlag'
export const Language: React.FC = () => {
const { lang: currentLanguage } = useTranslation()
const [hiddenMenu, setHiddenMenu] = useState(true)
const languageClickRef = useRef<HTMLDivElement | null>(null)
const handleHiddenMenu = useCallback(() => {
setHiddenMenu(!hiddenMenu)
}, [hiddenMenu])
setHiddenMenu((oldHiddenMenu) => !oldHiddenMenu)
}, [])
useEffect(() => {
if (!hiddenMenu) {
window.document.addEventListener('click', handleHiddenMenu)
} else {
window.document.removeEventListener('click', handleHiddenMenu)
const handleClickEvent = (event: MouseEvent): void => {
if (languageClickRef.current == null || event.target == null) {
return
}
if (!languageClickRef.current.contains(event.target as Node)) {
setHiddenMenu(true)
}
}
window.document.addEventListener('click', handleClickEvent)
return () => {
window.document.removeEventListener('click', handleHiddenMenu)
return window.removeEventListener('click', handleClickEvent)
}
}, [hiddenMenu, handleHiddenMenu])
}, [])
const handleLanguage = async (language: string): Promise<void> => {
await setLanguage(language)
handleHiddenMenu()
}
return (
<div className='flex cursor-pointer flex-col items-center justify-center'>
<div
ref={languageClickRef}
data-cy='language-click'
className='mr-5 flex items-center'
onClick={handleHiddenMenu}

View File

@ -1,10 +0,0 @@
import { render } from '@testing-library/react'
import { Header } from '..'
describe('<Header />', () => {
it('should render', () => {
const { getByText } = render(<Header />)
expect(getByText('Divlo')).toBeInTheDocument()
})
})

View File

@ -1,4 +1,4 @@
import classNames from 'classnames'
import classNames from 'clsx'
export const Icon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
const { children, className, ...rest } = props

View File

@ -3,7 +3,9 @@ interface SocialMediaItemProps {
ariaLabel: string
}
export const SocialMediaItem: React.FC<SocialMediaItemProps> = (props) => {
export const SocialMediaItem: React.FC<
React.PropsWithChildren<SocialMediaItemProps>
> = (props) => {
const { link, ariaLabel, children } = props
return (

View File

@ -1,15 +0,0 @@
import { render } from '@testing-library/react'
import { ErrorPage } from '../ErrorPage'
describe('<ErrorPage />', () => {
it('should render the message and statusCode', () => {
const messageContent = 'message content'
const statusCode = 404
const { getByText } = render(
<ErrorPage statusCode={statusCode} message={messageContent} />
)
expect(getByText(messageContent)).toBeInTheDocument()
expect(getByText(statusCode)).toBeInTheDocument()
})
})

View File

@ -1,16 +0,0 @@
import { render } from '@testing-library/react'
import { Footer } from '../Footer'
describe('<Footer />', () => {
it('should render with appropriate link tag version', () => {
const version = '1.0.0'
const { getByText } = render(<Footer version={version} />)
const versionLink = getByText(version) as HTMLAnchorElement
expect(getByText('Divlo')).toBeInTheDocument()
expect(versionLink).toBeInTheDocument()
expect(versionLink.href).toEqual(
`https://github.com/Divlo/Divlo/releases/tag/v${version}`
)
})
})

View File

@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react'
export const RevealFade: React.FC = (props) => {
export const RevealFade: React.FC<React.PropsWithChildren<{}>> = (props) => {
const { children } = props
const htmlElement = useRef<HTMLDivElement>(null)

View File

@ -1,4 +1,4 @@
import classNames from 'classnames'
import classNames from 'clsx'
type ShadowContainerProps = React.ComponentPropsWithRef<'div'>

20
cypress.config.ts Normal file
View File

@ -0,0 +1,20 @@
import { defineConfig } from 'cypress'
export default defineConfig({
fixturesFolder: false,
video: false,
downloadsFolder: undefined,
screenshotOnRunFailure: false,
e2e: {
baseUrl: 'http://localhost:3000',
supportFile: false
},
component: {
devServer: {
framework: 'next',
bundler: 'webpack'
}
}
})

View File

@ -1,8 +0,0 @@
{
"baseUrl": "http://localhost:3000",
"pluginsFile": false,
"supportFile": false,
"fixturesFolder": false,
"video": false,
"screenshotOnRunFailure": false
}

View File

@ -0,0 +1,18 @@
import { Footer } from 'components/Footer'
describe('<Footer />', () => {
it('should render with appropriate link tag version', () => {
const version = '1.0.0'
cy.mount(<Footer version={version} />)
cy.contains('Divlo')
.get('[data-cy=version-link]')
.should('have.text', version)
.should(
'have.attr',
'href',
`https://github.com/Divlo/Divlo/releases/tag/v${version}`
)
})
})
export {}

View File

@ -0,0 +1,17 @@
import { getAge } from '../../../utils/getAge'
describe('utils/getAge', () => {
it('should calculate the right age of a person', () => {
cy.clock(new Date('2018-03-20')).then(() => {
const birthDate = new Date('1980-02-20')
expect(getAge(birthDate)).equal(38)
})
})
it('should calculate the right age of a person (taking into account the months)', () => {
cy.clock(new Date('2018-03-20')).then(() => {
const birthDate = new Date('1980-07-20')
expect(getAge(birthDate)).equal(37)
})
})
})

View File

@ -56,3 +56,5 @@ describe('Common > Header', () => {
})
})
})
export {}

View File

@ -5,3 +5,5 @@ describe('Page /404', () => {
cy.get('[data-cy=status-code]').contains('404')
})
})
export {}

View File

@ -5,3 +5,5 @@ describe('Page /500', () => {
cy.get('[data-cy=status-code]').contains('500')
})
})
export {}

View File

@ -11,3 +11,5 @@ describe('Page /blog/[slug]', () => {
cy.get('[data-cy=status-code]').contains('404')
})
})
export {}

View File

@ -20,3 +20,5 @@ describe('Page /blog', () => {
.should('eq', '/blog/hello-world')
})
})
export {}

View File

@ -17,3 +17,5 @@ describe('Page /', () => {
}
})
})
export {}

View File

@ -0,0 +1,3 @@
/// <reference types="cypress" />
export {}

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Components App</title>
<!-- Used by Next.js to inject CSS. -->
<div id="__next_css__DO_NOT_USE__"></div>
</head>
<body>
<div data-cy-root></div>
</body>
</html>

View File

@ -0,0 +1,13 @@
import { mount } from 'cypress/react'
import './commands'
declare global {
namespace Cypress {
interface Chainable {
mount: typeof mount
}
}
}
Cypress.Commands.add('mount', mount)

View File

@ -1,9 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"noEmit": true,
"types": ["cypress"],
"isolatedModules": false
},
"include": ["../node_modules/cypress", "./**/*.ts"]
}

View File

@ -1,4 +1,3 @@
version: '3.0'
services:
divlo.fr:
container_name: ${COMPOSE_PROJECT_NAME}

View File

@ -1,14 +0,0 @@
const nextJest = require('next/jest')
const createJestConfig = nextJest()
const customJestConfig = {
moduleDirectories: ['node_modules', './'],
modulePathIgnorePatterns: ['<rootDir>/cypress'],
testEnvironment: 'jsdom',
setupFilesAfterEnv: [
'@testing-library/jest-dom/extend-expect',
'@testing-library/react'
]
}
module.exports = createJestConfig(customJestConfig)

View File

@ -1,4 +1,22 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
theme/index.html
dist
.parcel-cache
dist-ssr
*.local
# Editor directories and files
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

Before

Width:  |  Height:  |  Size: 1015 B

After

Width:  |  Height:  |  Size: 1015 B

View File

Before

Width:  |  Height:  |  Size: 986 B

After

Width:  |  Height:  |  Size: 986 B

View File

Before

Width:  |  Height:  |  Size: 629 B

After

Width:  |  Height:  |  Size: 629 B

View File

Before

Width:  |  Height:  |  Size: 912 B

After

Width:  |  Height:  |  Size: 912 B

View File

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 528 B

View File

@ -5,10 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= locals.basics.name %></title>
<link rel="icon" type="image/png" href="<%= locals.basics.image %>" />
<style>
@import './styles/global.css';
</style>
<link rel="stylesheet" href="./styles/global.css" />
</head>
<body>
<div class="container-fluid">
@ -59,7 +56,7 @@
<div class="background-details">
<div class="detail" id="about">
<div class="icon">
<img src="data-url:./images/user.svg" alt="user" />
<img src="./images/user.svg" alt="user" />
</div>
<div class="info">
<h4 class="title text-uppercase">À propos</h4>
@ -75,10 +72,7 @@
<div class="detail" id="work-experience">
<div class="icon">
<img
src="data-url:./images/building-columns.svg"
alt="work"
/>
<img src="./images/building-columns.svg" alt="work" />
</div>
<div class="info">
<h4 class="title text-uppercase">Expériences</h4>
@ -117,7 +111,7 @@
<div class="detail" id="skills">
<div class="icon">
<img src="data-url:./images/toolbox.svg" alt="toolbox" />
<img src="./images/toolbox.svg" alt="toolbox" />
</div>
<div class="info">
<h4 class="title text-uppercase">Compétences</h4>
@ -144,10 +138,7 @@
<div class="detail" id="education">
<div class="icon">
<img
src="data-url:./images/graduation-cap.svg"
alt="graduation"
/>
<img src="./images/graduation-cap.svg" alt="graduation" />
</div>
<div class="info">
<h4 class="title text-uppercase">Éducation</h4>
@ -167,7 +158,8 @@
</p>
<p class="text-muted clear-margin">
<small>
<%= degree.startDate %> - <%= degree.endDate %>
<%= degree.startDate %> <%= degree.endDate != null
? " - " + degree.endDate : "" %>
</small>
</p>
</div>
@ -182,7 +174,7 @@
<div class="detail" id="interests">
<div class="icon">
<img src="data-url:./images/heart.svg" alt="heart" />
<img src="./images/heart.svg" alt="heart" />
</div>
<div class="info">
<h4 class="title text-uppercase">Intérets</h4>

View File

@ -1,28 +0,0 @@
import fs from 'fs'
import { fileURLToPath } from 'url'
import ejs from 'ejs'
import date from 'date-and-time'
import { Parcel } from '@parcel/core'
export const render = async (resume) => {
const themeIndexURL = new URL('./theme/index.ejs', import.meta.url)
const themeBuildURL = new URL('./theme/index.html', import.meta.url)
const indexHTMLURL = new URL('./dist/index.html', import.meta.url)
const themeBuildPath = fileURLToPath(themeBuildURL)
const html = await ejs.renderFile(fileURLToPath(themeIndexURL), {
date,
locals: {
...resume
}
})
await fs.promises.writeFile(themeBuildURL, html, { encoding: 'utf-8' })
const bundler = new Parcel({
entries: themeBuildPath,
source: themeBuildPath,
mode: 'production',
defaultConfig: '@parcel/config-default'
})
await bundler.run()
return await fs.promises.readFile(indexHTMLURL, { encoding: 'utf-8' })
}

File diff suppressed because it is too large Load Diff

View File

@ -3,17 +3,18 @@
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {},
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"date-and-time": "2.3.0",
"ejs": "3.1.6",
"modern-normalize": "1.1.0"
},
"devDependencies": {
"@parcel/config-default": "2.4.1",
"@parcel/core": "2.4.1",
"@parcel/optimizer-data-url": "2.4.1",
"@parcel/transformer-inline-string": "2.4.1",
"parcel": "2.4.1"
"@types/node": "18.7.11",
"date-and-time": "2.4.1",
"vite": "3.0.9",
"vite-plugin-html": "3.2.0"
}
}

View File

@ -1,18 +1,20 @@
import fs from 'fs'
import { fileURLToPath } from 'node:url'
import fs from 'node:fs'
import { render } from '../index.js'
import { build } from 'vite'
const jsonResumeURL = new URL('../../resume.json', import.meta.url)
const publicResumeURL = new URL(
'../../public/curriculum-vitae.html',
const jsonResumeThemeCustom = new URL('../', import.meta.url)
const jsonResumeThemeCustomDist = new URL('./dist', jsonResumeThemeCustom)
const publicResumeOutputURL = new URL(
'../../public/curriculum-vitae',
import.meta.url
)
const dataResumeStringJSON = await fs.promises.readFile(jsonResumeURL, {
encoding: 'utf-8'
await build({
root: fileURLToPath(jsonResumeThemeCustom),
base: '/curriculum-vitae/'
})
const dataResumeJSON = JSON.parse(dataResumeStringJSON)
const dataResumeIndexHTML = await render(dataResumeJSON)
await fs.promises.writeFile(publicResumeURL, dataResumeIndexHTML, {
encoding: 'utf-8'
await fs.promises.cp(jsonResumeThemeCustomDist, publicResumeOutputURL, {
recursive: true
})

View File

@ -1,4 +1,4 @@
@import 'npm:modern-normalize/modern-normalize.css';
@import 'modern-normalize/modern-normalize.css';
body {
font-family: 'Montserrat', 'Arial', 'sans-serif';

View File

@ -0,0 +1,33 @@
import fs from 'node:fs'
import { defineConfig } from 'vite'
import { createHtmlPlugin } from 'vite-plugin-html'
import date from 'date-and-time'
const jsonResumeURL = new URL('../resume.json', import.meta.url)
const dataResumeStringJSON = await fs.promises.readFile(jsonResumeURL, {
encoding: 'utf-8'
})
const resume = JSON.parse(dataResumeStringJSON)
// https://vitejs.dev/config/
export default defineConfig({
build: {
assetsDir: './'
},
plugins: [
createHtmlPlugin({
inject: {
data: {
date,
locals: {
...resume
}
}
}
})
],
css: {
postcss: {}
}
})

View File

@ -6,7 +6,7 @@
"birth-date": "Birth date",
"years-old": "years old",
"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\" (first year)."
"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)."
},
"interests": {
"title": "Interests",

View File

@ -6,7 +6,7 @@
"birth-date": "Date de naissance",
"years-old": "ans",
"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\" (premre année)."
"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\" (deuxme année)."
},
"interests": {
"title": "Intérêts",

View File

@ -1,41 +1,12 @@
const nextPWA = require('next-pwa')
const nextPWA = require('next-pwa')({
disable: process.env.NODE_ENV !== 'production',
dest: 'public'
})
const nextTranslate = require('next-translate')
const { createSecureHeaders } = require('next-secure-headers')
/** @type {import("next").NextConfig} */
module.exports = nextTranslate(
nextPWA({
reactStrictMode: true,
pwa: {
disable: process.env.NODE_ENV !== 'production',
dest: 'public'
},
headers() {
return [
{
source: '/:path*',
headers: createSecureHeaders({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
'data:',
"'unsafe-eval'",
"'unsafe-inline'"
],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ['*', 'data:', 'blob:'],
mediaSrc: "'none'",
connectSrc: '*',
objectSrc: "'none'",
fontSrc: "'self'",
baseURI: "'none'"
}
}
})
}
]
}
reactStrictMode: true
})
)

21309
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,14 @@
{
"name": "divlo",
"version": "2.3.0",
"version": "2.4.1",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/Divlo/Divlo"
},
"engines": {
"node": ">=14.0.0",
"npm": ">=7.0.0"
"node": ">=16.0.0",
"npm": ">=8.0.0"
},
"scripts": {
"dev": "next dev",
@ -17,86 +17,82 @@
"export": "next export",
"lint:commit": "commitlint",
"lint:editorconfig": "editorconfig-checker",
"lint:markdown": "markdownlint \"**/*.{md,mdx}\" --dot --ignore-path \".gitignore\"",
"lint:markdown": "markdownlint-cli2",
"lint:typescript": "eslint \"**/*.{js,jsx,ts,tsx}\" --ignore-path \".gitignore\"",
"lint:prettier": "prettier \".\" --check --ignore-path \".gitignore\"",
"lint:staged": "lint-staged",
"test:unit": "jest",
"test:unit": "cypress run --component",
"test:html-w3c-validator": "start-server-and-test \"start\" \"http://localhost:3000\" \"html-w3c-validator\"",
"test:lighthouse": "lhci autorun",
"test:e2e": "start-server-and-test \"start\" \"http://localhost:3000\" \"cypress run\"",
"test:e2e:dev": "start-server-and-test \"dev\" \"http://localhost:3000\" \"cypress open\"",
"test:dev": "start-server-and-test \"dev\" \"http://localhost:3000\" \"cypress open\"",
"resume:build": "node ./jsonresume-theme-custom/scripts/build.js",
"release": "semantic-release",
"deploy": "vercel",
"postinstall": "husky install"
},
"dependencies": {
"@fontsource/montserrat": "4.5.7",
"@fortawesome/fontawesome-svg-core": "6.1.1",
"@fortawesome/free-brands-svg-icons": "6.1.1",
"@fortawesome/free-solid-svg-icons": "6.1.1",
"@fortawesome/react-fontawesome": "0.1.18",
"classnames": "2.3.1",
"date-and-time": "2.3.0",
"@fontsource/montserrat": "4.5.12",
"@fortawesome/fontawesome-svg-core": "6.1.2",
"@fortawesome/free-brands-svg-icons": "6.1.2",
"@fortawesome/free-solid-svg-icons": "6.1.2",
"@fortawesome/react-fontawesome": "0.2.0",
"@giscus/react": "2.2.0",
"clsx": "1.2.1",
"date-and-time": "2.4.1",
"gray-matter": "4.0.3",
"html-react-parser": "1.4.10",
"next": "12.1.4",
"next-mdx-remote": "4.0.2",
"next-pwa": "5.4.7",
"next-themes": "0.1.1",
"next-translate": "1.4.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"html-react-parser": "3.0.4",
"next": "12.2.5",
"next-mdx-remote": "4.1.0",
"next-pwa": "5.6.0",
"next-themes": "0.2.0",
"next-translate": "1.5.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"read-pkg": "7.1.0",
"rehype-raw": "6.1.1",
"rehype-slug": "5.0.1",
"remark-gfm": "3.0.1",
"sharp": "0.30.3",
"shiki": "0.10.1",
"sharp": "0.30.7",
"shiki": "0.11.1",
"unified": "10.1.2",
"unist-util-visit": "4.1.0",
"unist-util-visit": "4.1.1",
"universal-cookie": "4.0.4"
},
"devDependencies": {
"@commitlint/cli": "16.2.3",
"@commitlint/config-conventional": "16.2.1",
"@commitlint/cli": "17.0.3",
"@commitlint/config-conventional": "17.0.3",
"@lhci/cli": "0.9.0",
"@saithodev/semantic-release-backmerge": "2.1.2",
"@semantic-release/git": "10.0.1",
"@tailwindcss/typography": "0.5.2",
"@testing-library/jest-dom": "5.16.4",
"@testing-library/react": "12.1.4",
"@types/jest": "27.4.1",
"@types/node": "17.0.23",
"@types/react": "17.0.43",
"@tailwindcss/typography": "0.5.4",
"@types/node": "18.7.11",
"@types/react": "18.0.17",
"@types/unist": "2.0.6",
"@typescript-eslint/eslint-plugin": "5.18.0",
"autoprefixer": "10.4.4",
"cypress": "9.5.3",
"@typescript-eslint/eslint-plugin": "5.34.0",
"autoprefixer": "10.4.8",
"cypress": "10.6.0",
"editorconfig-checker": "4.0.2",
"eslint": "8.13.0",
"eslint-config-conventions": "2.0.0",
"eslint-config-next": "12.1.4",
"eslint": "8.22.0",
"eslint-config-conventions": "3.0.0",
"eslint-config-next": "12.2.5",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-promise": "6.0.0",
"eslint-plugin-unicorn": "42.0.0",
"eslint-plugin-unicorn": "43.0.2",
"html-w3c-validator": "1.2.0",
"husky": "7.0.4",
"jest": "27.5.1",
"husky": "8.0.1",
"jsonresume-theme-custom": "file:./jsonresume-theme-custom",
"lint-staged": "12.3.7",
"markdownlint-cli": "0.31.1",
"next-secure-headers": "2.2.0",
"postcss": "8.4.12",
"prettier": "2.6.2",
"prettier-plugin-tailwindcss": "0.1.8",
"semantic-release": "19.0.2",
"lint-staged": "13.0.3",
"markdownlint-cli2": "0.5.1",
"postcss": "8.4.16",
"prettier": "2.7.1",
"prettier-plugin-tailwindcss": "0.1.13",
"semantic-release": "19.0.4",
"start-server-and-test": "1.14.0",
"tailwindcss": "3.0.23",
"typescript": "4.6.3",
"vercel": "24.0.1"
"tailwindcss": "3.1.8",
"typescript": "4.7.4",
"vercel": "28.1.1"
}
}

View File

@ -1,6 +1,8 @@
import { GetStaticProps, GetStaticPaths, NextPage } from 'next'
import { MDXRemote } from 'next-mdx-remote'
import date from 'date-and-time'
import Giscus from '@giscus/react'
import { useTheme } from 'next-themes'
import { Head } from 'components/Head'
import { Header } from 'components/Header'
@ -14,6 +16,8 @@ interface BlogPostPageProps extends FooterProps {
const BlogPostPage: NextPage<BlogPostPageProps> = (props) => {
const { version, post } = props
const { theme = 'dark' } = useTheme()
return (
<>
<Head
@ -43,6 +47,20 @@ const BlogPostPage: NextPage<BlogPostPageProps> = (props) => {
}
}}
/>
<Giscus
id='comments'
repo='Divlo/Divlo'
repoId='MDEwOlJlcG9zaXRvcnkzNTg5NDg1NDQ='
category='General'
categoryId='DIC_kwDOFWUewM4CQ_WK'
mapping='pathname'
reactionsEnabled='1'
emitMetadata='0'
inputPosition='top'
theme={theme}
lang='en'
loading='lazy'
/>
</div>
</main>
<Footer version={version} />

View File

View File

@ -15,7 +15,6 @@
"education": [
{
"startDate": "2022",
"endDate": "2024",
"studyType": "Diplôme du Bachelor Universitaire de Technologie (BUT) Informatique",
"institution": "IUT Robert Schuman à Illkirch-Graffenstaden",
"score": "En cours"
@ -41,8 +40,8 @@
"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-07-07",
"endDate": "2021-07-30"
"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.",

View File

@ -10,7 +10,7 @@
"removeComments": true,
"noEmit": true,
"strict": true,
"types": ["jest", "@testing-library/jest-dom", "@testing-library/react"],
"types": ["cypress"],
"baseUrl": ".",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,

View File

@ -1,15 +0,0 @@
import { getAge } from '../getAge'
describe('utils/getAge', () => {
it('should calculate the right age of a person', () => {
const birthDate = new Date('1980-02-20')
jest.useFakeTimers().setSystemTime(new Date('2018-03-20'))
expect(getAge(birthDate)).toBe(38)
})
it('should calculate the right age of a person (taking into account the months)', () => {
const birthDate = new Date('1980-07-20')
jest.useFakeTimers().setSystemTime(new Date('2018-03-20'))
expect(getAge(birthDate)).toBe(37)
})
})

View File

@ -1,5 +1,5 @@
import fs from 'fs'
import path from 'path'
import fs from 'node:fs'
import path from 'node:path'
import type { MDXRemoteSerializeResult } from 'next-mdx-remote'
import { nodeTypes } from '@mdx-js/mdx'