Merge pull request #10 from Divlo/release/v2.2

Release/v2.2
This commit is contained in:
Divlo 2020-10-30 17:28:58 +01:00 committed by GitHub
commit cbe82f74a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 5060 additions and 4518 deletions

View File

@ -5,10 +5,9 @@
</p> </p>
<p align="center"> <p align="center">
<a href="https://gitmoji.carloscuesta.me/"><img src="https://camo.githubusercontent.com/2a4924a23bd9ef18afe793f4999b1b9ec474e48f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6769746d6f6a692d253230f09f989c253230f09f988d2d4646444436372e7376673f7374796c653d666c61742d737175617265" alt="Gitmoji"/></a>
<a href="https://standardjs.com"><img alt="JavaScript Style Guide" src="https://img.shields.io/badge/code_style-standard-brightgreen.svg"/></a> <a href="https://standardjs.com"><img alt="JavaScript Style Guide" src="https://img.shields.io/badge/code_style-standard-brightgreen.svg"/></a>
<a href="https://conventionalcommits.org"><img src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg" alt="Conventional Commits" /></a>
<a href="./LICENSE"><img src="https://img.shields.io/badge/licence-MIT-blue.svg" alt="Licence MIT"/></a> <a href="./LICENSE"><img src="https://img.shields.io/badge/licence-MIT-blue.svg" alt="Licence MIT"/></a>
<img src="https://img.shields.io/github/stars/Divlo/FunctionProject?style=social" alt="Stars">
<br/> <br/> <br/> <br/>
<a href="https://function.divlo.fr/"><img src="https://raw.githubusercontent.com/Divlo/FunctionProject/master/.github/FunctionProject.png" alt="FunctionProject" /></a> <a href="https://function.divlo.fr/"><img src="https://raw.githubusercontent.com/Divlo/FunctionProject/master/.github/FunctionProject.png" alt="FunctionProject" /></a>
</p> </p>
@ -23,7 +22,7 @@ Si vous aimez le projet, vous pouvez aider à **le faire connaître** en utilisa
Les dernières versions publiées : [https://github.com/Divlo/FunctionProject/releases](https://github.com/Divlo/FunctionProject/releases) Les dernières versions publiées : [https://github.com/Divlo/FunctionProject/releases](https://github.com/Divlo/FunctionProject/releases)
Le projet est disponible sur [function.divlo.fr](https://function.divlo.fr/) (actuellement en version 2.1). Le projet est disponible sur [function.divlo.fr](https://function.divlo.fr/) (actuellement en version 2.2).
## 🚀 Open Source ## 🚀 Open Source
@ -39,8 +38,9 @@ Si vous voulez avoir les données des catégories et des fonctions, vous pouvez
### Prérequis : ### Prérequis :
- NodeJS (et npm) → version récente - [Node.js](https://nodejs.org/) >= 14
- Base de donnée MySQL → J'utilise Wamp ce qui me permet d'avoir phpmyadmin. - [npm](https://www.npmjs.com/) >= 6
- [MySQL](https://www.mysql.com/) >= 5.7
### Commandes (à suivre dans l'ordre) : ### Commandes (à suivre dans l'ordre) :
@ -62,6 +62,24 @@ Vous devrez ensuite configurer l'API en créant un fichier `.env` à la racine d
### Lancer l'environnement de développement : ### Lancer l'environnement de développement :
#### Avec [docker](https://www.docker.com/) :
```sh
# Setup and run all the services for you
docker-compose up --build
```
**Services started :**
- api : `http://localhost:8080`
- s.divlo.fr : `http://localhost:7000`
- website : `http://localhost:3000`
- [phpmyadmin](https://www.phpmyadmin.net/) : `http://localhost:8000`
- [MailDev](https://maildev.github.io/maildev/) : `http://localhost:1080`
- [MySQL database](https://www.mysql.com/) (with PORT 3006)
#### Sans docker :
Dans deux terminals séparés : Dans deux terminals séparés :
- Lancer le front-end en allant dans `/website` - Lancer le front-end en allant dans `/website`

2
api/.dockerignore Normal file
View File

@ -0,0 +1,2 @@
node_modules
build

View File

@ -2,11 +2,14 @@ HOST = "http://localhost:8080"
FRONT_END_HOST="http://localhost:3000" FRONT_END_HOST="http://localhost:3000"
OpenWeatherMap_API_KEY="" OpenWeatherMap_API_KEY=""
Scraper_API_KEY="" Scraper_API_KEY=""
DB_HOST = "" DATABASE_HOST="functionproject-database"
DB_NAME = "" DATABASE_NAME="functionproject"
DB_USER = "" DATABASE_USER="root"
DB_PASS = "" DATABASE_PASSWORD="password"
DATABASE_PORT=3306
JWT_SECRET="" JWT_SECRET=""
EMAIL_HOST = "" EMAIL_HOST="functionproject-maildev"
EMAIL_USER = "" EMAIL_USER="no-reply@functionproject.fr"
EMAIL_PASSWORD = "" EMAIL_PASSWORD="password"
EMAIL_PORT=25
COMPOSE_PROJECT_NAME="function.divlo.fr-api"

6
api/.gitignore vendored
View File

@ -11,14 +11,16 @@
# production # production
/build /build
# misc # envs
.DS_Store
.env .env
.env.local .env.local
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
.env.production .env.production
# misc
.DS_Store
/temp /temp
/assets/images/ /assets/images/

13
api/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM node:14.15.0-alpine3.12
WORKDIR /app
COPY ./package*.json ./
RUN npm install
COPY ./ ./
# docker-compose-wait
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.7.3/wait /wait
RUN chmod +x /wait
CMD /wait && npm run dev

View File

@ -6,6 +6,7 @@ const helmet = require('helmet')
const cors = require('cors') const cors = require('cors')
const morgan = require('morgan') const morgan = require('morgan')
const { redirectToHTTPS } = require('express-http-to-https') const { redirectToHTTPS } = require('express-http-to-https')
const rateLimit = require('express-rate-limit')
/* Files Imports & Variables */ /* Files Imports & Variables */
const sequelize = require('./assets/utils/database') const sequelize = require('./assets/utils/database')
@ -16,11 +17,27 @@ const isAdmin = require('./middlewares/isAdmin')
const app = express() const app = express()
/* Middlewares */ /* Middlewares */
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'))
} else if (process.env.NODE_ENV === 'production') {
app.use(redirectToHTTPS())
const requestPerSecond = 2
const seconds = 60
const windowMs = seconds * 1000
app.enable('trust proxy')
app.use(
rateLimit({
windowMs,
max: seconds * requestPerSecond,
handler: (_req, res) => {
return res.status(429).json({ message: 'Too many requests' })
}
})
)
}
app.use(helmet()) app.use(helmet())
app.use(cors()) app.use(cors())
app.use(morgan('dev'))
app.use(express.json()) app.use(express.json())
app.use(redirectToHTTPS([/localhost:(\d{4})/]))
/* Routes */ /* Routes */
app.use('/images', express.static(path.join(__dirname, 'assets', 'images'))) app.use('/images', express.static(path.join(__dirname, 'assets', 'images')))
@ -37,7 +54,7 @@ app.use('/links', require('./routes/links_shortener'))
/* Errors Handling */ /* Errors Handling */
app.use((_req, _res, next) => app.use((_req, _res, next) =>
errorHandling(next, { statusCode: 404, message: "La route n'existe pas!" }) errorHandling(next, { statusCode: 404, message: "La route n'existe pas!" })
) // 404 )
app.use((error, _req, res, _next) => { app.use((error, _req, res, _next) => {
console.log(error) console.log(error)
const { statusCode, message } = error const { statusCode, message } = error
@ -83,7 +100,6 @@ Users.hasMany(ShortLinks)
ShortLinks.belongsTo(Users, { constraints: false }) ShortLinks.belongsTo(Users, { constraints: false })
/* Server */ /* Server */
// sequelize.sync({ force: true })
sequelize sequelize
.sync() .sync()
.then(() => { .then(() => {

View File

@ -1,3 +1,8 @@
const dotenv = require('dotenv')
dotenv.config()
const EMAIL_PORT = parseInt(process.env.EMAIL_PORT ?? '465', 10)
const config = { const config = {
PORT: process.env.PORT || 8080, PORT: process.env.PORT || 8080,
HOST: process.env.HOST, HOST: process.env.HOST,
@ -5,20 +10,22 @@ const config = {
WEATHER_API_KEY: process.env.OpenWeatherMap_API_KEY, WEATHER_API_KEY: process.env.OpenWeatherMap_API_KEY,
SCRAPER_API_KEY: process.env.Scraper_API_KEY, SCRAPER_API_KEY: process.env.Scraper_API_KEY,
DATABASE: { DATABASE: {
host: process.env.DB_HOST, host: process.env.DATABASE_HOST,
name: process.env.DB_NAME, name: process.env.DATABASE_NAME,
user: process.env.DB_USER, user: process.env.DATABASE_USER,
password: process.env.DB_PASS password: process.env.DATABASE_PASSWORD,
port: parseInt(process.env.DATABASE_PORT ?? '3306', 10)
}, },
JWT_SECRET: process.env.JWT_SECRET, JWT_SECRET: process.env.JWT_SECRET,
EMAIL_INFO: { EMAIL_INFO: {
host: process.env.EMAIL_HOST, host: process.env.EMAIL_HOST,
port: 465, port: EMAIL_PORT,
secure: true, // true for 465, false for other ports secure: EMAIL_PORT === 465,
auth: { auth: {
user: process.env.EMAIL_USER, user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASSWORD pass: process.env.EMAIL_PASSWORD
} },
ignoreTLS: process.env.NODE_ENV !== 'production'
}, },
TOKEN_LIFE: '1 week' TOKEN_LIFE: '1 week'
} }

View File

@ -7,7 +7,8 @@ const sequelize = new Sequelize(
DATABASE.password, DATABASE.password,
{ {
dialect: 'mysql', dialect: 'mysql',
host: DATABASE.host host: DATABASE.host,
port: DATABASE.port
} }
) )

1648
api/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +1,38 @@
{ {
"name": "api", "name": "api",
"version": "2.1.0", "version": "2.2.0",
"description": "Backend REST API for FunctionProject", "description": "Backend REST API for FunctionProject",
"scripts": { "scripts": {
"start": "node app.js", "start": "node app.js",
"dev": "nodemon app.js", "dev": "nodemon app.js",
"format": "standard \"./**/*.js\" --fix | snazzy || exit 0" "format": "standard \"./**/*.js\" --fix | snazzy"
}, },
"dependencies": { "dependencies": {
"axios": "^0.19.2", "axios": "^0.21.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^4.17.1",
"express-fileupload": "^1.1.6",
"express-http-to-https": "^1.1.4",
"express-validator": "^6.4.0",
"helmet": "^3.21.3",
"jsdom": "^16.2.2",
"jsonwebtoken": "^8.5.1",
"moment": "^2.24.0",
"ms": "^2.1.2",
"mysql2": "^2.1.0",
"nodemailer": "^6.4.6",
"sequelize": "^5.21.5",
"smart-request-balancer": "^2.1.1",
"uuid": "^7.0.2",
"validator": "^13.0.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"morgan": "^1.9.1" "express": "^4.17.1",
"express-fileupload": "^1.2.0",
"express-http-to-https": "^1.1.4",
"express-rate-limit": "^5.1.3",
"express-validator": "^6.6.1",
"helmet": "^4.1.1",
"jsdom": "^16.4.0",
"jsonwebtoken": "^8.5.1",
"moment": "^2.29.1",
"morgan": "^1.10.0",
"ms": "^2.1.2",
"mysql2": "^2.2.5",
"nodemailer": "^6.4.14",
"sequelize": "^6.3.5",
"smart-request-balancer": "^2.1.1",
"uuid": "^8.3.1",
"validator": "^13.1.17"
}, },
"devDependencies": { "devDependencies": {
"nodemon": "^2.0.4", "nodemon": "^2.0.6",
"snazzy": "^8.0.0", "snazzy": "^9.0.0",
"standard": "^14.3.4" "standard": "^16.0.0"
} }
} }

73
docker-compose.yml Normal file
View File

@ -0,0 +1,73 @@
version: '3.0'
services:
functionproject-api:
build:
context: './api'
ports:
- '8080:8080'
depends_on:
- 'functionproject-database'
- 'functionproject-maildev'
volumes:
- './api:/app'
- '/app/node_modules'
environment:
WAIT_HOSTS: 'functionproject-database:3306'
container_name: 'functionproject-api'
s.divlo.fr-website:
build:
context: './s.divlo.fr'
ports:
- '7000:7000'
depends_on:
- 'functionproject-database'
volumes:
- './s.divlo.fr:/app'
- '/app/node_modules'
environment:
WAIT_HOSTS: 'functionproject-database:3306'
container_name: 's.divlo.fr-website'
functionproject-website:
build:
context: './website'
ports:
- '3000:3000'
volumes:
- './website:/app'
- '/app/node_modules'
container_name: 'functionproject-website'
functionproject-phpmyadmin:
image: 'phpmyadmin/phpmyadmin:5.0.2'
environment:
PMA_HOST: 'functionproject-database'
PMA_USER: 'root'
PMA_PASSWORD: 'password'
ports:
- '8000:80'
depends_on:
- 'functionproject-database'
container_name: 'functionproject-phpmyadmin'
functionproject-database:
image: 'mysql:5.7'
command: '--default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci'
environment:
MYSQL_ROOT_PASSWORD: 'password'
MYSQL_DATABASE: 'functionproject'
ports:
- '3306:3306'
volumes:
- 'database-volume:/var/lib/mysql'
container_name: 'functionproject-database'
functionproject-maildev:
image: 'maildev/maildev:1.1.0'
ports:
- '1080:80'
container_name: 'functionproject-maildev'
volumes:
database-volume:

2
s.divlo.fr/.dockerignore Normal file
View File

@ -0,0 +1,2 @@
node_modules
build

View File

@ -1,4 +1,6 @@
DB_HOST = "" DATABASE_HOST="functionproject-database"
DB_NAME = "" DATABASE_NAME="functionproject"
DB_USER = "" DATABASE_USER="root"
DB_PASS = "" DATABASE_PASSWORD="password"
DATABASE_PORT=3306
COMPOSE_PROJECT_NAME="s.divlo.fr-website"

13
s.divlo.fr/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM node:14.15.0-alpine3.12
WORKDIR /app
COPY ./package*.json ./
RUN npm install
COPY ./ ./
# docker-compose-wait
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.7.3/wait /wait
RUN chmod +x /wait
CMD /wait && npm run dev

View File

@ -10,18 +10,21 @@ const mysql = require('mysql')
/* Files Imports & Variables */ /* Files Imports & Variables */
const app = express() const app = express()
const database = mysql.createPool({ const database = mysql.createPool({
host: process.env.DB_HOST, host: process.env.DATABASE_HOST,
user: process.env.DB_USER, user: process.env.DATABASE_USER,
password: process.env.DB_PASS, password: process.env.DATABASE_PASS,
database: process.env.DB_NAME, database: process.env.DATABASE_NAME,
port: process.env.DB_PORT port: process.env.DATABASE_PORT
}) })
/* Middlewares */ /* Middlewares */
app.use(helmet()) if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev')) app.use(morgan('dev'))
} else if (process.env.NODE_ENV === 'production') {
app.use(redirectToHTTPS())
}
app.use(helmet())
app.use(express.json()) app.use(express.json())
app.use(redirectToHTTPS([/localhost:(\d{4})/]))
/* EJS Template Engines */ /* EJS Template Engines */
app.set('view engine', 'ejs') app.set('view engine', 'ejs')
@ -77,7 +80,7 @@ app.use((error, _req, res) => {
}) })
/* Server */ /* Server */
const PORT = process.env.PORT || 8000 const PORT = process.env.PORT || 7000
app.listen(PORT, () => { app.listen(PORT, () => {
console.log('\x1b[36m%s\x1b[0m', `Started on port ${PORT}.`) console.log('\x1b[36m%s\x1b[0m', `Started on port ${PORT}.`)
}) })

File diff suppressed because it is too large Load Diff

View File

@ -9,16 +9,16 @@
}, },
"dependencies": { "dependencies": {
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"ejs": "^3.1.3", "ejs": "^3.1.5",
"express": "^4.17.1", "express": "^4.17.1",
"express-http-to-https": "^1.1.4", "express-http-to-https": "^1.1.4",
"helmet": "^4.0.0", "helmet": "^4.1.1",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"mysql": "^2.18.1" "mysql": "^2.18.1"
}, },
"devDependencies": { "devDependencies": {
"nodemon": "^2.0.4", "nodemon": "^2.0.6",
"snazzy": "^8.0.0", "snazzy": "^9.0.0",
"standard": "^14.3.4" "standard": "^16.0.0"
} }
} }

2
website/.dockerignore Normal file
View File

@ -0,0 +1,2 @@
node_modules
build

View File

@ -1 +1,3 @@
NEXT_PUBLIC_API_URL = "http://localhost:8080" NEXT_PUBLIC_API_URL = "http://localhost:8080"
CONTAINER_API_URL="http://functionproject-api:8080"
COMPOSE_PROJECT_NAME="function.divlo.fr-website"

9
website/Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM node:14.15.0-alpine3.12
WORKDIR /app
COPY ./package*.json ./
RUN npm install
COPY ./ ./
CMD ["npm", "run", "dev"]

View File

@ -8,7 +8,7 @@ export default function Footer () {
<Link href='/about'> <Link href='/about'>
<a>FunctionProject</a> <a>FunctionProject</a>
</Link> </Link>
&nbsp;- Version 2.1 <br /> &nbsp;- Version 2.2 <br />
<a href='https://divlo.fr/' target='_blank' rel='noopener noreferrer'> <a href='https://divlo.fr/' target='_blank' rel='noopener noreferrer'>
Divlo Divlo
</a>{' '} </a>{' '}

View File

@ -82,12 +82,17 @@ const CommentCard = forwardRef((props, ref) => {
<a>{props.user.name}</a> <a>{props.user.name}</a>
</Link> </Link>
&nbsp;-{' '} &nbsp;-{' '}
{date.format(new Date(props.createdAt), 'DD/MM/YYYY à HH:mm', false)} {date.format(
new Date(props.createdAt),
'DD/MM/YYYY à HH:mm',
false
)}
</span> </span>
</div> </div>
<div className='row'> <div className='row'>
<div className='col-24'> <div className='col-24'>
{!isEditing ? ( {!isEditing
? (
<> <>
<div className='CommentCard__message'> <div className='CommentCard__message'>
<ReactMarkdown <ReactMarkdown
@ -113,7 +118,8 @@ const CommentCard = forwardRef((props, ref) => {
</p> </p>
)} )}
</> </>
) : ( )
: (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className='form-group FunctionComments__post-group'> <div className='form-group FunctionComments__post-group'>
<label className='form-label' htmlFor='commentEdit'> <label className='form-label' htmlFor='commentEdit'>

View File

@ -3,9 +3,11 @@ import htmlParser from 'html-react-parser'
const FunctionArticle = ({ article }) => { const FunctionArticle = ({ article }) => {
return ( return (
<div style={{ marginBottom: '50px' }} className='container-fluid'> <div style={{ marginBottom: '50px' }} className='container-fluid'>
{article != null ? ( {article != null
? (
htmlParser(article) htmlParser(article)
) : ( )
: (
<p className='text-center'>L'article n'est pas encore disponible.</p> <p className='text-center'>L'article n'est pas encore disponible.</p>
)} )}
</div> </div>

View File

@ -90,7 +90,8 @@ const FunctionComments = ({ functionId }) => {
<div className='FunctionComments__post container-fluid'> <div className='FunctionComments__post container-fluid'>
<div className='row FunctionComments__row'> <div className='row FunctionComments__row'>
<div className='col-24'> <div className='col-24'>
{isAuth ? ( {isAuth
? (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className='form-group FunctionComments__post-group'> <div className='form-group FunctionComments__post-group'>
<label className='form-label' htmlFor='commentPost'> <label className='form-label' htmlFor='commentPost'>
@ -111,7 +112,8 @@ const FunctionComments = ({ functionId }) => {
</button> </button>
</div> </div>
</form> </form>
) : ( )
: (
<p className='text-center'> <p className='text-center'>
Vous devez être{' '} Vous devez être{' '}
<Link href='/users/login'> <Link href='/users/login'>

View File

@ -51,12 +51,14 @@ export default function Header () {
<NavigationLink name='Accueil' path='/' /> <NavigationLink name='Accueil' path='/' />
<NavigationLink name='Fonctions' path='/functions' /> <NavigationLink name='Fonctions' path='/functions' />
<NavigationLink name='Utilisateurs' path='/users' /> <NavigationLink name='Utilisateurs' path='/users' />
{!isAuth ? ( {!isAuth
? (
<> <>
<NavigationLink name="S'inscrire" path='/users/register' /> <NavigationLink name="S'inscrire" path='/users/register' />
<NavigationLink name='Connexion' path='/users/login' /> <NavigationLink name='Connexion' path='/users/login' />
</> </>
) : ( )
: (
<> <>
<li className='navbar-item'> <li className='navbar-item'>
<Link href='/users/[name]' as={`/users/${user.name}`}> <Link href='/users/[name]' as={`/users/${user.name}`}>

4242
website/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,49 @@
{ {
"name": "website", "name": "website",
"version": "2.1.0", "version": "2.2.0",
"description": "Website frontend for FunctionProject", "description": "Website frontend for FunctionProject",
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {
"dev": "cross-env NODE_ENV=development node server", "dev:custom": "cross-env NODE_ENV=development node server",
"start:custom": "cross-env NODE_ENV=production node server",
"dev": "next",
"start": "next start",
"build": "next build", "build": "next build",
"export": "next export", "export": "next export",
"start": "cross-env NODE_ENV=production node server", "format": "standard \"./**/*.{js,jsx}\" --fix | snazzy"
"format": "standard \"./**/*.{js,jsx}\" --fix | snazzy || exit 0"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.28", "@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-brands-svg-icons": "^5.13.0", "@fortawesome/free-brands-svg-icons": "^5.15.1",
"@fortawesome/free-regular-svg-icons": "^5.13.0", "@fortawesome/free-regular-svg-icons": "^5.15.1",
"@fortawesome/free-solid-svg-icons": "^5.13.0", "@fortawesome/free-solid-svg-icons": "^5.15.1",
"@fortawesome/react-fontawesome": "^0.1.9", "@fortawesome/react-fontawesome": "^0.1.12",
"@zeit/next-css": "^1.0.1", "@zeit/next-css": "^1.0.1",
"axios": "^0.19.2", "axios": "^0.21.0",
"date-and-time": "^0.13.1", "date-and-time": "^0.14.1",
"date-fns": "^2.12.0", "date-fns": "^2.16.1",
"express": "^4.17.1", "express": "^4.17.1",
"express-http-to-https": "^1.1.4", "express-http-to-https": "^1.1.4",
"html-react-parser": "^0.10.2", "html-react-parser": "^0.14.0",
"next": "^9.5.4", "next": "^10.0.0",
"next-fonts": "^1.0.3", "next-fonts": "^1.4.0",
"notyf": "^3.6.0", "notyf": "^3.9.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"react": "16.13.0", "react": "17.0.1",
"react-codepen-embed": "^1.0.1", "react-codepen-embed": "^1.0.1",
"react-color": "^2.18.0", "react-color": "^2.19.3",
"react-datepicker": "^2.14.1", "react-datepicker": "^3.3.0",
"react-dom": "16.13.0", "react-dom": "17.0.1",
"react-markdown": "^4.3.1", "react-markdown": "^5.0.2",
"react-swipeable-views": "^0.13.9", "react-swipeable-views": "^0.13.9",
"react-swipeable-views-utils": "^0.13.9", "react-swipeable-views-utils": "^0.13.9",
"react-syntax-highlighter": "^12.2.1", "react-syntax-highlighter": "^15.3.0",
"suneditor-react": "^2.8.0", "suneditor-react": "^2.8.0",
"universal-cookie": "^4.0.3" "universal-cookie": "^4.0.4"
}, },
"devDependencies": { "devDependencies": {
"cross-env": "^7.0.2", "cross-env": "^7.0.2",
"snazzy": "^8.0.0", "snazzy": "^9.0.0",
"standard": "^14.3.4" "standard": "^16.0.0"
} }
} }

View File

@ -23,7 +23,8 @@ const Admin = props => {
/> />
{/* Création d'une fonction */} {/* Création d'une fonction */}
{isOpen ? ( {isOpen
? (
<Modal toggleModal={toggleModal}> <Modal toggleModal={toggleModal}>
<div className='Admin__Modal__container container-fluid'> <div className='Admin__Modal__container container-fluid'>
<div className='Admin__Modal__row row'> <div className='Admin__Modal__row row'>
@ -57,7 +58,8 @@ const Admin = props => {
</div> </div>
</div> </div>
</Modal> </Modal>
) : ( )
: (
<FunctionsList isAdmin token={props.user.token}> <FunctionsList isAdmin token={props.user.token}>
<div className='col-24'> <div className='col-24'>
<h1 className='Functions__title'>Administration</h1> <h1 className='Functions__title'>Administration</h1>

View File

@ -159,7 +159,8 @@ const manageCategories = props => {
description="Page d'administration de FunctionProject. Gérer les catégories." description="Page d'administration de FunctionProject. Gérer les catégories."
/> />
{isOpen ? ( {isOpen
? (
<Modal> <Modal>
<AddEditCategory <AddEditCategory
handleToggleModal={toggleModal} handleToggleModal={toggleModal}
@ -168,7 +169,8 @@ const manageCategories = props => {
isEditing={isEditing} isEditing={isEditing}
/> />
</Modal> </Modal>
) : ( )
: (
<div className='container-fluid text-center'> <div className='container-fluid text-center'>
<div className='row justify-content-center'> <div className='row justify-content-center'>
<div className='col-24'> <div className='col-24'>

View File

@ -266,7 +266,8 @@ const LinksList = ({
</div> </div>
<div className='row justify-content-center'> <div className='row justify-content-center'>
<div className='container-fluid'> <div className='container-fluid'>
{!isEditing ? ( {!isEditing
? (
<div className='col-24 table-column'> <div className='col-24 table-column'>
<table className='table' style={{ marginBottom: '40px' }}> <table className='table' style={{ marginBottom: '40px' }}>
<thead> <thead>
@ -334,7 +335,8 @@ const LinksList = ({
</tbody> </tbody>
</table> </table>
</div> </div>
) : ( )
: (
<Modal> <Modal>
<div className='Admin__Modal__container container-fluid'> <div className='Admin__Modal__container container-fluid'>
<div className='Admin__Modal__row row'> <div className='Admin__Modal__row row'>

View File

@ -54,7 +54,8 @@ const PlayRightPrice = () => {
return ( return (
<div className='container-fluid'> <div className='container-fluid'>
{!isPlaying ? ( {!isPlaying
? (
<div className='row justify-content-center'> <div className='row justify-content-center'>
<div className='form-group text-center'> <div className='form-group text-center'>
<button <button
@ -66,11 +67,14 @@ const PlayRightPrice = () => {
</button> </button>
</div> </div>
</div> </div>
) : isLoadingProduct ? ( )
: isLoadingProduct
? (
<div className='row justify-content-center'> <div className='row justify-content-center'>
<Loader /> <Loader />
</div> </div>
) : ( )
: (
<> <>
<div className='row justify-content-center'> <div className='row justify-content-center'>
<div <div
@ -90,7 +94,8 @@ const PlayRightPrice = () => {
<div style={{ marginBottom: '25px' }} className='col-24'> <div style={{ marginBottom: '25px' }} className='col-24'>
{attemptsArray.length > 0 && {attemptsArray.length > 0 &&
attemptsArray[0].message === attemptsArray[0].message ===
'Bravo, vous avez trouvé le juste prix !' ? ( 'Bravo, vous avez trouvé le juste prix !'
? (
<div className='form-group text-center'> <div className='form-group text-center'>
<button <button
onClick={handlePlaying} onClick={handlePlaying}
@ -100,7 +105,8 @@ const PlayRightPrice = () => {
Rejouer ? Rejouer ?
</button> </button>
</div> </div>
) : ( )
: (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className='text-center'> <div className='text-center'>
<input <input

View File

@ -84,7 +84,8 @@ const Profile = props => {
/> />
{/* Édition du profil */} {/* Édition du profil */}
{isOpen ? ( {isOpen
? (
<Modal toggleModal={toggleModal}> <Modal toggleModal={toggleModal}>
<div className='Profile__container container-fluid'> <div className='Profile__container container-fluid'>
<div className='Profile__row row'> <div className='Profile__row row'>
@ -213,7 +214,8 @@ const Profile = props => {
</div> </div>
</div> </div>
</Modal> </Modal>
) : ( )
: (
<div className='container-fluid Profile__container'> <div className='container-fluid Profile__container'>
<div className='row Profile__row'> <div className='row Profile__row'>
<div className='col-20'> <div className='col-20'>

View File

@ -2,11 +2,15 @@ import axios from 'axios'
export const API_URL = process.env.NEXT_PUBLIC_API_URL export const API_URL = process.env.NEXT_PUBLIC_API_URL
const api = axios.create({ const api = (() => {
baseURL: API_URL, const baseURL =
typeof window === 'undefined' ? process.env.CONTAINER_API_URL : API_URL
return axios.create({
baseURL,
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
}) })
})()
export default api export default api