mirror of
https://github.com/theoludwig/theoludwig.git
synced 2024-12-08 00:44:30 +01:00
chore: maintenance
This commit is contained in:
parent
e5f4615f7f
commit
729e540d04
@ -1,14 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"next/babel",
|
||||
{
|
||||
"preset-env": {
|
||||
"targets": {
|
||||
"browsers": ">1%, not ie 11, not dead"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@ -1,7 +1,2 @@
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.163.1/containers/javascript-node/.devcontainer/base.Dockerfile
|
||||
|
||||
ARG VARIANT="14-buster"
|
||||
ARG VARIANT="16"
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
|
||||
|
||||
ARG EXTRA_NODE_VERSION=16
|
||||
RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
|
||||
|
@ -1,7 +1,8 @@
|
||||
.next
|
||||
.lighthouseci
|
||||
storybook-static
|
||||
coverage
|
||||
node_modules
|
||||
next-env.d.ts
|
||||
**/workbox-*.js
|
||||
**/sw.js
|
||||
.vercel
|
||||
|
@ -1,11 +1,6 @@
|
||||
{
|
||||
"extends": [
|
||||
"standard-with-typescript",
|
||||
"next",
|
||||
"next/core-web-vitals",
|
||||
"prettier"
|
||||
],
|
||||
"plugins": ["unicorn", "prettier"],
|
||||
"extends": ["conventions", "next/core-web-vitals", "prettier"],
|
||||
"plugins": ["prettier"],
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
@ -15,17 +10,6 @@
|
||||
"jest": true
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"unicorn/prefer-node-protocol": "error",
|
||||
"unicorn/prevent-abbreviations": [
|
||||
"error",
|
||||
{
|
||||
"replacements": {
|
||||
"props": {
|
||||
"properties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"prettier/prettier": "error"
|
||||
}
|
||||
}
|
||||
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
16
.github/dependabot.yml
vendored
16
.github/dependabot.yml
vendored
@ -1,16 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
- package-ecosystem: 'docker'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
# - package-ecosystem: 'npm'
|
||||
# directory: '/'
|
||||
# schedule:
|
||||
# interval: 'daily'
|
41
.github/workflows/Divlo.yml
vendored
41
.github/workflows/Divlo.yml
vendored
@ -32,7 +32,7 @@ jobs:
|
||||
- uses: 'actions/checkout@v2.4.0'
|
||||
|
||||
- name: 'Use Node.js'
|
||||
uses: 'actions/setup-node@v2.4.1'
|
||||
uses: 'actions/setup-node@v2.5.1'
|
||||
with:
|
||||
node-version: '16.x'
|
||||
cache: 'npm'
|
||||
@ -49,7 +49,7 @@ jobs:
|
||||
- uses: 'actions/checkout@v2.4.0'
|
||||
|
||||
- name: 'Use Node.js'
|
||||
uses: 'actions/setup-node@v2.4.1'
|
||||
uses: 'actions/setup-node@v2.5.1'
|
||||
with:
|
||||
node-version: '16.x'
|
||||
cache: 'npm'
|
||||
@ -57,24 +57,38 @@ jobs:
|
||||
- name: 'Install'
|
||||
run: 'npm install'
|
||||
|
||||
- run: 'npm run lint:commit -- --to "${{ github.sha }}"'
|
||||
- run: 'npm run lint:docker'
|
||||
- run: 'npm run lint:editorconfig'
|
||||
- run: 'npm run lint:markdown'
|
||||
- run: 'npm run lint:typescript'
|
||||
- name: 'lint:commit'
|
||||
run: 'npm run lint:commit -- --to "${{ github.sha }}"'
|
||||
|
||||
- name: 'dotenv-linter'
|
||||
- name: 'lint:editorconfig'
|
||||
run: 'npm run lint:editorconfig'
|
||||
|
||||
- name: 'lint:markdown'
|
||||
run: 'npm run lint:markdown'
|
||||
|
||||
- name: 'lint:typescript'
|
||||
run: 'npm run lint:typescript'
|
||||
|
||||
- name: 'lint:prettier'
|
||||
run: 'npm run lint:prettier'
|
||||
|
||||
- name: 'lint:dotenv'
|
||||
uses: 'dotenv-linter/action-dotenv-linter@v2'
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
|
||||
- name: 'lint:docker'
|
||||
uses: 'hadolint/hadolint-action@v1.6.0'
|
||||
with:
|
||||
dockerfile: './Dockerfile'
|
||||
|
||||
test-unit:
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- uses: 'actions/checkout@v2.4.0'
|
||||
|
||||
- name: 'Use Node.js'
|
||||
uses: 'actions/setup-node@v2.4.1'
|
||||
uses: 'actions/setup-node@v2.5.1'
|
||||
with:
|
||||
node-version: '16.x'
|
||||
cache: 'npm'
|
||||
@ -91,7 +105,7 @@ jobs:
|
||||
- uses: 'actions/checkout@v2.4.0'
|
||||
|
||||
- name: 'Use Node.js'
|
||||
uses: 'actions/setup-node@v2.4.1'
|
||||
uses: 'actions/setup-node@v2.5.1'
|
||||
with:
|
||||
node-version: '16.x'
|
||||
cache: 'npm'
|
||||
@ -102,6 +116,9 @@ jobs:
|
||||
- name: 'Build'
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'html-w3c-validator'
|
||||
run: 'npm run test:html-w3c-validator'
|
||||
|
||||
- name: 'Lighthouse'
|
||||
run: 'npm run test:lighthouse'
|
||||
env:
|
||||
@ -113,7 +130,7 @@ jobs:
|
||||
- uses: 'actions/checkout@v2.4.0'
|
||||
|
||||
- name: 'Use Node.js'
|
||||
uses: 'actions/setup-node@v2.4.1'
|
||||
uses: 'actions/setup-node@v2.5.1'
|
||||
with:
|
||||
node-version: '16.x'
|
||||
cache: 'npm'
|
||||
@ -145,7 +162,7 @@ jobs:
|
||||
git_commit_gpgsign: true
|
||||
|
||||
- name: 'Use Node.js'
|
||||
uses: 'actions/setup-node@v2.4.1'
|
||||
uses: 'actions/setup-node@v2.5.1'
|
||||
with:
|
||||
node-version: '16.x'
|
||||
cache: 'npm'
|
||||
|
7
.html-w3c-validatorrc.json
Normal file
7
.html-w3c-validatorrc.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"urls": [
|
||||
"http://localhost:3000/",
|
||||
"http://localhost:3000/blog",
|
||||
"http://localhost:3000/blog/hello-world"
|
||||
]
|
||||
}
|
@ -9,13 +9,16 @@
|
||||
"http://localhost:3000/blog",
|
||||
"http://localhost:3000/blog/hello-world"
|
||||
],
|
||||
"numberOfRuns": 3
|
||||
"numberOfRuns": 1
|
||||
},
|
||||
"assert": {
|
||||
"preset": "lighthouse:recommended",
|
||||
"assertions": {
|
||||
"csp-xss": "warning",
|
||||
"unused-javascript": "warning"
|
||||
"unused-javascript": "warning",
|
||||
"image-size-responsive": "warning",
|
||||
"unsized-images": "warning",
|
||||
"color-contrast": "warning"
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
|
@ -1,11 +1,10 @@
|
||||
{
|
||||
"*": ["editorconfig-checker"],
|
||||
"*.{js,ts,jsx,tsx}": [
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"prettier --write",
|
||||
"eslint --fix",
|
||||
"jest --findRelatedTests"
|
||||
],
|
||||
"*.{css,yml,json}": ["prettier --write"],
|
||||
"*.{md,mdx}": ["prettier --write", "markdownlint --dot --fix"],
|
||||
"./Dockerfile": ["dockerfilelint"]
|
||||
"*.{css,scss,sass,json,jsonc,yml,yaml}": ["prettier --write"],
|
||||
"*.{md,mdx}": ["prettier --write", "markdownlint --dot --fix"]
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
.next
|
||||
.lighthouseci
|
||||
storybook-static
|
||||
coverage
|
||||
node_modules
|
||||
next-env.d.ts
|
||||
package.json
|
||||
package-lock.json
|
||||
**/workbox-*.js
|
||||
**/sw.js
|
||||
.vercel
|
||||
*.hbs
|
||||
|
42
.vscode/settings.json
vendored
42
.vscode/settings.json
vendored
@ -1,48 +1,10 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"editor.bracketPairColorization.enabled": true,
|
||||
"prettier.configPath": ".prettierrc.json",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true
|
||||
},
|
||||
"[css]": {
|
||||
"editor.autoClosingBrackets": "always",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[sass]": {
|
||||
"editor.autoClosingBrackets": "always",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.autoClosingBrackets": "always",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.autoClosingBrackets": "always",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.autoClosingBrackets": "always",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.autoClosingBrackets": "always",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.autoClosingBrackets": "always",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.autoClosingBrackets": "always",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.autoClosingBrackets": "always",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.autoClosingBrackets": "always",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ Thanks a lot for your interest in contributing to **divlo.fr**! 🎉
|
||||
|
||||
- **Please first discuss** the change you wish to make via [issue](https://github.com/Divlo/Divlo/issues) before making a change. It might avoid a waste of your time.
|
||||
|
||||
- Ensure your code respect [Typescript Standard Style](https://www.npmjs.com/package/ts-standard).
|
||||
- Ensure your code respect linting.
|
||||
|
||||
- Make sure your **code passes the tests**.
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
FROM node:16.13.0 AS dependencies
|
||||
FROM node:16.14.0 AS dependencies
|
||||
WORKDIR /usr/src/app
|
||||
COPY ./package*.json ./
|
||||
RUN npm clean-install
|
||||
RUN npm install
|
||||
|
||||
FROM node:16.13.0 AS builder
|
||||
FROM node:16.14.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.13.0 AS runner
|
||||
FROM node:16.14.0 AS runner
|
||||
WORKDIR /usr/src/app
|
||||
ENV NODE_ENV=production
|
||||
COPY --from=builder /usr/src/app/next.config.js ./next.config.js
|
||||
|
@ -12,7 +12,7 @@ export const ErrorPage: React.FC<ErrorPageProps> = (props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className='my-6 font-semibold text-4xl'>
|
||||
<h1 className='my-6 text-4xl font-semibold'>
|
||||
{t('errors:error')}{' '}
|
||||
<span
|
||||
className='text-yellow dark:text-yellow-dark'
|
||||
@ -24,7 +24,7 @@ export const ErrorPage: React.FC<ErrorPageProps> = (props) => {
|
||||
<p className='text-center text-lg'>
|
||||
{message}{' '}
|
||||
<Link href='/'>
|
||||
<a className='text-yellow dark:text-yellow-dark hover:underline'>
|
||||
<a className='text-yellow hover:underline dark:text-yellow-dark'>
|
||||
{t('errors:return-to-home-page')}
|
||||
</a>
|
||||
</Link>
|
||||
|
@ -15,10 +15,10 @@ export const Footer: React.FC<FooterProps> = (props) => {
|
||||
}, [version])
|
||||
|
||||
return (
|
||||
<footer className='bg-white flex flex-col items-center justify-center py-6 text-lg border-t-2 border-gray-600 dark:border-gray-400 dark:bg-black'>
|
||||
<footer className='flex flex-col items-center justify-center border-t-2 border-gray-600 bg-white py-6 text-lg dark:border-gray-400 dark:bg-black'>
|
||||
<p>
|
||||
<Link href='/'>
|
||||
<a className='hover:underline text-yellow dark:text-yellow-dark'>
|
||||
<a className='text-yellow hover:underline dark:text-yellow-dark'>
|
||||
Divlo
|
||||
</a>
|
||||
</Link>{' '}
|
||||
@ -27,7 +27,7 @@ export const Footer: React.FC<FooterProps> = (props) => {
|
||||
<p className='mt-1'>
|
||||
Version{' '}
|
||||
<a
|
||||
className='hover:underline text-yellow dark:text-yellow-dark'
|
||||
className='text-yellow hover:underline dark:text-yellow-dark'
|
||||
href={versionLink}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
|
@ -3,7 +3,7 @@ import NextHead from 'next/head'
|
||||
interface HeadProps {
|
||||
title?: string
|
||||
image?: string
|
||||
description?: string
|
||||
description: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ export const Head: React.FC<HeadProps> = (props) => {
|
||||
const {
|
||||
title = 'Divlo',
|
||||
image = '/images/icons/icon-96x96.png',
|
||||
description = "I'm Divlo, I'm 18 years old, I'm from France - Developer Full Stack Junior • Passionate about High-Tech",
|
||||
description,
|
||||
url = 'https://divlo.fr/'
|
||||
} = props
|
||||
|
||||
|
@ -3,9 +3,10 @@ import useTranslation from 'next-translate/useTranslation'
|
||||
import setLanguage from 'next-translate/setLanguage'
|
||||
import classNames from 'classnames'
|
||||
|
||||
import i18n from 'i18n.json'
|
||||
|
||||
import { Arrow } from './Arrow'
|
||||
import { LanguageFlag } from './LanguageFlag'
|
||||
import i18n from 'i18n.json'
|
||||
|
||||
export const Language: React.FC = () => {
|
||||
const { lang: currentLanguage } = useTranslation()
|
||||
@ -33,10 +34,10 @@ export const Language: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex flex-col justify-center items-center cursor-pointer'>
|
||||
<div className='flex cursor-pointer flex-col items-center justify-center'>
|
||||
<div
|
||||
data-cy='language-click'
|
||||
className='flex items-center mr-5'
|
||||
className='mr-5 flex items-center'
|
||||
onClick={handleHiddenMenu}
|
||||
>
|
||||
<LanguageFlag language={currentLanguage} />
|
||||
@ -46,7 +47,7 @@ export const Language: React.FC = () => {
|
||||
<ul
|
||||
data-cy='languages-list'
|
||||
className={classNames(
|
||||
'flex flex-col justify-center items-center absolute p-0 top-14 z-10 w-24 mt-3 mr-4 rounded-lg list-none shadow-light dark:shadow-dark bg-white dark:bg-black',
|
||||
'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-light dark:bg-black dark:shadow-dark',
|
||||
{ hidden: hiddenMenu }
|
||||
)}
|
||||
>
|
||||
@ -57,7 +58,7 @@ export const Language: React.FC = () => {
|
||||
return (
|
||||
<li
|
||||
key={index}
|
||||
className='flex items-center justify-center w-full h-12 hover:bg-[#4f545c] hover:bg-opacity-20 pl-2'
|
||||
className='flex h-12 w-full items-center justify-center pl-2 hover:bg-[#4f545c] hover:bg-opacity-20'
|
||||
onClick={async () => await handleLanguage(language)}
|
||||
>
|
||||
<LanguageFlag language={language} />
|
||||
|
@ -24,13 +24,13 @@ export const SwitchTheme: React.FC = () => {
|
||||
data-cy='switch-theme-click'
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div className='toggle-theme-button relative cursor-pointer bg-transparent inline-block'>
|
||||
<div className='toggle-theme-button relative inline-block cursor-pointer bg-transparent'>
|
||||
<div className='toggle-track'>
|
||||
<div
|
||||
data-cy='switch-theme-dark'
|
||||
className='toggle-track-check absolute'
|
||||
>
|
||||
<span className='toggle_Dark flex justify-center items-center relative'>
|
||||
<span className='toggle_Dark relative flex items-center justify-center'>
|
||||
🌜
|
||||
</span>
|
||||
</div>
|
||||
@ -38,7 +38,7 @@ export const SwitchTheme: React.FC = () => {
|
||||
data-cy='switch-theme-light'
|
||||
className='toggle-track-x absolute'
|
||||
>
|
||||
<span className='toggle_Light flex justify-center items-center relative'>
|
||||
<span className='toggle_Light relative flex items-center justify-center'>
|
||||
🌞
|
||||
</span>
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@ import { render } from '@testing-library/react'
|
||||
import { Header } from '..'
|
||||
|
||||
describe('<Header />', () => {
|
||||
it('should render', async () => {
|
||||
it('should render', () => {
|
||||
const { getByText } = render(<Header />)
|
||||
expect(getByText('Divlo')).toBeInTheDocument()
|
||||
})
|
||||
|
@ -12,7 +12,7 @@ export const Header: React.FC<HeaderProps> = (props) => {
|
||||
const { showLanguage = false } = props
|
||||
|
||||
return (
|
||||
<header className='bg-white sticky top-0 z-50 flex w-full justify-between px-6 py-2 border-b-2 border-gray-600 dark:border-gray-400 dark:bg-black'>
|
||||
<header className='sticky top-0 z-50 flex w-full justify-between border-b-2 border-gray-600 bg-white px-6 py-2 dark:border-gray-400 dark:bg-black'>
|
||||
<Link href='/'>
|
||||
<a>
|
||||
<div className='flex items-center justify-center'>
|
||||
@ -22,18 +22,18 @@ export const Header: React.FC<HeaderProps> = (props) => {
|
||||
src='/images/divlo_icon_small.png'
|
||||
alt='Divlo'
|
||||
/>
|
||||
<strong className='ml-1 font-headline font-semibold hidden xs:block text-yellow dark:text-yellow-dark'>
|
||||
<strong className='ml-1 hidden font-headline font-semibold text-yellow dark:text-yellow-dark xs:block'>
|
||||
Divlo
|
||||
</strong>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className='flex justify-between'>
|
||||
<div className='flex flex-col justify-center items-center px-6'>
|
||||
<div className='flex flex-col items-center justify-center px-6'>
|
||||
<Link href='/blog'>
|
||||
<a
|
||||
data-cy='header-blog-link'
|
||||
className='text-yellow dark:text-yellow-dark hover:underline'
|
||||
className='text-yellow hover:underline dark:text-yellow-dark'
|
||||
>
|
||||
Blog
|
||||
</a>
|
||||
|
@ -10,8 +10,8 @@ export const InterestParagraph: React.FC<InterestParagraphProps> = (props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className='text-center my-6 text-gray dark:text-gray-dark'>
|
||||
<strong className='text-yellow font-semibold text-lg dark:text-yellow-dark'>
|
||||
<p className='my-6 text-center text-gray dark:text-gray-dark'>
|
||||
<strong className='text-lg font-semibold text-yellow dark:text-yellow-dark'>
|
||||
{title}
|
||||
</strong>
|
||||
<br />
|
||||
|
@ -10,9 +10,9 @@ export const InterestItem: React.FC<InterestItemProps> = (props) => {
|
||||
const { fontAwesomeIcon, title } = props
|
||||
|
||||
return (
|
||||
<li className='interest-item my-2 mx-2 w-8 h-8' title={title}>
|
||||
<li className='interest-item my-2 mx-2 h-8 w-8' title={title}>
|
||||
<FontAwesomeIcon
|
||||
className='text-yellow cursor-pointer h-full w-full block dark:text-yellow-dark'
|
||||
className='block h-full w-full cursor-pointer text-yellow dark:text-yellow-dark'
|
||||
icon={fontAwesomeIcon}
|
||||
/>
|
||||
</li>
|
||||
|
@ -5,8 +5,8 @@ import { InterestItem } from './InterestItem'
|
||||
|
||||
export const InterestsList: React.FC = () => {
|
||||
return (
|
||||
<div className='flex justify-center my-4'>
|
||||
<ul className='flex justify-around p-0 m-0 list-none w-96'>
|
||||
<div className='my-4 flex justify-center'>
|
||||
<ul className='m-0 flex w-96 list-none justify-around p-0'>
|
||||
<InterestItem
|
||||
title='Developer Full Stack Junior'
|
||||
fontAwesomeIcon={faCode}
|
||||
|
@ -11,10 +11,10 @@ export const Repository: React.FC<RepositoryProps> = (props) => {
|
||||
const { name, description, href } = props
|
||||
|
||||
return (
|
||||
<ShadowContainer className='cursor-pointer relative p-6 !mb-4 max-h-32 transition-transform duration-200 ease-in-out hover:-translate-y-2'>
|
||||
<ShadowContainer className='relative !mb-4 max-h-32 cursor-pointer p-6 transition-transform duration-200 ease-in-out hover:-translate-y-2'>
|
||||
<a href={href} target='_blank' rel='noopener noreferrer'>
|
||||
<div className='flex'>
|
||||
<GitHubIcon className='h-6 mr-2' />
|
||||
<GitHubIcon className='mr-2 h-6' />
|
||||
<span className='text-yellow dark:text-yellow-dark'>{name}</span>
|
||||
</div>
|
||||
<p className='my-4'>{description}</p>
|
||||
|
@ -6,13 +6,12 @@ export const OpenSource: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='max-w-full mt-0 flex flex-col items-center'>
|
||||
<div className='mt-0 flex max-w-full flex-col items-center'>
|
||||
<p className='text-center'>{t('home:open-source.description')}</p>
|
||||
<div className='grid grid-cols-1 md:w-10/12 md:grid-cols-2 gap-6 my-6'>
|
||||
<div className='my-6 grid grid-cols-1 gap-6 md:w-10/12 md:grid-cols-2'>
|
||||
<Repository
|
||||
name='nodejs/node'
|
||||
description='Node.js JavaScript runtime ✨️🐢🚀✨️'
|
||||
description='Node.js JavaScript runtime 🐢🚀'
|
||||
href='https://github.com/nodejs/node/commits?author=Divlo'
|
||||
/>
|
||||
<Repository
|
||||
@ -26,22 +25,11 @@ export const OpenSource: React.FC = () => {
|
||||
href='https://github.com/nrwl/nx/commits?author=Divlo'
|
||||
/>
|
||||
<Repository
|
||||
name='vercel/styled-jsx'
|
||||
description='Full CSS support for JSX without compromises'
|
||||
href='https://github.com/vercel/styled-jsx/commits?author=Divlo'
|
||||
name='vercel/next.js'
|
||||
description='The React Framework for Production'
|
||||
href='https://github.com/vercel/next.js/commits?author=Divlo'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style jsx global>{`
|
||||
.animation-custom {
|
||||
position: relative;
|
||||
transition: all 0.3s ease 0s;
|
||||
}
|
||||
.animation-custom:hover {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ShadowContainer } from 'components/design/ShadowContainer'
|
||||
import Image from 'next/image'
|
||||
|
||||
import { ShadowContainer } from 'components/design/ShadowContainer'
|
||||
|
||||
export interface PortfolioItemProps {
|
||||
title: string
|
||||
description: string
|
||||
@ -12,7 +13,7 @@ export const PortfolioItem: React.FC<PortfolioItemProps> = (props) => {
|
||||
const { title, description, link, image } = props
|
||||
|
||||
return (
|
||||
<ShadowContainer className='cursor-pointer relative items-center sm:ml-10'>
|
||||
<ShadowContainer className='relative cursor-pointer items-center sm:ml-10'>
|
||||
<a
|
||||
className='group inline-flex justify-center'
|
||||
target='_blank'
|
||||
@ -29,8 +30,8 @@ export const PortfolioItem: React.FC<PortfolioItemProps> = (props) => {
|
||||
alt={title}
|
||||
/>
|
||||
</div>
|
||||
<div className='opacity-0 transition-opacity duration-500 h-auto absolute text-center overflow-hidden bottom-0 group-hover:opacity-100'>
|
||||
<h3 className='text-yellow text-xl font-semibold my-6 dark:text-yellow-dark'>
|
||||
<div className='absolute bottom-0 h-auto overflow-hidden text-center opacity-0 transition-opacity duration-500 group-hover:opacity-100'>
|
||||
<h3 className='my-6 text-xl font-semibold text-yellow dark:text-yellow-dark'>
|
||||
{title}
|
||||
</h3>
|
||||
<p className='my-6'>{description}</p>
|
||||
|
@ -14,7 +14,7 @@ export const Portfolio: React.FC = () => {
|
||||
)
|
||||
|
||||
return (
|
||||
<div className='flex flex-wrap justify-center px-3 w-full'>
|
||||
<div className='flex w-full flex-wrap justify-center px-3'>
|
||||
{items.map((item, index) => {
|
||||
return <PortfolioItem key={index} {...item} />
|
||||
})}
|
||||
|
@ -2,7 +2,7 @@ import Translation from 'next-translate/Trans'
|
||||
|
||||
export const ProfileDescriptionBottom: React.FC = () => {
|
||||
return (
|
||||
<p className='mt-8 mb-8 font-normal text-base text-gray dark:text-gray-dark'>
|
||||
<p className='mt-8 mb-8 text-base font-normal text-gray dark:text-gray-dark'>
|
||||
<Translation
|
||||
i18nKey='home:about.description-bottom'
|
||||
components={[<br key='break' />]}
|
||||
|
@ -4,14 +4,14 @@ export const ProfileInformation: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className='pb-2 mb-6 border-b-2 font-headline border-gray-600 dark:border-gray-400'>
|
||||
<h1 className='text-4xl mb-2'>
|
||||
<div className='mb-6 border-b-2 border-gray-600 pb-2 font-headline dark:border-gray-400'>
|
||||
<h1 className='mb-2 text-4xl'>
|
||||
{t('home:about.i-am')}{' '}
|
||||
<strong className='font-semibold text-yellow dark:text-yellow-dark'>
|
||||
Divlo
|
||||
</strong>
|
||||
</h1>
|
||||
<h2 className='text-base mb-3'>{t('home:about.description')}</h2>
|
||||
<h2 className='mb-3 text-base'>{t('home:about.description')}</h2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -8,15 +8,14 @@ export const ProfileItem: React.FC<ProfileItemProps> = (props) => {
|
||||
const { title, value, link } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<li className='profile-list__item'>
|
||||
<strong className='profile-list__item-title text-black dark:text-white'>
|
||||
<li className='mb-3 before:table after:clear-both after:table'>
|
||||
<strong className='float-left block w-28 text-xs font-bold uppercase text-black dark:text-white'>
|
||||
{title}
|
||||
</strong>
|
||||
<span className='profile-list__item-info text-gray dark:text-gray-dark'>
|
||||
<span className='profile-list__item-info ml-0 mb-4 block text-sm font-normal text-gray dark:text-gray-dark sm:mb-0 sm:ml-32'>
|
||||
{link != null ? (
|
||||
<a
|
||||
className='text-gray dark:text-gray-dark hover:underline'
|
||||
className='text-gray hover:underline dark:text-gray-dark'
|
||||
href={link}
|
||||
>
|
||||
{value}
|
||||
@ -26,54 +25,5 @@ export const ProfileItem: React.FC<ProfileItemProps> = (props) => {
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.profile-list__item {
|
||||
margin-bottom: 13px;
|
||||
}
|
||||
.profile-list__item::after,
|
||||
.profile-list__item::before {
|
||||
content: ' ';
|
||||
display: table;
|
||||
}
|
||||
.profile-list__item::after {
|
||||
clear: both;
|
||||
}
|
||||
.profile-list__item-title {
|
||||
display: block;
|
||||
width: 120px;
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 20px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.profile-list__item-info {
|
||||
display: block;
|
||||
margin-left: 125px;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.profile-list__item-title {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.profile-list__item-info {
|
||||
margin-left: 0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.profile-list__item-info,
|
||||
.profile-list__item-title {
|
||||
width: 100%;
|
||||
float: none;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ export const ProfileList: React.FC = () => {
|
||||
const { t } = useTranslation('home')
|
||||
|
||||
return (
|
||||
<ul className='m-0 p-0 list-none'>
|
||||
<ul className='m-0 list-none p-0'>
|
||||
<ProfileItem title={t('home:about.birth-date')} value='31/03/2003' />
|
||||
<ProfileItem title={t('home:about.nationality')} value='Alsace, France' />
|
||||
<ProfileItem
|
||||
|
@ -4,8 +4,8 @@ import DivloLogo from 'public/images/divlo_logo.png'
|
||||
|
||||
export const ProfileLogo: React.FC = () => {
|
||||
return (
|
||||
<div className='px-2 py-6 max-w-[370px] max-h-[370px]'>
|
||||
<Image src={DivloLogo} alt='Divlo' priority />
|
||||
<div className='max-h-[370px] max-w-[370px] px-2 py-6'>
|
||||
<Image src={DivloLogo} alt='Divlo' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ export const Icon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 24 24'
|
||||
className={classNames(
|
||||
'dark:text-white text-black w-8 h-8 fill-current',
|
||||
'h-8 w-8 fill-current text-black dark:text-white',
|
||||
className
|
||||
)}
|
||||
{...rest}
|
||||
|
@ -7,7 +7,7 @@ export const SocialMediaItem: React.FC<SocialMediaItemProps> = (props) => {
|
||||
const { link, ariaLabel, children } = props
|
||||
|
||||
return (
|
||||
<li className='inline-block mx-4 my-1'>
|
||||
<li className='mx-4 my-1 inline-block'>
|
||||
<a
|
||||
href={link}
|
||||
aria-label={ariaLabel}
|
||||
|
@ -9,7 +9,7 @@ import { NPMIcon } from './SocialMediaIcons/NPMIcon'
|
||||
|
||||
export const SocialMediaList: React.FC = () => {
|
||||
return (
|
||||
<ul className='social-media-list m-0 mt-2 py-4 list-none text-center'>
|
||||
<ul className='social-media-list m-0 mt-2 list-none py-4 text-center'>
|
||||
<SocialMediaItem link='https://github.com/Divlo' ariaLabel='GitHub'>
|
||||
<GitHubIcon />
|
||||
</SocialMediaItem>
|
||||
|
@ -5,7 +5,7 @@ import { ProfileLogo } from './ProfileLogo'
|
||||
|
||||
export const Profile: React.FC = () => {
|
||||
return (
|
||||
<div className='flex flex-col justify-center items-center px-10 pt-2 md:pt-10 md:flex-row'>
|
||||
<div className='flex flex-col items-center justify-center px-10 pt-2 md:flex-row md:pt-10'>
|
||||
<ProfileLogo />
|
||||
<div>
|
||||
<ProfileInformation />
|
||||
|
@ -10,15 +10,15 @@ export const SkillsSection: React.FC<SkillsSectionProps> = (props) => {
|
||||
|
||||
return (
|
||||
<ShadowContainer>
|
||||
<div className='w-full px-4 mx-auto'>
|
||||
<div className='mx-auto w-full px-4'>
|
||||
<div className='flex flex-wrap px-4 py-6'>
|
||||
<div className='flex-1'>
|
||||
<div className='mb-8 border-b border-gray-600 dark:border-opacity-10 dark:border-white'>
|
||||
<h3 className='text-yellow font-semibold text-xl my-3 dark:text-yellow-dark'>
|
||||
<div className='mb-8 border-b border-gray-600 dark:border-white dark:border-opacity-10'>
|
||||
<h3 className='my-3 text-xl font-semibold text-yellow dark:text-yellow-dark'>
|
||||
{title}
|
||||
</h3>
|
||||
</div>
|
||||
<div className='flex justify-around flex-wrap'>{children}</div>
|
||||
<div className='flex flex-wrap justify-around'>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@ import { render } from '@testing-library/react'
|
||||
import { ErrorPage } from '../ErrorPage'
|
||||
|
||||
describe('<ErrorPage />', () => {
|
||||
it('should render the message and statusCode', async () => {
|
||||
it('should render the message and statusCode', () => {
|
||||
const messageContent = 'message content'
|
||||
const statusCode = 404
|
||||
const { getByText } = render(
|
||||
|
@ -3,7 +3,7 @@ import { render } from '@testing-library/react'
|
||||
import { Footer } from '../Footer'
|
||||
|
||||
describe('<Footer />', () => {
|
||||
it('should render with appropriate link tag version', async () => {
|
||||
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
|
||||
|
@ -10,7 +10,8 @@ export const RevealFade: React.FC = (props) => {
|
||||
(entries, observer) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('reveal-visible')
|
||||
entry.target.className =
|
||||
'opacity-100 visible translate-y-0 transition-all duration-700 ease-in-out'
|
||||
observer.unobserve(entry.target)
|
||||
}
|
||||
})
|
||||
@ -25,26 +26,8 @@ export const RevealFade: React.FC = (props) => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={htmlElement} className='reveal'>
|
||||
<div ref={htmlElement} className='invisible -translate-y-7 opacity-0'>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(-30px);
|
||||
}
|
||||
.reveal-visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
transition: all 500ms ease-out 100ms;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ export const SectionHeading: React.FC<SectionHeadingProps> = (props) => {
|
||||
const { children, ...rest } = props
|
||||
|
||||
return (
|
||||
<h2 {...rest} className='text-4xl font-semibold text-center mt-1 mb-3'>
|
||||
<h2 {...rest} className='mt-1 mb-3 text-center text-4xl font-semibold'>
|
||||
{children}
|
||||
</h2>
|
||||
)
|
||||
|
@ -20,11 +20,11 @@ export const Section: React.FC<SectionProps> = (props) => {
|
||||
|
||||
if (isMain) {
|
||||
return (
|
||||
<div className='px-3 w-full'>
|
||||
<div className='w-full px-3'>
|
||||
<ShadowContainer style={{ marginTop: 50 }}>
|
||||
<section {...rest}>
|
||||
{heading != null && <SectionHeading>{heading}</SectionHeading>}
|
||||
<div className='px-3 w-full'>{children}</div>
|
||||
<div className='w-full px-3'>{children}</div>
|
||||
</section>
|
||||
</ShadowContainer>
|
||||
</div>
|
||||
@ -35,7 +35,7 @@ export const Section: React.FC<SectionProps> = (props) => {
|
||||
return (
|
||||
<section {...rest}>
|
||||
{heading != null && <SectionHeading>{heading}</SectionHeading>}
|
||||
<div className='px-3 w-full'>{children}</div>
|
||||
<div className='w-full px-3'>{children}</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@ -52,9 +52,9 @@ export const Section: React.FC<SectionProps> = (props) => {
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
<div className='px-3 w-full'>
|
||||
<div className='w-full px-3'>
|
||||
<ShadowContainer>
|
||||
<div className='px-16 py-4 leading-8 w-full'>{children}</div>
|
||||
<div className='w-full px-16 py-4 leading-8'>{children}</div>
|
||||
</ShadowContainer>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -9,7 +9,7 @@ export const ShadowContainer: React.FC<ShadowContainerProps> = (props) => {
|
||||
<>
|
||||
<div
|
||||
className={classNames(
|
||||
'shadow-container h-full max-w-full break-words',
|
||||
'shadow-container mb-12 h-full max-w-full break-words',
|
||||
className
|
||||
)}
|
||||
{...rest}
|
||||
@ -23,7 +23,6 @@ export const ShadowContainer: React.FC<ShadowContainerProps> = (props) => {
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
|
@ -2,8 +2,8 @@ describe('Page /blog/[slug]', () => {
|
||||
it('should displays the first blog post (`hello-world`)', () => {
|
||||
cy.visit('/blog/hello-world')
|
||||
cy.get('[data-cy=language-flag-text]').should('not.exist')
|
||||
cy.get('h1').should('have.text', 'Hello, world! 👋')
|
||||
cy.get('[data-cy=blog-post-date]').should('have.text', '06/11/2021')
|
||||
cy.get('h1').should('have.text', '👋 Hello, world!')
|
||||
cy.get('[data-cy=blog-post-date]').should('have.text', '06/10/2021')
|
||||
cy.get('.prose a').should('have.attr', 'target', '_blank')
|
||||
})
|
||||
|
||||
|
@ -3,7 +3,7 @@ describe('Page /blog', () => {
|
||||
cy.visit('/blog')
|
||||
cy.get('[data-cy=blog-posts] [data-cy=blog-post-title]')
|
||||
.last()
|
||||
.should('have.text', 'Hello, world! 👋')
|
||||
.should('have.text', '👋 Hello, world!')
|
||||
cy.get('[data-cy=blog-posts] [data-cy=blog-post-description]')
|
||||
.last()
|
||||
.should(
|
||||
@ -12,7 +12,7 @@ describe('Page /blog', () => {
|
||||
)
|
||||
cy.get('[data-cy=blog-posts] [data-cy=blog-post-date]')
|
||||
.last()
|
||||
.should('have.text', '06/11/2021')
|
||||
.should('have.text', '06/10/2021')
|
||||
})
|
||||
|
||||
it('should redirect the user to the right blog post', () => {
|
||||
|
14
jest.config.js
Normal file
14
jest.config.js
Normal file
@ -0,0 +1,14 @@
|
||||
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)
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"roots": ["<rootDir>"],
|
||||
"transform": {
|
||||
"^.+\\.(js|jsx|ts|tsx)$": "babel-jest"
|
||||
},
|
||||
"moduleDirectories": ["node_modules", "./"],
|
||||
"modulePathIgnorePatterns": ["<rootDir>/cypress"],
|
||||
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
|
||||
"testEnvironment": "jsdom",
|
||||
"setupFilesAfterEnv": [
|
||||
"@testing-library/jest-dom/extend-expect",
|
||||
"@testing-library/react"
|
||||
]
|
||||
}
|
@ -10,15 +10,15 @@
|
||||
"title": "Interests",
|
||||
"paragraphs": [
|
||||
{
|
||||
"title": "Developer Full Stack Junior :",
|
||||
"description": "Computer programming is my main hobby, I love it! <br/> Mostly web development for the moment but I'm programming some Python and others programming language too."
|
||||
"title": "Developer Full Stack Junior",
|
||||
"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": "Passionate about High-Tech",
|
||||
"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."
|
||||
},
|
||||
{
|
||||
"title": "Open-Source enthusiast :",
|
||||
"title": "Open-Source enthusiast",
|
||||
"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>."
|
||||
}
|
||||
]
|
||||
|
@ -10,15 +10,15 @@
|
||||
"title": "Intérêts",
|
||||
"paragraphs": [
|
||||
{
|
||||
"title": "Développeur Full Stack Junior :",
|
||||
"description": "La programmation informatique est mon loisir principal, j'adore! <br/> Principalement du développement Web pour le moment, mais je programme aussi du Python et d'autres langages de programmation."
|
||||
"title": "Développeur Full Stack Junior",
|
||||
"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": "Passionné de High-Tech",
|
||||
"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."
|
||||
},
|
||||
{
|
||||
"title": "Enthousiaste de l'Open-Source :",
|
||||
"title": "Enthousiaste de l'Open-Source",
|
||||
"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>."
|
||||
}
|
||||
]
|
||||
@ -39,7 +39,7 @@
|
||||
},
|
||||
{
|
||||
"title": "thream.divlo.fr",
|
||||
"description": "Votre plateforme open source pour rester proche de vos amis et communautés, parler, discuter, collaborer, partager et vous amuser.",
|
||||
"description": "Votre plateforme open source pour rester proche de vos amis et communautés, parler, discuter, collaborer, partager et amusez-vous.",
|
||||
"link": "https://thream.divlo.fr/",
|
||||
"image": "/images/portfolio/threamdivlofr.png"
|
||||
},
|
||||
|
1
next-env.d.ts
vendored
1
next-env.d.ts
vendored
@ -1,5 +1,4 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
|
@ -5,11 +5,12 @@ 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'
|
||||
},
|
||||
async headers() {
|
||||
headers() {
|
||||
return [
|
||||
{
|
||||
source: '/:path*',
|
||||
|
27755
package-lock.json
generated
27755
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
103
package.json
103
package.json
@ -16,85 +16,84 @@
|
||||
"build": "next build",
|
||||
"export": "next export",
|
||||
"lint:commit": "commitlint",
|
||||
"lint:docker": "dockerfilelint './Dockerfile'",
|
||||
"lint:editorconfig": "editorconfig-checker",
|
||||
"lint:markdown": "markdownlint '**/*.{md,mdx}' --dot --ignore node_modules",
|
||||
"lint:typescript": "eslint '**/*.{js,ts,jsx,tsx}'",
|
||||
"lint:markdown": "markdownlint \"**/*.{md,mdx}\" --dot --ignore-path \".gitignore\"",
|
||||
"lint:typescript": "eslint \"**/*.{js,jsx,ts,tsx}\"",
|
||||
"lint:prettier": "prettier \".\" --check",
|
||||
"lint:staged": "lint-staged",
|
||||
"test:unit": "jest",
|
||||
"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:e2e": "start-server-and-test \"start\" \"http://localhost:3000\" \"cypress run\"",
|
||||
"test:e2e:dev": "start-server-and-test \"dev\" \"http://localhost:3000\" \"cypress open\"",
|
||||
"release": "semantic-release",
|
||||
"deploy": "vercel",
|
||||
"postinstall": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/montserrat": "4.5.1",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.36",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||
"@fortawesome/react-fontawesome": "0.1.16",
|
||||
"@fontsource/montserrat": "4.5.5",
|
||||
"@fortawesome/fontawesome-svg-core": "1.3.0",
|
||||
"@fortawesome/free-brands-svg-icons": "6.0.0",
|
||||
"@fortawesome/free-solid-svg-icons": "6.0.0",
|
||||
"@fortawesome/react-fontawesome": "0.1.17",
|
||||
"classnames": "2.3.1",
|
||||
"date-and-time": "2.0.1",
|
||||
"esbuild": "0.13.12",
|
||||
"date-and-time": "2.1.2",
|
||||
"gray-matter": "4.0.3",
|
||||
"html-react-parser": "1.4.0",
|
||||
"next": "11.1.2",
|
||||
"next-mdx-remote": "3.0.8",
|
||||
"next-pwa": "5.4.0",
|
||||
"html-react-parser": "1.4.8",
|
||||
"next": "12.1.0",
|
||||
"next-mdx-remote": "4.0.0",
|
||||
"next-pwa": "5.4.4",
|
||||
"next-themes": "0.0.15",
|
||||
"next-translate": "1.2.0",
|
||||
"next-translate": "1.3.4",
|
||||
"prism-themes": "1.9.0",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"read-pkg": "7.0.0",
|
||||
"rehype-slug": "5.0.0",
|
||||
"read-pkg": "7.1.0",
|
||||
"rehype-slug": "5.0.1",
|
||||
"remark-gfm": "3.0.1",
|
||||
"remark-prism": "1.3.6",
|
||||
"sharp": "0.29.2",
|
||||
"sharp": "0.30.1",
|
||||
"universal-cookie": "4.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "14.1.0",
|
||||
"@commitlint/config-conventional": "14.1.0",
|
||||
"@lhci/cli": "0.8.2",
|
||||
"@saithodev/semantic-release-backmerge": "2.1.0",
|
||||
"@commitlint/cli": "16.2.1",
|
||||
"@commitlint/config-conventional": "16.2.1",
|
||||
"@lhci/cli": "0.9.0",
|
||||
"@saithodev/semantic-release-backmerge": "2.1.1",
|
||||
"@semantic-release/git": "10.0.1",
|
||||
"@tailwindcss/typography": "0.4.1",
|
||||
"@testing-library/jest-dom": "5.15.0",
|
||||
"@testing-library/react": "12.1.2",
|
||||
"@tailwindcss/typography": "0.5.2",
|
||||
"@testing-library/jest-dom": "5.16.2",
|
||||
"@testing-library/react": "12.1.3",
|
||||
"@types/date-and-time": "0.13.0",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/node": "16.11.6",
|
||||
"@types/react": "17.0.34",
|
||||
"@types/jest": "27.4.0",
|
||||
"@types/node": "17.0.18",
|
||||
"@types/react": "17.0.39",
|
||||
"@types/remark-prism": "1.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.33.0",
|
||||
"autoprefixer": "10.4.0",
|
||||
"babel-jest": "27.3.1",
|
||||
"cypress": "8.7.0",
|
||||
"dockerfilelint": "1.8.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.12.0",
|
||||
"autoprefixer": "10.4.2",
|
||||
"cypress": "9.5.0",
|
||||
"editorconfig-checker": "4.0.2",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-config-next": "11.1.2",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-config-standard-with-typescript": "21.0.1",
|
||||
"eslint-plugin-import": "2.25.2",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"eslint": "8.9.0",
|
||||
"eslint-config-next": "12.1.0",
|
||||
"eslint-config-prettier": "8.4.0",
|
||||
"eslint-config-conventions": "1.1.0",
|
||||
"eslint-plugin-import": "2.25.4",
|
||||
"eslint-plugin-prettier": "4.0.0",
|
||||
"eslint-plugin-promise": "5.1.1",
|
||||
"eslint-plugin-unicorn": "38.0.1",
|
||||
"eslint-plugin-promise": "6.0.0",
|
||||
"eslint-plugin-unicorn": "41.0.0",
|
||||
"husky": "7.0.4",
|
||||
"jest": "27.3.1",
|
||||
"lint-staged": "11.2.6",
|
||||
"markdownlint-cli": "0.29.0",
|
||||
"jest": "27.5.1",
|
||||
"lint-staged": "12.3.4",
|
||||
"markdownlint-cli": "0.31.1",
|
||||
"next-secure-headers": "2.2.0",
|
||||
"postcss": "8.3.11",
|
||||
"prettier": "2.4.1",
|
||||
"semantic-release": "18.0.0",
|
||||
"postcss": "8.4.6",
|
||||
"prettier": "2.5.1",
|
||||
"prettier-plugin-tailwindcss": "0.1.7",
|
||||
"semantic-release": "19.0.2",
|
||||
"html-w3c-validator": "1.0.0",
|
||||
"start-server-and-test": "1.14.0",
|
||||
"tailwindcss": "2.2.19",
|
||||
"typescript": "4.4.4",
|
||||
"vercel": "23.1.2"
|
||||
"tailwindcss": "3.0.23",
|
||||
"typescript": "4.5.5",
|
||||
"vercel": "24.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,23 @@
|
||||
import { GetStaticProps } from 'next'
|
||||
import { GetStaticProps, NextPage } from 'next'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
|
||||
import { ErrorPage } from 'components/ErrorPage'
|
||||
import { Head } from 'components/Head'
|
||||
import { Header } from 'components/Header'
|
||||
import { Footer, FooterProps } from 'components/Footer'
|
||||
import { getDefaultDescription } from 'utils/getDefaultDescription'
|
||||
|
||||
const Error404: React.FC<FooterProps> = (props) => {
|
||||
interface Error404Props extends FooterProps {
|
||||
description: string
|
||||
}
|
||||
|
||||
const Error404: NextPage<Error404Props> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
const { version } = props
|
||||
const { version, description } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title='404 | Divlo' />
|
||||
<Head title='404 | Divlo' description={description} />
|
||||
|
||||
<Header showLanguage />
|
||||
<main className='flex flex-col md:mx-auto md:max-w-4xl lg:max-w-7xl'>
|
||||
@ -26,7 +31,8 @@ const Error404: React.FC<FooterProps> = (props) => {
|
||||
export const getStaticProps: GetStaticProps<FooterProps> = async () => {
|
||||
const { readPackage } = await import('read-pkg')
|
||||
const { version } = await readPackage()
|
||||
return { props: { version } }
|
||||
const description = getDefaultDescription()
|
||||
return { props: { version, description } }
|
||||
}
|
||||
|
||||
export default Error404
|
||||
|
@ -1,18 +1,23 @@
|
||||
import { GetStaticProps } from 'next'
|
||||
import { GetStaticProps, NextPage } from 'next'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
|
||||
import { ErrorPage } from 'components/ErrorPage'
|
||||
import { Head } from 'components/Head'
|
||||
import { Header } from 'components/Header'
|
||||
import { Footer, FooterProps } from 'components/Footer'
|
||||
import { getDefaultDescription } from 'utils/getDefaultDescription'
|
||||
|
||||
const Error500: React.FC<FooterProps> = (props) => {
|
||||
interface Error500Props extends FooterProps {
|
||||
description: string
|
||||
}
|
||||
|
||||
const Error500: NextPage<Error500Props> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
const { version } = props
|
||||
const { version, description } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title='500 | Divlo' />
|
||||
<Head title='500 | Divlo' description={description} />
|
||||
|
||||
<Header showLanguage />
|
||||
<main className='flex flex-col md:mx-auto md:max-w-4xl lg:max-w-7xl'>
|
||||
@ -26,7 +31,8 @@ const Error500: React.FC<FooterProps> = (props) => {
|
||||
export const getStaticProps: GetStaticProps<FooterProps> = async () => {
|
||||
const { readPackage } = await import('read-pkg')
|
||||
const { version } = await readPackage()
|
||||
return { props: { version } }
|
||||
const description = getDefaultDescription()
|
||||
return { props: { version, description } }
|
||||
}
|
||||
|
||||
export default Error500
|
||||
|
@ -4,7 +4,7 @@ const Document: React.FC = () => {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<body className='bg-white dark:bg-black text-black dark:text-white font-headline'>
|
||||
<body className='bg-white font-headline text-black dark:bg-black dark:text-white'>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { GetStaticProps, GetStaticPaths } from 'next'
|
||||
import { GetStaticProps, GetStaticPaths, NextPage } from 'next'
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
import date from 'date-and-time'
|
||||
import 'prism-themes/themes/prism-one-dark.css'
|
||||
@ -12,7 +12,7 @@ interface BlogPostPageProps extends FooterProps {
|
||||
post: Post
|
||||
}
|
||||
|
||||
const BlogPostPage: React.FC<BlogPostPageProps> = (props) => {
|
||||
const BlogPostPage: NextPage<BlogPostPageProps> = (props) => {
|
||||
const { version, post } = props
|
||||
|
||||
return (
|
||||
@ -23,8 +23,8 @@ const BlogPostPage: React.FC<BlogPostPageProps> = (props) => {
|
||||
/>
|
||||
|
||||
<Header />
|
||||
<main className='flex flex-col flex-wrap flex-1 items-center'>
|
||||
<div className='flex flex-col items-center my-10'>
|
||||
<main className='flex flex-1 flex-col flex-wrap items-center'>
|
||||
<div className='my-10 flex flex-col items-center'>
|
||||
<h1 className='text-3xl font-semibold'>{post.frontmatter.title}</h1>
|
||||
<p className='mt-2' data-cy='blog-post-date'>
|
||||
{date.format(new Date(post.frontmatter.publishedOn), 'DD/MM/YYYY')}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { GetStaticProps } from 'next'
|
||||
import { GetStaticProps, NextPage } from 'next'
|
||||
import Link from 'next/link'
|
||||
import date from 'date-and-time'
|
||||
|
||||
@ -15,7 +15,7 @@ interface BlogPageProps extends FooterProps {
|
||||
posts: PostMetadata[]
|
||||
}
|
||||
|
||||
const BlogPage: React.FC<BlogPageProps> = (props) => {
|
||||
const BlogPage: NextPage<BlogPageProps> = (props) => {
|
||||
const { version, posts } = props
|
||||
|
||||
return (
|
||||
@ -23,14 +23,14 @@ const BlogPage: React.FC<BlogPageProps> = (props) => {
|
||||
<Head title='Blog | Divlo' description={blogDescription} />
|
||||
|
||||
<Header />
|
||||
<main className='flex flex-col flex-wrap flex-1 items-center'>
|
||||
<div className='flex flex-col items-center mt-10'>
|
||||
<main className='flex flex-1 flex-col flex-wrap items-center'>
|
||||
<div className='mt-10 flex flex-col items-center'>
|
||||
<h1 className='text-4xl font-semibold'>Blog</h1>
|
||||
<p className='mt-6' data-cy='blog-post-date'>
|
||||
{blogDescription}
|
||||
</p>
|
||||
</div>
|
||||
<div className='w-full flex items-center justify-center p-8'>
|
||||
<div className='flex w-full items-center justify-center p-8'>
|
||||
<div className='w-[1600px]' data-cy='blog-posts'>
|
||||
{posts.map((post, index) => {
|
||||
const postPublishedOn = date.format(
|
||||
@ -40,7 +40,7 @@ const BlogPage: React.FC<BlogPageProps> = (props) => {
|
||||
return (
|
||||
<Link href={`/blog/${post.slug}`} key={index}>
|
||||
<a data-cy='blog-post'>
|
||||
<ShadowContainer className='p-6 cursor-pointer transition duration-200 ease-in-out hover:-translate-y-2'>
|
||||
<ShadowContainer className='cursor-pointer p-6 transition duration-200 ease-in-out hover:-translate-y-2'>
|
||||
<h2
|
||||
data-cy='blog-post-title'
|
||||
className='text-xl font-semibold'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { GetStaticProps } from 'next'
|
||||
import { GetStaticProps, NextPage } from 'next'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
|
||||
import { RevealFade } from 'components/design/RevealFade'
|
||||
@ -12,14 +12,19 @@ import { Skills } from 'components/Skills'
|
||||
import { OpenSource } from 'components/OpenSource'
|
||||
import { Header } from 'components/Header'
|
||||
import { Footer, FooterProps } from 'components/Footer'
|
||||
import { getDefaultDescription } from 'utils/getDefaultDescription'
|
||||
|
||||
const Home: React.FC<FooterProps> = (props) => {
|
||||
interface HomeProps extends FooterProps {
|
||||
description: string
|
||||
}
|
||||
|
||||
const Home: NextPage<HomeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
const { version } = props
|
||||
const { version, description } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head />
|
||||
<Head description={description} />
|
||||
|
||||
<Header showLanguage />
|
||||
<main className='flex flex-col md:mx-auto md:max-w-4xl lg:max-w-7xl'>
|
||||
@ -72,7 +77,8 @@ const Home: React.FC<FooterProps> = (props) => {
|
||||
export const getStaticProps: GetStaticProps<FooterProps> = async () => {
|
||||
const { readPackage } = await import('read-pkg')
|
||||
const { version } = await readPackage()
|
||||
return { props: { version } }
|
||||
const description = getDefaultDescription()
|
||||
return { props: { version, description } }
|
||||
}
|
||||
|
||||
export default Home
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: 'Clean Code 🧼'
|
||||
title: '🧼 Clean Code'
|
||||
description: 'What is "Clean Code", what are "Design Patterns", and why is it so important today ? Tips and tricks to make your code more readable and maintainable in the long term.'
|
||||
isPublished: false
|
||||
publishedOn: '2021-11-06T22:06:33.818Z'
|
||||
@ -17,9 +17,9 @@ Even if you know what it is about, this blog post will probably still be useful
|
||||
|
||||
A clean code is a code that is **easy** to **read** and easy to **understand**.
|
||||
|
||||
But I promise it is not a code that is easy to write, in fact it is really **hard** to **write Clean Code**.
|
||||
But I promise it is not a code that is easy to write, in fact it is really **hard to write Clean Code**.
|
||||
|
||||
We could ask ourselves, what is **easy** to **read** and **easy** to **understand** ?
|
||||
We could ask ourselves, what is **easy** to **read** and easy to **understand** ?
|
||||
|
||||
It depends of many factors, and is somewhat relative to each one of us. The **perfect** Clean code **doesn't exist**, but we can try to be **as perfect as possible**.
|
||||
|
||||
@ -171,7 +171,7 @@ We have to keep it as simple as possible, not to implement features that are not
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
const createFile = async (name: string) => {
|
||||
const createFile = async (name: string, isTemporary: boolean = false) => {
|
||||
if (isTemporary) {
|
||||
return await fs.promises.writeFile(path.join('temporary', name), '')
|
||||
}
|
||||
@ -187,7 +187,7 @@ const createFile = async (name: string) => {
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
const createFile = async (name: string, isTemporary: boolean = false) => {
|
||||
const createFile = async (name: string) => {
|
||||
await fs.promises.writeFile(name, '')
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ Here we are creating a new variable `isSubscriptionActive` that allows us to avo
|
||||
|
||||
## Conclusion
|
||||
|
||||
We can't write the perfect clean code understandable by everyone but we can **write code that is as perfect as possible to ease maintaibility** of the code by others developers (and for yourself).
|
||||
We can't write the perfect clean code understandable by everyone but we can **write code that is as perfect as possible to ease maintaibility** for yourself and others developers.
|
||||
|
||||
## Sources
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
---
|
||||
title: 'Hello, world! 👋'
|
||||
title: '👋 Hello, world!'
|
||||
description: 'First post of the blog, introduction and explanation of how this blog is made.'
|
||||
isPublished: true
|
||||
publishedOn: '2021-11-06T22:06:33.818Z'
|
||||
publishedOn: '2021-10-06T22:06:33.818Z'
|
||||
---
|
||||
|
||||
Hello, world! 👋
|
||||
@ -15,7 +15,7 @@ The idea is that I will share my knowledge with you (readers), and hopefully hel
|
||||
|
||||
Keep in mind that I will not translate the posts in French, all the posts will be written in English, as I'm not a native English speaker, I will probably make mistakes, feel free to open pull requests on [GitHub](https://github.com/Divlo/Divlo) to correct them. 😊
|
||||
|
||||
I don't plan to publish new posts regularly, but I will do so when I have something new to share.
|
||||
I plan to publish new posts when I have something new to share. There's no schedule, so stay tuned!
|
||||
|
||||
To stay informed of new blog post and to ask questions, feel free to follow me on Twitter: [@Divlo_FR](https://twitter.com/Divlo_FR).
|
||||
|
||||
@ -25,9 +25,9 @@ The blog posts subjects will be often related to the problems I encountered in t
|
||||
|
||||
Most of the time, when I am learning something new, I **learn it because I actually need it for a project**, I don't learn [React.js](https://reactjs.org) because it is trending, and everyone talks about it.
|
||||
|
||||
I learn it, because it solved a "real life" problem I had encountered. For example, [React.js](https://reactjs.org) allows to easily update the DOM (Document Object Model) in the browser, so we can add interactivity to our web pages, not only that, it allows to reuse multiple HTML (JSX) elements with components.
|
||||
I learn something new, because it solved a "real life" problem I had encountered. For example, [React.js](https://reactjs.org) allows to easily update the DOM (Document Object Model) in the browser, so we can add interactivity to our web pages, not only that, it allows to reuse multiple HTML (JSX) elements with components.
|
||||
|
||||
[React.js](https://reactjs.org) is only a example, but hopefully you understood my point: I often don't like too much theoretical thing, and enjoy much more practical things.
|
||||
[React.js](https://reactjs.org) is only an example, but hopefully you understood my point: I often don't like too much theoretical thing, and enjoy much more practical things.
|
||||
|
||||
## How this blog is made
|
||||
|
||||
@ -66,6 +66,6 @@ That not mean that theses features will never be implemented, but to avoid the n
|
||||
|
||||
## Conclusion
|
||||
|
||||
I hope you will enjoy my blog, and I hope you will find it useful.
|
||||
I hope you will enjoy my blog, and will find it useful.
|
||||
|
||||
See you in the next posts! 😊
|
||||
|
44
posts/mistakes-as-junior-developer.mdx
Normal file
44
posts/mistakes-as-junior-developer.mdx
Normal file
@ -0,0 +1,44 @@
|
||||
---
|
||||
title: '❌ Mistakes I made as a junior developer'
|
||||
description: 'Here are mistakes I made when I started, to prevent you from making the same mistakes.'
|
||||
isPublished: false
|
||||
publishedOn: '2021-12-06T22:06:33.818Z'
|
||||
---
|
||||
|
||||
Hello! 👋
|
||||
|
||||
I will explain some of my mistakes I made as a junior developer, so you can avoid doing them.
|
||||
|
||||
## 1. Skipped learning how to do automated tests
|
||||
|
||||
Probably one of the most common error junior developers do.
|
||||
|
||||
When you begin in programming, you learn a programming language, so you learn variables, conditions, loops, functions, etc.
|
||||
|
||||
With these concepts, you might start a new project, thinking that you will be able to do everything.
|
||||
|
||||
But as the project grows, you will end up using functions at multiple places in code, so if you change the behavior of a function, it will affect the whole project.
|
||||
|
||||
And because the code grows, you might do some refactoring, but because we are humans, we make mistakes, you could accidentally break the whole project even with a tiny change you thought was safe to do.
|
||||
|
||||
If you would have automated tests, you would have a way to know if you made a mistake even before deploying to production.
|
||||
|
||||
Depending on the programming language you are using, and what is the project you are working on, writing tests will be different.
|
||||
|
||||
Be aware that there are 3 main testing strategy:
|
||||
|
||||
- [Unit testing](https://en.wikipedia.org/wiki/Unit_testing)
|
||||
- [Integration testing](https://en.wikipedia.org/wiki/Integration_testing)
|
||||
- [End-to-end testing](https://en.wikipedia.org/wiki/End-to-end_testing)
|
||||
|
||||
After you learnt the basic of programming, learn how to write automated tests, it will save you a lot of time and debugging.
|
||||
|
||||
## 2. Thinking too big, with too much abstraction
|
||||
|
||||
Abstraction is great, but it can be harder to understand what is going on if actally don't need this abstraction.
|
||||
|
||||
Find the right balance, between abstraction and implementation, start simple, and then gradually improve and add more features.
|
||||
|
||||
When you start a new project, you should focus on the core of the project, not on the details, to release as soon as possible, a working usable version of your project also called a [**Minimum Viable Product** (MVP)](https://en.wikipedia.org/wiki/Minimum_viable_product).
|
||||
|
||||
## 3. Focusing on the thing that don't add value to a project
|
@ -3,11 +3,11 @@
|
||||
@tailwind utilities;
|
||||
|
||||
#__next {
|
||||
@apply flex flex-col h-screen;
|
||||
@apply flex h-screen flex-col;
|
||||
}
|
||||
|
||||
.prose {
|
||||
@apply text-gray dark:text-gray-dark !max-w-4xl;
|
||||
@apply !max-w-4xl text-gray dark:text-gray-300;
|
||||
}
|
||||
|
||||
.prose a,
|
||||
@ -20,7 +20,7 @@
|
||||
.prose h4,
|
||||
.prose h5,
|
||||
.prose h6 {
|
||||
@apply text-gray dark:text-gray-dark mt-1;
|
||||
@apply mt-1 text-gray dark:text-gray-dark;
|
||||
}
|
||||
|
||||
.prose code {
|
||||
|
@ -1,6 +1,8 @@
|
||||
module.exports = {
|
||||
mode: 'jit',
|
||||
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
|
||||
content: [
|
||||
'./pages/**/*.{js,ts,jsx,tsx}',
|
||||
'./components/**/*.{js,ts,jsx,tsx}'
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
@ -14,7 +16,7 @@ module.exports = {
|
||||
dark: '#b2bac2'
|
||||
},
|
||||
yellow: {
|
||||
DEFAULT: '#ff8000',
|
||||
DEFAULT: '#ff6000',
|
||||
dark: '#ffd800'
|
||||
}
|
||||
},
|
||||
@ -23,12 +25,12 @@ module.exports = {
|
||||
light: '0px 1px 10px rgba(0, 0, 0, 0.25)'
|
||||
},
|
||||
fontFamily: {
|
||||
headline: ['Montserrat', 'Arial', 'sans-serif']
|
||||
headline: "'Montserrat', 'Arial', 'sans-serif'"
|
||||
},
|
||||
typography: {
|
||||
DEFAULT: {
|
||||
css: {
|
||||
fontFamily: ['Montserrat', 'Arial', 'sans-serif'],
|
||||
fontFamily: "'Montserrat', 'Arial', 'sans-serif'",
|
||||
a: {
|
||||
textDecoration: 'none',
|
||||
'&:hover': {
|
||||
@ -41,8 +43,5 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
variants: {
|
||||
extend: {}
|
||||
},
|
||||
plugins: [require('@tailwindcss/typography')]
|
||||
}
|
||||
|
@ -17,7 +17,8 @@
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true
|
||||
"isolatedModules": true,
|
||||
"incremental": true
|
||||
},
|
||||
"exclude": ["dist", ".next", "out", "next.config.js"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
||||
|
15
utils/__test__/getAge.test.ts
Normal file
15
utils/__test__/getAge.test.ts
Normal file
@ -0,0 +1,15 @@
|
||||
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)
|
||||
})
|
||||
})
|
16
utils/getAge.ts
Normal file
16
utils/getAge.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export const DIVLO_BIRTHDAY = new Date('2003-03-31')
|
||||
|
||||
/**
|
||||
* Calculates the age of a person based on their birth date
|
||||
* @param birthDate
|
||||
* @returns
|
||||
*/
|
||||
export const getAge = (birthDate: Date): number => {
|
||||
const today = new Date()
|
||||
let age = today.getFullYear() - birthDate.getFullYear()
|
||||
const month = today.getMonth() - birthDate.getMonth()
|
||||
if (month < 0 || (month === 0 && today.getDate() < birthDate.getDate())) {
|
||||
age--
|
||||
}
|
||||
return age
|
||||
}
|
6
utils/getDefaultDescription.ts
Normal file
6
utils/getDefaultDescription.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { DIVLO_BIRTHDAY, getAge } from './getAge'
|
||||
|
||||
export const getDefaultDescription = (): string => {
|
||||
const age = getAge(DIVLO_BIRTHDAY)
|
||||
return `I'm Divlo, I'm ${age} years old, I'm from France - Developer Full Stack Junior • Passionate about High-Tech`
|
||||
}
|
Loading…
Reference in New Issue
Block a user