Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
2fcc7ac384 | |||
9351edf626 | |||
1f4aa54211 | |||
8bc1471cbb | |||
1ebdab18a5 | |||
b9b76e839a | |||
bc065a2e19 | |||
5d3a287b27 |
@ -1,5 +1,3 @@
|
||||
version: '3.0'
|
||||
|
||||
services:
|
||||
workspace:
|
||||
build:
|
||||
|
@ -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
@ -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
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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
@ -0,0 +1,11 @@
|
||||
{
|
||||
"config": {
|
||||
"default": true,
|
||||
"MD013": false,
|
||||
"MD024": false,
|
||||
"MD033": false,
|
||||
"MD041": false
|
||||
},
|
||||
"globs": ["**/*.{md,mdx}"],
|
||||
"ignores": ["**/node_modules"]
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"default": true,
|
||||
"MD013": false,
|
||||
"MD024": false,
|
||||
"MD033": false,
|
||||
"MD041": false
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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}
|
||||
|
@ -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()
|
||||
})
|
||||
})
|
@ -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
|
||||
|
@ -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 (
|
||||
|
@ -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()
|
||||
})
|
||||
})
|
@ -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}`
|
||||
)
|
||||
})
|
||||
})
|
@ -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)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import classNames from 'classnames'
|
||||
import classNames from 'clsx'
|
||||
|
||||
type ShadowContainerProps = React.ComponentPropsWithRef<'div'>
|
||||
|
||||
|
20
cypress.config.ts
Normal 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'
|
||||
}
|
||||
}
|
||||
})
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"pluginsFile": false,
|
||||
"supportFile": false,
|
||||
"fixturesFolder": false,
|
||||
"video": false,
|
||||
"screenshotOnRunFailure": false
|
||||
}
|
18
cypress/component/Footer.cy.tsx
Normal 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 {}
|
17
cypress/component/utils/getAge.cy.ts
Normal 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)
|
||||
})
|
||||
})
|
||||
})
|
@ -56,3 +56,5 @@ describe('Common > Header', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
@ -5,3 +5,5 @@ describe('Page /404', () => {
|
||||
cy.get('[data-cy=status-code]').contains('404')
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
@ -5,3 +5,5 @@ describe('Page /500', () => {
|
||||
cy.get('[data-cy=status-code]').contains('500')
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
@ -11,3 +11,5 @@ describe('Page /blog/[slug]', () => {
|
||||
cy.get('[data-cy=status-code]').contains('404')
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
@ -20,3 +20,5 @@ describe('Page /blog', () => {
|
||||
.should('eq', '/blog/hello-world')
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
@ -17,3 +17,5 @@ describe('Page /', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
3
cypress/support/commands.ts
Normal file
@ -0,0 +1,3 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
export {}
|
14
cypress/support/component-index.html
Normal 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>
|
13
cypress/support/component.ts
Normal 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)
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"types": ["cypress"],
|
||||
"isolatedModules": false
|
||||
},
|
||||
"include": ["../node_modules/cypress", "./**/*.ts"]
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
version: '3.0'
|
||||
services:
|
||||
divlo.fr:
|
||||
container_name: ${COMPOSE_PROJECT_NAME}
|
||||
|
@ -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)
|
22
jsonresume-theme-custom/.gitignore
vendored
@ -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?
|
||||
|
Before Width: | Height: | Size: 1015 B After Width: | Height: | Size: 1015 B |
Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 986 B |
Before Width: | Height: | Size: 629 B After Width: | Height: | Size: 629 B |
Before Width: | Height: | Size: 912 B After Width: | Height: | Size: 912 B |
Before Width: | Height: | Size: 528 B After Width: | Height: | Size: 528 B |
@ -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>
|
@ -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' })
|
||||
}
|
5110
jsonresume-theme-custom/package-lock.json
generated
@ -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.6.2",
|
||||
"date-and-time": "2.4.1",
|
||||
"vite": "3.0.3",
|
||||
"vite-plugin-html": "3.2.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,19 @@
|
||||
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)
|
||||
})
|
||||
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
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
@import 'npm:modern-normalize/modern-normalize.css';
|
||||
@import 'modern-normalize/modern-normalize.css';
|
||||
|
||||
body {
|
||||
font-family: 'Montserrat', 'Arial', 'sans-serif';
|
33
jsonresume-theme-custom/vite.config.ts
Normal 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: {}
|
||||
}
|
||||
})
|
@ -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",
|
||||
|
@ -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\" (première 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\" (deuxième année)."
|
||||
},
|
||||
"interests": {
|
||||
"title": "Intérêts",
|
||||
|
20507
package-lock.json
generated
94
package.json
@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "divlo",
|
||||
"version": "2.3.0",
|
||||
"version": "2.3.2",
|
||||
"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.11",
|
||||
"@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",
|
||||
"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.1",
|
||||
"next": "12.2.2",
|
||||
"next-mdx-remote": "4.1.0",
|
||||
"next-pwa": "5.5.4",
|
||||
"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",
|
||||
"sharp": "0.30.7",
|
||||
"shiki": "0.10.1",
|
||||
"unified": "10.1.2",
|
||||
"unist-util-visit": "4.1.0",
|
||||
"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.6.2",
|
||||
"@types/react": "18.0.15",
|
||||
"@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.31.0",
|
||||
"autoprefixer": "10.4.7",
|
||||
"cypress": "10.3.1",
|
||||
"editorconfig-checker": "4.0.2",
|
||||
"eslint": "8.13.0",
|
||||
"eslint-config-conventions": "2.0.0",
|
||||
"eslint-config-next": "12.1.4",
|
||||
"eslint": "8.20.0",
|
||||
"eslint-config-conventions": "3.0.0",
|
||||
"eslint-config-next": "12.2.2",
|
||||
"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",
|
||||
"lint-staged": "13.0.3",
|
||||
"markdownlint-cli2": "0.5.0",
|
||||
"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",
|
||||
"postcss": "8.4.14",
|
||||
"prettier": "2.7.1",
|
||||
"prettier-plugin-tailwindcss": "0.1.13",
|
||||
"semantic-release": "19.0.3",
|
||||
"start-server-and-test": "1.14.0",
|
||||
"tailwindcss": "3.0.23",
|
||||
"typescript": "4.6.3",
|
||||
"vercel": "24.0.1"
|
||||
"tailwindcss": "3.1.6",
|
||||
"typescript": "4.7.4",
|
||||
"vercel": "27.3.0"
|
||||
}
|
||||
}
|
||||
|
0
public/curriculum-vitae/.gitkeep
Normal 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.",
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
@ -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'
|
||||
|