🎨 standardJS all files
This commit is contained in:
parent
dc962c9120
commit
4be7a46a10
13
api/app.js
13
api/app.js
@ -34,7 +34,9 @@ app.use('/quotes', require('./routes/quotes'))
|
|||||||
app.use('/tasks', require('./routes/tasks'))
|
app.use('/tasks', require('./routes/tasks'))
|
||||||
|
|
||||||
/* Errors Handling */
|
/* Errors Handling */
|
||||||
app.use((_req, _res, next) => errorHandling(next, { statusCode: 404, message: "La route n'existe pas!" })) // 404
|
app.use((_req, _res, next) =>
|
||||||
|
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
|
||||||
@ -76,8 +78,11 @@ Tasks.belongsTo(Users, { constraints: false })
|
|||||||
|
|
||||||
/* Server */
|
/* Server */
|
||||||
// sequelize.sync({ force: true })
|
// sequelize.sync({ force: true })
|
||||||
sequelize.sync()
|
sequelize
|
||||||
|
.sync()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
app.listen(PORT, () => console.log('\x1b[36m%s\x1b[0m', `Started on port ${PORT}.`))
|
app.listen(PORT, () =>
|
||||||
|
console.log('\x1b[36m%s\x1b[0m', `Started on port ${PORT}.`)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.catch((error) => console.log(error))
|
.catch(error => console.log(error))
|
||||||
|
@ -26,16 +26,24 @@ exports.emailQuoteTemplate = (isValid, quote, frontendLink) => `
|
|||||||
<tr>
|
<tr>
|
||||||
<td align="left" valign="top" style="line-height:150%;font-family:Helvetica;font-size:14px;color:rgb(222, 222, 222);padding:30px;box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);border: 1px solid black;border-radius: 1rem;">
|
<td align="left" valign="top" style="line-height:150%;font-family:Helvetica;font-size:14px;color:rgb(222, 222, 222);padding:30px;box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);border: 1px solid black;border-radius: 1rem;">
|
||||||
<h2 style="font-size:22px;line-height:28px;margin:0 0 12px 0;">
|
<h2 style="font-size:22px;line-height:28px;margin:0 0 12px 0;">
|
||||||
La citation que vous avez proposée a été ${(isValid) ? 'validée' : 'supprimée'}.
|
La citation que vous avez proposée a été ${
|
||||||
|
isValid
|
||||||
|
? 'validée'
|
||||||
|
: 'supprimée'
|
||||||
|
}.
|
||||||
</h2>
|
</h2>
|
||||||
<p style="margin: 0 0 12px 0;">
|
<p style="margin: 0 0 12px 0;">
|
||||||
<a style="color: #ffd800;" href="${frontendLink}/functions/randomQuote">Lien vers la fonction randomQuote de FunctionProject.</a>
|
<a style="color: #ffd800;" href="${frontendLink}/functions/randomQuote">Lien vers la fonction randomQuote de FunctionProject.</a>
|
||||||
</p>
|
</p>
|
||||||
${(!isValid) ? `
|
${
|
||||||
|
!isValid
|
||||||
|
? `
|
||||||
<p style="margin: 0 0 12px 0;">
|
<p style="margin: 0 0 12px 0;">
|
||||||
Si votre citation a été supprimée et vous pensez que c'est une erreur, contactez-moi à cette adresse email : <a style="color: #ffd800;" href="mailto:contact@divlo.fr">contact@divlo.fr</a>.
|
Si votre citation a été supprimée et vous pensez que c'est une erreur, contactez-moi à cette adresse email : <a style="color: #ffd800;" href="mailto:contact@divlo.fr">contact@divlo.fr</a>.
|
||||||
</p>
|
</p>
|
||||||
` : ''}
|
`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
<div>
|
<div>
|
||||||
<p style="padding:0 0 10px 0">
|
<p style="padding:0 0 10px 0">
|
||||||
La citation en question : <br/>
|
La citation en question : <br/>
|
||||||
|
@ -16,14 +16,24 @@ function armstrongNumber (number) {
|
|||||||
let resultString = ''
|
let resultString = ''
|
||||||
for (let index = 0; index < numberStringLength; index++) {
|
for (let index = 0; index < numberStringLength; index++) {
|
||||||
result = result + parseInt(numberString[index]) ** numberStringLength
|
result = result + parseInt(numberString[index]) ** numberStringLength
|
||||||
resultString = resultString + ' + ' + numberString[index] + '<sup>' + numberStringLength + '</sup>'
|
resultString =
|
||||||
|
resultString +
|
||||||
|
' + ' +
|
||||||
|
numberString[index] +
|
||||||
|
'<sup>' +
|
||||||
|
numberStringLength +
|
||||||
|
'</sup>'
|
||||||
}
|
}
|
||||||
|
|
||||||
const formattedNumber = formatNumberResult(number)
|
const formattedNumber = formatNumberResult(number)
|
||||||
const isArmstrongNumber = (result === number)
|
const isArmstrongNumber = result === number
|
||||||
return {
|
return {
|
||||||
isArmstrongNumber,
|
isArmstrongNumber,
|
||||||
resultHTML: `<p>${formattedNumber} ${isArmstrongNumber ? 'est' : "n'est pas"} un nombre d'Armstrong, car ${resultString.slice(2)} = ${formatNumberResult(result)}.</p>`
|
resultHTML: `<p>${formattedNumber} ${
|
||||||
|
isArmstrongNumber ? 'est' : "n'est pas"
|
||||||
|
} un nombre d'Armstrong, car ${resultString.slice(
|
||||||
|
2
|
||||||
|
)} = ${formatNumberResult(result)}.</p>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,14 +42,17 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
let { number } = argsObject
|
let { number } = argsObject
|
||||||
|
|
||||||
// S'il n'y a pas les champs obligatoire
|
// S'il n'y a pas les champs obligatoire
|
||||||
if (!(number)) {
|
if (!number) {
|
||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si ce n'est pas un nombre
|
// Si ce n'est pas un nombre
|
||||||
number = parseInt(number)
|
number = parseInt(number)
|
||||||
if (isNaN(number) || number <= 0) {
|
if (isNaN(number) || number <= 0) {
|
||||||
return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Veuillez rentré un nombre valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json(armstrongNumber(number))
|
return res.status(200).json(armstrongNumber(number))
|
||||||
|
@ -2,7 +2,10 @@ const errorHandling = require('../../utils/errorHandling')
|
|||||||
const moment = require('moment')
|
const moment = require('moment')
|
||||||
const { requiredFields } = require('../../config/errors')
|
const { requiredFields } = require('../../config/errors')
|
||||||
|
|
||||||
function calculateAge (currentDate, { birthDateDay, birthDateMonth, birthDateYear }) {
|
function calculateAge (
|
||||||
|
currentDate,
|
||||||
|
{ birthDateDay, birthDateMonth, birthDateYear }
|
||||||
|
) {
|
||||||
const day = currentDate.getDate()
|
const day = currentDate.getDate()
|
||||||
const month = currentDate.getMonth()
|
const month = currentDate.getMonth()
|
||||||
const currentDateMoment = moment([currentDate.getFullYear(), month, day])
|
const currentDateMoment = moment([currentDate.getFullYear(), month, day])
|
||||||
@ -15,7 +18,7 @@ function calculateAge (currentDate, { birthDateDay, birthDateMonth, birthDateYea
|
|||||||
birthDateMoment.add(ageMonths, 'months')
|
birthDateMoment.add(ageMonths, 'months')
|
||||||
const ageDays = currentDateMoment.diff(birthDateMoment, 'days')
|
const ageDays = currentDateMoment.diff(birthDateMoment, 'days')
|
||||||
|
|
||||||
const isBirthday = (birthDateDay === day && birthDateMonth === month)
|
const isBirthday = birthDateDay === day && birthDateMonth === month
|
||||||
return { ageYears, ageMonths, ageDays, isBirthday }
|
return { ageYears, ageMonths, ageDays, isBirthday }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,20 +27,27 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
const { birthDate } = argsObject
|
const { birthDate } = argsObject
|
||||||
|
|
||||||
// S'il n'y a pas les champs obligatoire
|
// S'il n'y a pas les champs obligatoire
|
||||||
if (!(birthDate)) {
|
if (!birthDate) {
|
||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
const birthDateDay = parseInt(birthDate.substring(0, 2))
|
const birthDateDay = parseInt(birthDate.substring(0, 2))
|
||||||
const birthDateMonth = parseInt((birthDate.substring(3, 5)) - 1)
|
const birthDateMonth = parseInt(birthDate.substring(3, 5) - 1)
|
||||||
const birthDateYear = parseInt(birthDate.substring(6, 10))
|
const birthDateYear = parseInt(birthDate.substring(6, 10))
|
||||||
|
|
||||||
// Si ce n'est pas une date valide
|
// Si ce n'est pas une date valide
|
||||||
const currentDate = new Date()
|
const currentDate = new Date()
|
||||||
const birthDateObject = new Date(birthDateYear, birthDateMonth, birthDateDay)
|
const birthDateObject = new Date(birthDateYear, birthDateMonth, birthDateDay)
|
||||||
const result = calculateAge(currentDate, { birthDateYear, birthDateMonth, birthDateDay })
|
const result = calculateAge(currentDate, {
|
||||||
if ((currentDate < birthDateObject) || isNaN(result.ageYears)) {
|
birthDateYear,
|
||||||
return errorHandling(next, { message: 'Veuillez rentré une date valide...', statusCode: 400 })
|
birthDateMonth,
|
||||||
|
birthDateDay
|
||||||
|
})
|
||||||
|
if (currentDate < birthDateObject || isNaN(result.ageYears)) {
|
||||||
|
return errorHandling(next, {
|
||||||
|
message: 'Veuillez rentré une date valide...',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let resultHTML
|
let resultHTML
|
||||||
|
@ -15,23 +15,39 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
// Si ce n'est pas un nombre
|
// Si ce n'est pas un nombre
|
||||||
number = parseFloat(number)
|
number = parseFloat(number)
|
||||||
if (isNaN(number)) {
|
if (isNaN(number)) {
|
||||||
return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Veuillez rentré un nombre valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.get(`https://api.exchangeratesapi.io/latest?base=${baseCurrency}`)
|
axios
|
||||||
.then((response) => {
|
.get(`https://api.exchangeratesapi.io/latest?base=${baseCurrency}`)
|
||||||
|
.then(response => {
|
||||||
const rate = response.data.rates[finalCurrency]
|
const rate = response.data.rates[finalCurrency]
|
||||||
if (!rate) {
|
if (!rate) {
|
||||||
return errorHandling(next, { message: "La devise n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La devise n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const result = rate * number
|
const result = rate * number
|
||||||
const dateObject = new Date(response.data.date)
|
const dateObject = new Date(response.data.date)
|
||||||
const year = dateObject.getFullYear()
|
const year = dateObject.getFullYear()
|
||||||
const day = ('0' + (dateObject.getDate())).slice(-2)
|
const day = ('0' + dateObject.getDate()).slice(-2)
|
||||||
const month = ('0' + (dateObject.getMonth() + 1)).slice(-2)
|
const month = ('0' + (dateObject.getMonth() + 1)).slice(-2)
|
||||||
const date = `${day}/${month}/${year}`
|
const date = `${day}/${month}/${year}`
|
||||||
const resultHTML = `<p>${formatNumberResult(number)} ${response.data.base} = ${formatNumberResult(result.toFixed(2))} ${finalCurrency}</p><p>Dernier rafraîchissement du taux d'échange : ${date}</p>`
|
const resultHTML = `<p>${formatNumberResult(number)} ${
|
||||||
|
response.data.base
|
||||||
|
} = ${formatNumberResult(
|
||||||
|
result.toFixed(2)
|
||||||
|
)} ${finalCurrency}</p><p>Dernier rafraîchissement du taux d'échange : ${date}</p>`
|
||||||
return res.status(200).json({ date, result, resultHTML })
|
return res.status(200).json({ date, result, resultHTML })
|
||||||
})
|
})
|
||||||
.catch(() => errorHandling(next, { message: "La devise n'existe pas.", statusCode: 404 }))
|
.catch(() =>
|
||||||
|
errorHandling(next, {
|
||||||
|
message: "La devise n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,33 @@ const errorHandling = require('../../utils/errorHandling')
|
|||||||
const { requiredFields, generalError } = require('../../config/errors')
|
const { requiredFields, generalError } = require('../../config/errors')
|
||||||
const formatNumberResult = require('../secondary/formatNumberResult')
|
const formatNumberResult = require('../secondary/formatNumberResult')
|
||||||
|
|
||||||
const correspondancesDistance = ['pm', null, null, 'nm', null, null, 'µm', null, null, 'mm', 'cm', 'dm', 'm', 'dam', 'hm', 'km', null, null, 'Mm', null, null, 'Gm', null, null, 'Tm']
|
const correspondancesDistance = [
|
||||||
|
'pm',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'nm',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'µm',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'mm',
|
||||||
|
'cm',
|
||||||
|
'dm',
|
||||||
|
'm',
|
||||||
|
'dam',
|
||||||
|
'hm',
|
||||||
|
'km',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'Mm',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'Gm',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'Tm'
|
||||||
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Convertis la longueur (distance) avec les unités allant de picomètre au Téramètre.
|
* @description Convertis la longueur (distance) avec les unités allant de picomètre au Téramètre.
|
||||||
@ -21,7 +47,11 @@ function convertDistance (firstValue, unitFirstValue, unitFinalValue) {
|
|||||||
const result = firstValue * Math.pow(10, difference)
|
const result = firstValue * Math.pow(10, difference)
|
||||||
return {
|
return {
|
||||||
result,
|
result,
|
||||||
resultHTML: `<p>${formatNumberResult(firstValue)} ${unitFirstValue} = ${formatNumberResult(result)} ${unitFinalValue}</p>`
|
resultHTML: `<p>${formatNumberResult(
|
||||||
|
firstValue
|
||||||
|
)} ${unitFirstValue} = ${formatNumberResult(
|
||||||
|
result
|
||||||
|
)} ${unitFinalValue}</p>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -39,7 +69,10 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
// Si ce n'est pas un nombre
|
// Si ce n'est pas un nombre
|
||||||
number = parseFloat(number)
|
number = parseFloat(number)
|
||||||
if (isNaN(number)) {
|
if (isNaN(number)) {
|
||||||
return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Veuillez rentré un nombre valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = convertDistance(number, numberUnit, finalUnit)
|
const result = convertDistance(number, numberUnit, finalUnit)
|
||||||
|
@ -73,7 +73,9 @@ function binaryToHexadecimal (value) {
|
|||||||
if (isNaN(value)) {
|
if (isNaN(value)) {
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
return parseInt(value).toString(16).toUpperCase()
|
return parseInt(value)
|
||||||
|
.toString(16)
|
||||||
|
.toUpperCase()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,10 +142,15 @@ function numberUnicodeToText (string) {
|
|||||||
function textToBinary (s) {
|
function textToBinary (s) {
|
||||||
try {
|
try {
|
||||||
s = unescape(encodeURIComponent(s))
|
s = unescape(encodeURIComponent(s))
|
||||||
let chr; let i = 0; const l = s.length; let out = ''
|
let chr
|
||||||
|
let i = 0
|
||||||
|
const l = s.length
|
||||||
|
let out = ''
|
||||||
for (; i < l; i++) {
|
for (; i < l; i++) {
|
||||||
chr = s.charCodeAt(i).toString(2)
|
chr = s.charCodeAt(i).toString(2)
|
||||||
while (chr.length % 8 !== 0) { chr = '0' + chr }
|
while (chr.length % 8 !== 0) {
|
||||||
|
chr = '0' + chr
|
||||||
|
}
|
||||||
out += chr
|
out += chr
|
||||||
}
|
}
|
||||||
return out.replace(/(\d{8})/g, '$1 ').replace(/(^\s+|\s+$)/, '')
|
return out.replace(/(\d{8})/g, '$1 ').replace(/(^\s+|\s+$)/, '')
|
||||||
@ -161,10 +168,13 @@ function textToBinary (s) {
|
|||||||
function binaryToText (s) {
|
function binaryToText (s) {
|
||||||
try {
|
try {
|
||||||
s = s.replace(/\s/g, '')
|
s = s.replace(/\s/g, '')
|
||||||
let i = 0; const l = s.length; let chr; let out = ''
|
let i = 0
|
||||||
|
const l = s.length
|
||||||
|
let chr
|
||||||
|
let out = ''
|
||||||
for (; i < l; i += 8) {
|
for (; i < l; i += 8) {
|
||||||
chr = parseInt(s.substr(i, 8), 2).toString(16)
|
chr = parseInt(s.substr(i, 8), 2).toString(16)
|
||||||
out += '%' + ((chr.length % 2 === 0) ? chr : '0' + chr)
|
out += '%' + (chr.length % 2 === 0 ? chr : '0' + chr)
|
||||||
}
|
}
|
||||||
return decodeURIComponent(out)
|
return decodeURIComponent(out)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -181,10 +191,13 @@ function binaryToText (s) {
|
|||||||
function textToHexadecimal (s) {
|
function textToHexadecimal (s) {
|
||||||
try {
|
try {
|
||||||
s = unescape(encodeURIComponent(s))
|
s = unescape(encodeURIComponent(s))
|
||||||
let chr; let i = 0; const l = s.length; let out = ''
|
let chr
|
||||||
|
let i = 0
|
||||||
|
const l = s.length
|
||||||
|
let out = ''
|
||||||
for (; i < l; i++) {
|
for (; i < l; i++) {
|
||||||
chr = s.charCodeAt(i).toString(16)
|
chr = s.charCodeAt(i).toString(16)
|
||||||
out += (chr.length % 2 === 0) ? chr : '0' + chr
|
out += chr.length % 2 === 0 ? chr : '0' + chr
|
||||||
out += ' '
|
out += ' '
|
||||||
}
|
}
|
||||||
return out.toUpperCase()
|
return out.toUpperCase()
|
||||||
@ -209,7 +222,20 @@ function hexadecimalToText (s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* OUTPUTS */
|
/* OUTPUTS */
|
||||||
const convertEncoding = { decimalToBinary, binaryToDecimal, decimalToHexadecimal, hexadecimalToDecimal, binaryToHexadecimal, hexadecimalToBinary, textToNumberUnicode, numberUnicodeToText, textToBinary, binaryToText, textToHexadecimal, hexadecimalToText }
|
const convertEncoding = {
|
||||||
|
decimalToBinary,
|
||||||
|
binaryToDecimal,
|
||||||
|
decimalToHexadecimal,
|
||||||
|
hexadecimalToDecimal,
|
||||||
|
binaryToHexadecimal,
|
||||||
|
hexadecimalToBinary,
|
||||||
|
textToNumberUnicode,
|
||||||
|
numberUnicodeToText,
|
||||||
|
textToBinary,
|
||||||
|
binaryToText,
|
||||||
|
textToHexadecimal,
|
||||||
|
hexadecimalToText
|
||||||
|
}
|
||||||
function executeFunction (option, value) {
|
function executeFunction (option, value) {
|
||||||
return convertEncoding[option](value)
|
return convertEncoding[option](value)
|
||||||
}
|
}
|
||||||
@ -225,7 +251,10 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
// Si la fonction n'existe pas
|
// Si la fonction n'existe pas
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
if (!convertEncoding.hasOwnProperty(functionName)) {
|
if (!convertEncoding.hasOwnProperty(functionName)) {
|
||||||
return errorHandling(next, { message: "Cette conversion n'existe pas.", statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: "Cette conversion n'existe pas.",
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = executeFunction(functionName, value)
|
const result = executeFunction(functionName, value)
|
||||||
|
@ -51,7 +51,7 @@ function convertArabicToRoman (nombre) {
|
|||||||
*/
|
*/
|
||||||
function convertRomanToArabic (string) {
|
function convertRomanToArabic (string) {
|
||||||
let result = 0
|
let result = 0
|
||||||
correspondancesRomainArabe.forEach((correspondance) => {
|
correspondancesRomainArabe.forEach(correspondance => {
|
||||||
while (string.indexOf(correspondance[1]) === 0) {
|
while (string.indexOf(correspondance[1]) === 0) {
|
||||||
// Ajout de la valeur décimale au résultat
|
// Ajout de la valeur décimale au résultat
|
||||||
result += correspondance[0]
|
result += correspondance[0]
|
||||||
@ -68,7 +68,7 @@ function convertRomanToArabic (string) {
|
|||||||
/* OUTPUTS */
|
/* OUTPUTS */
|
||||||
const convertRomanToArabicOutput = ({ res, next }, number) => {
|
const convertRomanToArabicOutput = ({ res, next }, number) => {
|
||||||
// S'il n'y a pas les champs obligatoire
|
// S'il n'y a pas les champs obligatoire
|
||||||
if (!(number)) {
|
if (!number) {
|
||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,26 +80,44 @@ const convertRomanToArabicOutput = ({ res, next }, number) => {
|
|||||||
return errorHandling(next, generalError)
|
return errorHandling(next, generalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json({ result, resultHTML: `<p><span class="important">${number}</span> s'écrit <span class="important">${result}</span> en chiffres arabes.</p>` })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
result,
|
||||||
|
resultHTML: `<p><span class="important">${number}</span> s'écrit <span class="important">${result}</span> en chiffres arabes.</p>`
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const convertArabicToRomanOutput = ({ res, next }, number) => {
|
const convertArabicToRomanOutput = ({ res, next }, number) => {
|
||||||
// S'il n'y a pas les champs obligatoire
|
// S'il n'y a pas les champs obligatoire
|
||||||
if (!(number)) {
|
if (!number) {
|
||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si ce n'est pas un nombre
|
// Si ce n'est pas un nombre
|
||||||
number = parseInt(number)
|
number = parseInt(number)
|
||||||
if (isNaN(number)) {
|
if (isNaN(number)) {
|
||||||
return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Veuillez rentré un nombre valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = convertArabicToRoman(number)
|
const result = convertArabicToRoman(number)
|
||||||
return res.status(200).json({ result, resultHTML: `<p><span class="important">${formatNumberResult(number)}</span> s'écrit <span class="important">${result}</span> en chiffres romains.</p>` })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
result,
|
||||||
|
resultHTML: `<p><span class="important">${formatNumberResult(
|
||||||
|
number
|
||||||
|
)}</span> s'écrit <span class="important">${result}</span> en chiffres romains.</p>`
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const convertRomanArabicObject = { convertRomanToArabicOutput, convertArabicToRomanOutput }
|
const convertRomanArabicObject = {
|
||||||
|
convertRomanToArabicOutput,
|
||||||
|
convertArabicToRomanOutput
|
||||||
|
}
|
||||||
function executeFunction (option, value, { res, next }) {
|
function executeFunction (option, value, { res, next }) {
|
||||||
return convertRomanArabicObject[option]({ res, next }, value)
|
return convertRomanArabicObject[option]({ res, next }, value)
|
||||||
}
|
}
|
||||||
@ -115,7 +133,10 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
// Si la fonction n'existe pas
|
// Si la fonction n'existe pas
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
if (!convertRomanArabicObject.hasOwnProperty(functionName)) {
|
if (!convertRomanArabicObject.hasOwnProperty(functionName)) {
|
||||||
return errorHandling(next, { message: "Cette conversion n'existe pas.", statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: "Cette conversion n'existe pas.",
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
executeFunction(functionName, value, { res, next })
|
executeFunction(functionName, value, { res, next })
|
||||||
|
@ -12,15 +12,17 @@ const formatNumberResult = require('../secondary/formatNumberResult')
|
|||||||
function convertTemperature (degree, unit) {
|
function convertTemperature (degree, unit) {
|
||||||
let temperatureValue = 0
|
let temperatureValue = 0
|
||||||
if (unit === '°C') {
|
if (unit === '°C') {
|
||||||
temperatureValue = (degree - 32) * 5 / 9
|
temperatureValue = ((degree - 32) * 5) / 9
|
||||||
} else if (unit === '°F') {
|
} else if (unit === '°F') {
|
||||||
temperatureValue = ((degree * 9 / 5) + 32)
|
temperatureValue = (degree * 9) / 5 + 32
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
result: temperatureValue,
|
result: temperatureValue,
|
||||||
resultHTML: `<p>${formatNumberResult(degree)} ${(unit === '°C') ? '°F' : '°C'} = ${formatNumberResult(temperatureValue)} ${unit}</p>`
|
resultHTML: `<p>${formatNumberResult(degree)} ${
|
||||||
|
unit === '°C' ? '°F' : '°C'
|
||||||
|
} = ${formatNumberResult(temperatureValue)} ${unit}</p>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +38,10 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
// Si ce n'est pas un nombre
|
// Si ce n'est pas un nombre
|
||||||
degree = parseFloat(degree)
|
degree = parseFloat(degree)
|
||||||
if (isNaN(degree)) {
|
if (isNaN(degree)) {
|
||||||
return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Veuillez rentré un nombre valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = convertTemperature(degree, unitToConvert)
|
const result = convertTemperature(degree, unitToConvert)
|
||||||
|
@ -20,27 +20,36 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
let { counter } = argsObject
|
let { counter } = argsObject
|
||||||
|
|
||||||
// S'il n'y a pas les champs obligatoire
|
// S'il n'y a pas les champs obligatoire
|
||||||
if (!(counter)) {
|
if (!counter) {
|
||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si ce n'est pas un nombre
|
// Si ce n'est pas un nombre
|
||||||
counter = parseInt(counter)
|
counter = parseInt(counter)
|
||||||
if (isNaN(counter)) {
|
if (isNaN(counter)) {
|
||||||
return errorHandling(next, { message: 'Veuillez rentré un nombre valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Veuillez rentré un nombre valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si le nombre dépasse LIMIT_COUNTER
|
// Si le nombre dépasse LIMIT_COUNTER
|
||||||
const LIMIT_COUNTER = 51
|
const LIMIT_COUNTER = 51
|
||||||
if (counter >= LIMIT_COUNTER) {
|
if (counter >= LIMIT_COUNTER) {
|
||||||
return errorHandling(next, { message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec un compteur dépassant ${LIMIT_COUNTER - 1}.`, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec un compteur dépassant ${LIMIT_COUNTER -
|
||||||
|
1}.`,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = fibonacci(counter)
|
const result = fibonacci(counter)
|
||||||
const resultFormatted = result.map((number) => formatNumberResult(number))
|
const resultFormatted = result.map(number => formatNumberResult(number))
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
result,
|
result,
|
||||||
resultFormatted,
|
resultFormatted,
|
||||||
resultHTML: `<p>Les ${counter} premiers nombres de la suite de fibonacci :<br/> ${resultFormatted.join(', ')}</p>`
|
resultHTML: `<p>Les ${counter} premiers nombres de la suite de fibonacci :<br/> ${resultFormatted.join(
|
||||||
|
', '
|
||||||
|
)}</p>`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ function findLongestWord (string) {
|
|||||||
let stringLength = 0
|
let stringLength = 0
|
||||||
let result = ''
|
let result = ''
|
||||||
|
|
||||||
arrayString.forEach((element) => {
|
arrayString.forEach(element => {
|
||||||
if (element.length > stringLength) {
|
if (element.length > stringLength) {
|
||||||
result = element
|
result = element
|
||||||
stringLength = element.length
|
stringLength = element.length
|
||||||
@ -27,7 +27,7 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
const { string } = argsObject
|
const { string } = argsObject
|
||||||
|
|
||||||
// S'il n'y a pas les champs obligatoire
|
// S'il n'y a pas les champs obligatoire
|
||||||
if (!(string)) {
|
if (!string) {
|
||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,9 +18,14 @@ function heapAlgorithm (string) {
|
|||||||
|
|
||||||
for (let indexString = 0; indexString < string.length; indexString++) {
|
for (let indexString = 0; indexString < string.length; indexString++) {
|
||||||
const firstChar = string[indexString]
|
const firstChar = string[indexString]
|
||||||
const charsLeft = string.substring(0, indexString) + string.substring(indexString + 1)
|
const charsLeft =
|
||||||
|
string.substring(0, indexString) + string.substring(indexString + 1)
|
||||||
const innerPermutations = heapAlgorithm(charsLeft)
|
const innerPermutations = heapAlgorithm(charsLeft)
|
||||||
for (let indexPermutation = 0; indexPermutation < innerPermutations.length; indexPermutation++) {
|
for (
|
||||||
|
let indexPermutation = 0;
|
||||||
|
indexPermutation < innerPermutations.length;
|
||||||
|
indexPermutation++
|
||||||
|
) {
|
||||||
results.push(firstChar + innerPermutations[indexPermutation])
|
results.push(firstChar + innerPermutations[indexPermutation])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,19 +37,26 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
const { string } = argsObject
|
const { string } = argsObject
|
||||||
|
|
||||||
// S'il n'y a pas les champs obligatoire
|
// S'il n'y a pas les champs obligatoire
|
||||||
if (!(string)) {
|
if (!string) {
|
||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si la chaîne de caractère dépasse LIMIT_CHARACTERS caractères
|
// Si la chaîne de caractère dépasse LIMIT_CHARACTERS caractères
|
||||||
const LIMIT_CHARACTERS = 7
|
const LIMIT_CHARACTERS = 7
|
||||||
if (string.length > LIMIT_CHARACTERS) {
|
if (string.length > LIMIT_CHARACTERS) {
|
||||||
return errorHandling(next, { message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec un mot dépassant ${LIMIT_CHARACTERS} caractères.`, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec un mot dépassant ${LIMIT_CHARACTERS} caractères.`,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = heapAlgorithm(string)
|
const result = heapAlgorithm(string)
|
||||||
let resultHTML = `<p>Il y a ${formatNumberResult(result.length)} possibilités d'anagramme pour le mot "${string}" qui contient ${string.length} caractères, la liste : <br/><br/>`
|
let resultHTML = `<p>Il y a ${formatNumberResult(
|
||||||
result.forEach((string) => {
|
result.length
|
||||||
|
)} possibilités d'anagramme pour le mot "${string}" qui contient ${
|
||||||
|
string.length
|
||||||
|
} caractères, la liste : <br/><br/>`
|
||||||
|
result.forEach(string => {
|
||||||
resultHTML += string + '<br/>'
|
resultHTML += string + '<br/>'
|
||||||
})
|
})
|
||||||
resultHTML += '</p>'
|
resultHTML += '</p>'
|
||||||
|
@ -8,7 +8,10 @@ const { requiredFields } = require('../../config/errors')
|
|||||||
* @example reverseString('Hello') → 'olleH'
|
* @example reverseString('Hello') → 'olleH'
|
||||||
*/
|
*/
|
||||||
function reverseString (string) {
|
function reverseString (string) {
|
||||||
return string.split('').reverse().join('')
|
return string
|
||||||
|
.split('')
|
||||||
|
.reverse()
|
||||||
|
.join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,12 +31,15 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
let { string } = argsObject
|
let { string } = argsObject
|
||||||
|
|
||||||
// S'il n'y a pas les champs obligatoire
|
// S'il n'y a pas les champs obligatoire
|
||||||
if (!(string)) {
|
if (!string) {
|
||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof string !== 'string') {
|
if (typeof string !== 'string') {
|
||||||
return errorHandling(next, { message: 'Vous devez rentré une chaîne de caractère valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Vous devez rentré une chaîne de caractère valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
string = string.toLowerCase()
|
string = string.toLowerCase()
|
||||||
@ -43,6 +49,10 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
isPalindrome: isPalindromeResult,
|
isPalindrome: isPalindromeResult,
|
||||||
reverseString: reverseStringResult,
|
reverseString: reverseStringResult,
|
||||||
resultHTML: `<p>"${string}" ${(isPalindromeResult) ? 'est' : "n'est pas"} un palindrome car <br/> "${string}" ${(isPalindromeResult) ? '===' : '!=='} "${reverseStringResult}"</p>`
|
resultHTML: `<p>"${string}" ${
|
||||||
|
isPalindromeResult ? 'est' : "n'est pas"
|
||||||
|
} un palindrome car <br/> "${string}" ${
|
||||||
|
isPalindromeResult ? '===' : '!=='
|
||||||
|
} "${reverseStringResult}"</p>`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,19 @@ module.exports = async ({ res, next }, argsObject) => {
|
|||||||
|
|
||||||
// Si ce n'est pas une url
|
// Si ce n'est pas une url
|
||||||
if (!validator.isURL(url)) {
|
if (!validator.isURL(url)) {
|
||||||
return errorHandling(next, { message: 'Veuillez entré une URL valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Veuillez entré une URL valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si ce n'est pas de type slug
|
// Si ce n'est pas de type slug
|
||||||
if (!validator.isSlug(shortcutName)) {
|
if (!validator.isSlug(shortcutName)) {
|
||||||
return errorHandling(next, { message: "Le nom de votre raccourci doit être de type slug (ne pas contenir d'espaces, ni de caractères spéciaux).", statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message:
|
||||||
|
"Le nom de votre raccourci doit être de type slug (ne pas contenir d'espaces, ni de caractères spéciaux).",
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanitize shortcutName
|
// Sanitize shortcutName
|
||||||
@ -31,20 +38,33 @@ module.exports = async ({ res, next }, argsObject) => {
|
|||||||
const urlInDatabase = await shortLinks.findOne({ where: { url } })
|
const urlInDatabase = await shortLinks.findOne({ where: { url } })
|
||||||
if (urlInDatabase) {
|
if (urlInDatabase) {
|
||||||
const urlShort = `https://short-links.divlo.fr/?q=${urlInDatabase.shortcut}`
|
const urlShort = `https://short-links.divlo.fr/?q=${urlInDatabase.shortcut}`
|
||||||
return errorHandling(next, { message: `L'url a déjà été raccourcie... <br/> <br/> <a target="_blank" rel="noopener noreferrer" href="${urlShort}">${urlShort}</a>`, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: `L'url a déjà été raccourcie... <br/> <br/> <a target="_blank" rel="noopener noreferrer" href="${urlShort}">${urlShort}</a>`,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si le nom du raccourci existe déjà
|
// Si le nom du raccourci existe déjà
|
||||||
const shortcutInDatabase = await shortLinks.findOne({ where: { shortcut: shortcutName } })
|
const shortcutInDatabase = await shortLinks.findOne({
|
||||||
|
where: { shortcut: shortcutName }
|
||||||
|
})
|
||||||
if (shortcutInDatabase) {
|
if (shortcutInDatabase) {
|
||||||
const urlShort = `https://short-links.divlo.fr/?q=${shortcutInDatabase.shortcut}`
|
const urlShort = `https://short-links.divlo.fr/?q=${shortcutInDatabase.shortcut}`
|
||||||
return errorHandling(next, { message: `Le nom du raccourci a déjà été utilisé... <br/> <br/> <a target="_blank" rel="noopener noreferrer" href="${urlShort}">${urlShort}</a>`, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: `Le nom du raccourci a déjà été utilisé... <br/> <br/> <a target="_blank" rel="noopener noreferrer" href="${urlShort}">${urlShort}</a>`,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ajout du lien raccourci
|
// Ajout du lien raccourci
|
||||||
const result = await shortLinks.create({ url, shortcut: shortcutName })
|
const result = await shortLinks.create({ url, shortcut: shortcutName })
|
||||||
const shortcutLinkResult = `https://short-links.divlo.fr/?q=${result.shortcut}`
|
const shortcutLinkResult = `https://short-links.divlo.fr/?q=${result.shortcut}`
|
||||||
return res.status(200).json({ resultHTML: `URL Raccourcie : <br/> <br/> <a target="_blank" rel="noopener noreferrer" href="${shortcutLinkResult}">${shortcutLinkResult}</a>`, result: shortcutLinkResult })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
resultHTML: `URL Raccourcie : <br/> <br/> <a target="_blank" rel="noopener noreferrer" href="${shortcutLinkResult}">${shortcutLinkResult}</a>`,
|
||||||
|
result: shortcutLinkResult
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
|
@ -26,11 +26,21 @@ const randomNumberOutput = ({ res, next }, argsObject) => {
|
|||||||
min = parseInt(min)
|
min = parseInt(min)
|
||||||
max = parseInt(max)
|
max = parseInt(max)
|
||||||
if (isNaN(min) || isNaN(max)) {
|
if (isNaN(min) || isNaN(max)) {
|
||||||
return errorHandling(next, { message: 'Les paramètres min et max doivent être des nombres...', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Les paramètres min et max doivent être des nombres...',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = randomNumber(min, max)
|
const result = randomNumber(min, max)
|
||||||
return res.status(200).json({ result, resultHTML: `<p>Nombre aléatoire compris entre ${min} inclus et ${max} inclus : <strong>${formatNumberResult(result)}</strong></p>` })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
result,
|
||||||
|
resultHTML: `<p>Nombre aléatoire compris entre ${min} inclus et ${max} inclus : <strong>${formatNumberResult(
|
||||||
|
result
|
||||||
|
)}</strong></p>`
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.randomNumber = randomNumber
|
exports.randomNumber = randomNumber
|
||||||
|
@ -8,9 +8,7 @@ module.exports = async ({ res, next }, _argsObject) => {
|
|||||||
try {
|
try {
|
||||||
const quote = await Quotes.findOne({
|
const quote = await Quotes.findOne({
|
||||||
order: sequelize.random(),
|
order: sequelize.random(),
|
||||||
include: [
|
include: [{ model: Users, attributes: ['name', 'logo'] }],
|
||||||
{ model: Users, attributes: ['name', 'logo'] }
|
|
||||||
],
|
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: ['isValidated']
|
exclude: ['isValidated']
|
||||||
},
|
},
|
||||||
|
@ -23,15 +23,18 @@ function getRandomArrayElement (array) {
|
|||||||
|
|
||||||
async function getAmazonProductList (subject) {
|
async function getAmazonProductList (subject) {
|
||||||
const url = `https://www.amazon.fr/s?k=${subject}`
|
const url = `https://www.amazon.fr/s?k=${subject}`
|
||||||
const { data } = await axios.get(`http://api.scraperapi.com/?api_key=${SCRAPER_API_KEY}&url=${url}`)
|
const { data } = await axios.get(
|
||||||
const { document } = (new JSDOM(data)).window
|
`http://api.scraperapi.com/?api_key=${SCRAPER_API_KEY}&url=${url}`
|
||||||
|
)
|
||||||
|
const { document } = new JSDOM(data).window
|
||||||
const amazonProductList = document.querySelectorAll('.s-result-item')
|
const amazonProductList = document.querySelectorAll('.s-result-item')
|
||||||
const productsList = []
|
const productsList = []
|
||||||
for (const indexProduct in amazonProductList) {
|
for (const indexProduct in amazonProductList) {
|
||||||
try {
|
try {
|
||||||
const elementProduct = amazonProductList[indexProduct]
|
const elementProduct = amazonProductList[indexProduct]
|
||||||
const productImage = elementProduct.querySelector('.s-image')
|
const productImage = elementProduct.querySelector('.s-image')
|
||||||
const originalPrice = elementProduct.querySelector('.a-price-whole').innerHTML
|
const originalPrice = elementProduct.querySelector('.a-price-whole')
|
||||||
|
.innerHTML
|
||||||
productsList.push({
|
productsList.push({
|
||||||
name: productImage.alt,
|
name: productImage.alt,
|
||||||
image: productImage.src,
|
image: productImage.src,
|
||||||
|
@ -29,28 +29,41 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
const { numbersList } = argsObject
|
const { numbersList } = argsObject
|
||||||
|
|
||||||
// S'il n'y a pas les champs obligatoire
|
// S'il n'y a pas les champs obligatoire
|
||||||
if (!(numbersList)) {
|
if (!numbersList) {
|
||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
const numbersListArray = numbersList.split(',').map((number) => number.trim().replace(' ', '')).map(Number)
|
const numbersListArray = numbersList
|
||||||
|
.split(',')
|
||||||
|
.map(number => number.trim().replace(' ', ''))
|
||||||
|
.map(Number)
|
||||||
|
|
||||||
// Si ce n'est pas une liste de nombres
|
// Si ce n'est pas une liste de nombres
|
||||||
if (numbersListArray.includes(NaN)) {
|
if (numbersListArray.includes(NaN)) {
|
||||||
return errorHandling(next, { message: 'Vous devez rentrer une liste de nombres séparée par des virgules valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message:
|
||||||
|
'Vous devez rentrer une liste de nombres séparée par des virgules valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si la taille du tableau dépasse LIMIT_ARRAY_LENGTH
|
// Si la taille du tableau dépasse LIMIT_ARRAY_LENGTH
|
||||||
const LIMIT_ARRAY_LENGTH = 31
|
const LIMIT_ARRAY_LENGTH = 31
|
||||||
if (numbersListArray.length >= LIMIT_ARRAY_LENGTH) {
|
if (numbersListArray.length >= LIMIT_ARRAY_LENGTH) {
|
||||||
return errorHandling(next, { message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec une liste de nombres dépassant ${LIMIT_ARRAY_LENGTH - 1} nombres.`, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: `Par souci de performance, vous ne pouvez pas exécuter cette fonction avec une liste de nombres dépassant ${LIMIT_ARRAY_LENGTH -
|
||||||
|
1} nombres.`,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = sortArray(numbersListArray)
|
const result = sortArray(numbersListArray)
|
||||||
const resultFormatted = result.map((number) => formatNumberResult(number))
|
const resultFormatted = result.map(number => formatNumberResult(number))
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
result,
|
result,
|
||||||
resultFormatted,
|
resultFormatted,
|
||||||
resultHTML: `<p>La liste de nombres dans l'ordre croissant :<br/> ${resultFormatted.join(', ')}</p>`
|
resultHTML: `<p>La liste de nombres dans l'ordre croissant :<br/> ${resultFormatted.join(
|
||||||
|
', '
|
||||||
|
)}</p>`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -26,21 +26,48 @@ module.exports = ({ res, next }, argsObject) => {
|
|||||||
let { cityName } = argsObject
|
let { cityName } = argsObject
|
||||||
|
|
||||||
// S'il n'y a pas les champs obligatoire
|
// S'il n'y a pas les champs obligatoire
|
||||||
if (!(cityName)) {
|
if (!cityName) {
|
||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
cityName = cityName.split(' ').join('+')
|
cityName = cityName.split(' ').join('+')
|
||||||
|
|
||||||
// Récupère les données météo grâce à l'API : openweathermap.org. (→ avec limite de 50 requêtes par minute)
|
// Récupère les données météo grâce à l'API : openweathermap.org. (→ avec limite de 50 requêtes par minute)
|
||||||
queue.request(() => {
|
queue.request(
|
||||||
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${cityName}&lang=fr&units=metric&appid=${WEATHER_API_KEY}`)
|
() => {
|
||||||
.then((response) => {
|
axios
|
||||||
|
.get(
|
||||||
|
`https://api.openweathermap.org/data/2.5/weather?q=${cityName}&lang=fr&units=metric&appid=${WEATHER_API_KEY}`
|
||||||
|
)
|
||||||
|
.then(response => {
|
||||||
const json = response.data
|
const json = response.data
|
||||||
const showDateTimeValue = dateTimeUTC((json.timezone / 60 / 60).toString()).showDateTimeValue
|
const showDateTimeValue = dateTimeUTC(
|
||||||
const resultHTML = `<p>🌎 Position : <a href="https://www.google.com/maps/search/?api=1&query=${json.coord.lat},${json.coord.lon}" rel="noopener noreferrer" target="_blank">${json.name}, ${json.sys.country}</a><br/>⏰ Date et heure : ${showDateTimeValue} <br/>☁️ Météo : ${capitalize(json.weather[0].description)}<br/>🌡️ Température : ${json.main.temp} °C<br/> 💧 Humidité : ${json.main.humidity}% <br/> <img src="https://openweathermap.org/img/wn/${json.weather[0].icon}@2x.png"/></p>`
|
(json.timezone / 60 / 60).toString()
|
||||||
|
).showDateTimeValue
|
||||||
|
const resultHTML = `<p>🌎 Position : <a href="https://www.google.com/maps/search/?api=1&query=${
|
||||||
|
json.coord.lat
|
||||||
|
},${json.coord.lon}" rel="noopener noreferrer" target="_blank">${
|
||||||
|
json.name
|
||||||
|
}, ${
|
||||||
|
json.sys.country
|
||||||
|
}</a><br/>⏰ Date et heure : ${showDateTimeValue} <br/>☁️ Météo : ${capitalize(
|
||||||
|
json.weather[0].description
|
||||||
|
)}<br/>🌡️ Température : ${json.main.temp} °C<br/> 💧 Humidité : ${
|
||||||
|
json.main.humidity
|
||||||
|
}% <br/> <img src="https://openweathermap.org/img/wn/${
|
||||||
|
json.weather[0].icon
|
||||||
|
}@2x.png"/></p>`
|
||||||
return res.status(200).json({ result: json, resultHTML })
|
return res.status(200).json({ result: json, resultHTML })
|
||||||
})
|
})
|
||||||
.catch(() => errorHandling(next, { message: "La ville n'existe pas (dans l'API de openweathermap.org).", statusCode: 404 }))
|
.catch(() =>
|
||||||
}, 'everyone', 'weatherRequest')
|
errorHandling(next, {
|
||||||
|
message:
|
||||||
|
"La ville n'existe pas (dans l'API de openweathermap.org).",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
|
)
|
||||||
|
},
|
||||||
|
'everyone',
|
||||||
|
'weatherRequest'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,8 @@ function showDateTime (timeNow) {
|
|||||||
const hour = ('0' + timeNow.getHours()).slice(-2)
|
const hour = ('0' + timeNow.getHours()).slice(-2)
|
||||||
const minute = ('0' + timeNow.getMinutes()).slice(-2)
|
const minute = ('0' + timeNow.getMinutes()).slice(-2)
|
||||||
const second = ('0' + timeNow.getSeconds()).slice(-2)
|
const second = ('0' + timeNow.getSeconds()).slice(-2)
|
||||||
const showDateTimeValue = day + '/' + month + '/' + year + ' - ' + hour + ':' + minute + ':' + second
|
const showDateTimeValue =
|
||||||
|
day + '/' + month + '/' + year + ' - ' + hour + ':' + minute + ':' + second
|
||||||
const objectDateTime = {
|
const objectDateTime = {
|
||||||
year: year,
|
year: year,
|
||||||
month: month,
|
month: month,
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
const Sequelize = require('sequelize')
|
const Sequelize = require('sequelize')
|
||||||
const { DATABASE } = require('../config/config')
|
const { DATABASE } = require('../config/config')
|
||||||
|
|
||||||
const sequelize = new Sequelize(DATABASE.name, DATABASE.user, DATABASE.password, {
|
const sequelize = new Sequelize(
|
||||||
|
DATABASE.name,
|
||||||
|
DATABASE.user,
|
||||||
|
DATABASE.password,
|
||||||
|
{
|
||||||
dialect: 'mysql',
|
dialect: 'mysql',
|
||||||
host: DATABASE.host
|
host: DATABASE.host
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
module.exports = sequelize
|
module.exports = sequelize
|
||||||
|
@ -12,7 +12,11 @@ const DEFAULT_OPTIONS = {
|
|||||||
* @param {*} Model Model Sequelize
|
* @param {*} Model Model Sequelize
|
||||||
* @param {Object} options Options avec clause where etc.
|
* @param {Object} options Options avec clause where etc.
|
||||||
*/
|
*/
|
||||||
async function getPagesHelper ({ req, res, next }, Model, options = DEFAULT_OPTIONS) {
|
async function getPagesHelper (
|
||||||
|
{ req, res, next },
|
||||||
|
Model,
|
||||||
|
options = DEFAULT_OPTIONS
|
||||||
|
) {
|
||||||
const page = helperQueryNumber(req.query.page, 1)
|
const page = helperQueryNumber(req.query.page, 1)
|
||||||
const limit = helperQueryNumber(req.query.limit, 10)
|
const limit = helperQueryNumber(req.query.limit, 10)
|
||||||
const offset = (page - 1) * limit
|
const offset = (page - 1) * limit
|
||||||
@ -23,7 +27,7 @@ async function getPagesHelper ({ req, res, next }, Model, options = DEFAULT_OPTI
|
|||||||
...options
|
...options
|
||||||
})
|
})
|
||||||
const { count, rows } = result
|
const { count, rows } = result
|
||||||
const hasMore = (page * limit) < count
|
const hasMore = page * limit < count
|
||||||
return res.status(200).json({ totalItems: count, hasMore, rows })
|
return res.status(200).json({ totalItems: count, hasMore, rows })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
@ -15,7 +15,12 @@ const { EMAIL_INFO, FRONT_END_HOST } = require('../assets/config/config')
|
|||||||
const transporter = require('../assets/config/transporter')
|
const transporter = require('../assets/config/transporter')
|
||||||
const { emailQuoteTemplate } = require('../assets/config/emails')
|
const { emailQuoteTemplate } = require('../assets/config/emails')
|
||||||
|
|
||||||
const handleEditFunction = async (res, resultFunction, { title, slug, description, type, categorieId, isOnline }, imageName = false) => {
|
const handleEditFunction = async (
|
||||||
|
res,
|
||||||
|
resultFunction,
|
||||||
|
{ title, slug, description, type, categorieId, isOnline },
|
||||||
|
imageName = false
|
||||||
|
) => {
|
||||||
resultFunction.title = title
|
resultFunction.title = title
|
||||||
resultFunction.slug = slug
|
resultFunction.slug = slug
|
||||||
resultFunction.description = description
|
resultFunction.description = description
|
||||||
@ -32,23 +37,41 @@ const handleEditFunction = async (res, resultFunction, { title, slug, descriptio
|
|||||||
exports.getFunctions = async (req, res, next) => {
|
exports.getFunctions = async (req, res, next) => {
|
||||||
const categoryId = helperQueryNumber(req.query.categoryId, 0)
|
const categoryId = helperQueryNumber(req.query.categoryId, 0)
|
||||||
let search = req.query.search
|
let search = req.query.search
|
||||||
try { search = search.toLowerCase() } catch {};
|
try {
|
||||||
|
search = search.toLowerCase()
|
||||||
|
} catch {}
|
||||||
const options = {
|
const options = {
|
||||||
where: {
|
where: {
|
||||||
// Trie par catégorie
|
// Trie par catégorie
|
||||||
...(categoryId !== 0) && { categorieId: categoryId },
|
...(categoryId !== 0 && { categorieId: categoryId }),
|
||||||
// Recherche
|
// Recherche
|
||||||
...(search != null) && {
|
...(search != null && {
|
||||||
[Sequelize.Op.or]: [
|
[Sequelize.Op.or]: [
|
||||||
{ title: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('title')), 'LIKE', `%${search}%`) },
|
{
|
||||||
{ slug: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('slug')), 'LIKE', `%${search}%`) },
|
title: Sequelize.where(
|
||||||
{ description: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('description')), 'LIKE', `%${search}%`) }
|
Sequelize.fn('LOWER', Sequelize.col('title')),
|
||||||
]
|
'LIKE',
|
||||||
}
|
`%${search}%`
|
||||||
|
)
|
||||||
},
|
},
|
||||||
include: [
|
{
|
||||||
{ model: Categories, attributes: ['name', 'color'] }
|
slug: Sequelize.where(
|
||||||
],
|
Sequelize.fn('LOWER', Sequelize.col('slug')),
|
||||||
|
'LIKE',
|
||||||
|
`%${search}%`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: Sequelize.where(
|
||||||
|
Sequelize.fn('LOWER', Sequelize.col('description')),
|
||||||
|
'LIKE',
|
||||||
|
`%${search}%`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
include: [{ model: Categories, attributes: ['name', 'color'] }],
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline']
|
exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline']
|
||||||
},
|
},
|
||||||
@ -61,18 +84,21 @@ exports.getFunctionBySlug = (req, res, next) => {
|
|||||||
const { slug } = req.params
|
const { slug } = req.params
|
||||||
Functions.findOne({
|
Functions.findOne({
|
||||||
where: { slug },
|
where: { slug },
|
||||||
include: [
|
include: [{ model: Categories, attributes: ['name', 'color'] }]
|
||||||
{ model: Categories, attributes: ['name', 'color'] }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then(result => {
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La fonction n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
try { result.utilizationForm = JSON.parse(result.utilizationForm) } catch {}
|
try {
|
||||||
|
result.utilizationForm = JSON.parse(result.utilizationForm)
|
||||||
|
} catch {}
|
||||||
return res.status(200).json(result)
|
return res.status(200).json(result)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
})
|
})
|
||||||
@ -83,28 +109,49 @@ exports.postFunction = (req, res, next) => {
|
|||||||
const image = req.files.image
|
const image = req.files.image
|
||||||
const errors = validationResult(req)
|
const errors = validationResult(req)
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: errors.array()[0].msg,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if ((!image || image.truncated) && (
|
if (
|
||||||
image.mimetype !== 'image/png' ||
|
(!image || image.truncated) &&
|
||||||
|
(image.mimetype !== 'image/png' ||
|
||||||
image.mimetype !== 'image/jpg' ||
|
image.mimetype !== 'image/jpg' ||
|
||||||
image.mimetype !== 'image/jpeg'
|
image.mimetype !== 'image/jpeg')
|
||||||
)) {
|
) {
|
||||||
return errorHandling(next, { message: 'La fonction doit avoir une image valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'La fonction doit avoir une image valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const splitedImageName = image.name.split('.')
|
const splitedImageName = image.name.split('.')
|
||||||
if (splitedImageName.length !== 2) return errorHandling(next, serverError)
|
if (splitedImageName.length !== 2) return errorHandling(next, serverError)
|
||||||
const imageName = slug + '.' + splitedImageName[1]
|
const imageName = slug + '.' + splitedImageName[1]
|
||||||
image.mv(path.join(__dirname, '..', 'assets', 'images', 'functions') + '/' + imageName, async (error) => {
|
image.mv(
|
||||||
|
path.join(__dirname, '..', 'assets', 'images', 'functions') +
|
||||||
|
'/' +
|
||||||
|
imageName,
|
||||||
|
async error => {
|
||||||
if (error) return errorHandling(next, serverError)
|
if (error) return errorHandling(next, serverError)
|
||||||
try {
|
try {
|
||||||
const result = await Functions.create({ title, slug, description, type, categorieId, image: `/images/functions/${imageName}` })
|
const result = await Functions.create({
|
||||||
return res.status(201).json({ message: 'La fonction a été correctement ajouté!', result })
|
title,
|
||||||
|
slug,
|
||||||
|
description,
|
||||||
|
type,
|
||||||
|
categorieId,
|
||||||
|
image: `/images/functions/${imageName}`
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
.status(201)
|
||||||
|
.json({ message: 'La fonction a été correctement ajouté!', result })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.putFunction = async (req, res, next) => {
|
exports.putFunction = async (req, res, next) => {
|
||||||
@ -113,43 +160,74 @@ exports.putFunction = async (req, res, next) => {
|
|||||||
const image = req.files.image
|
const image = req.files.image
|
||||||
const errors = validationResult(req)
|
const errors = validationResult(req)
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: errors.array()[0].msg,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// Vérifie si la fonction existe
|
// Vérifie si la fonction existe
|
||||||
const resultFunction = await Functions.findOne({ where: { id } })
|
const resultFunction = await Functions.findOne({ where: { id } })
|
||||||
if (!resultFunction) {
|
if (!resultFunction) {
|
||||||
return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La fonction n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vérifie si le slug existe déjà
|
// Vérifie si le slug existe déjà
|
||||||
const FunctionSlug = await Functions.findOne({ where: { slug } })
|
const FunctionSlug = await Functions.findOne({ where: { slug } })
|
||||||
if (!FunctionSlug && FunctionSlug.id !== resultFunction.id) {
|
if (!FunctionSlug && FunctionSlug.id !== resultFunction.id) {
|
||||||
return errorHandling(next, { message: 'Le slug existe déjà...', statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: 'Le slug existe déjà...',
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sauvegarde de la fonction
|
// Sauvegarde de la fonction
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
if (image.truncated && (
|
if (
|
||||||
image.mimetype !== 'image/png' ||
|
image.truncated &&
|
||||||
|
(image.mimetype !== 'image/png' ||
|
||||||
image.mimetype !== 'image/jpg' ||
|
image.mimetype !== 'image/jpg' ||
|
||||||
image.mimetype !== 'image/jpeg'
|
image.mimetype !== 'image/jpeg')
|
||||||
)) {
|
) {
|
||||||
return errorHandling(next, { message: 'La fonction doit avoir une image valide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'La fonction doit avoir une image valide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const splitedImageName = image.name.split('.')
|
const splitedImageName = image.name.split('.')
|
||||||
if (splitedImageName.length !== 2) return errorHandling(next, serverError)
|
if (splitedImageName.length !== 2) return errorHandling(next, serverError)
|
||||||
const imageName = slug + '.' + splitedImageName[1]
|
const imageName = slug + '.' + splitedImageName[1]
|
||||||
// Supprime les anciennes images
|
// Supprime les anciennes images
|
||||||
const functionPath = path.join(__dirname, '..', 'assets', 'images', 'functions')
|
const functionPath = path.join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'assets',
|
||||||
|
'images',
|
||||||
|
'functions'
|
||||||
|
)
|
||||||
deleteFilesNameStartWith(slug, functionPath, () => {
|
deleteFilesNameStartWith(slug, functionPath, () => {
|
||||||
image.mv(path.join(functionPath, imageName), async (error) => {
|
image.mv(path.join(functionPath, imageName), async error => {
|
||||||
if (error) return errorHandling(next, serverError)
|
if (error) return errorHandling(next, serverError)
|
||||||
return await handleEditFunction(res, resultFunction, { title, slug, description, type, categorieId, isOnline }, imageName)
|
return await handleEditFunction(
|
||||||
|
res,
|
||||||
|
resultFunction,
|
||||||
|
{ title, slug, description, type, categorieId, isOnline },
|
||||||
|
imageName
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return await handleEditFunction(res, resultFunction, { title, slug, description, type, categorieId, isOnline })
|
return await handleEditFunction(res, resultFunction, {
|
||||||
|
title,
|
||||||
|
slug,
|
||||||
|
description,
|
||||||
|
type,
|
||||||
|
categorieId,
|
||||||
|
isOnline
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
@ -165,7 +243,10 @@ exports.putFunctionArticle = async (req, res, next) => {
|
|||||||
// Vérifie si la fonction existe
|
// Vérifie si la fonction existe
|
||||||
const resultFunction = await Functions.findOne({ where: { id } })
|
const resultFunction = await Functions.findOne({ where: { id } })
|
||||||
if (!resultFunction) {
|
if (!resultFunction) {
|
||||||
return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La fonction n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
resultFunction.article = article
|
resultFunction.article = article
|
||||||
const result = await resultFunction.save()
|
const result = await resultFunction.save()
|
||||||
@ -184,7 +265,10 @@ exports.putFunctionForm = async (req, res, next) => {
|
|||||||
// Vérifie si la fonction existe
|
// Vérifie si la fonction existe
|
||||||
const resultFunction = await Functions.findOne({ where: { id } })
|
const resultFunction = await Functions.findOne({ where: { id } })
|
||||||
if (!resultFunction) {
|
if (!resultFunction) {
|
||||||
return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La fonction n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
resultFunction.utilizationForm = JSON.stringify(form)
|
resultFunction.utilizationForm = JSON.stringify(form)
|
||||||
const result = await resultFunction.save()
|
const result = await resultFunction.save()
|
||||||
@ -200,14 +284,19 @@ exports.deleteFunction = async (req, res, next) => {
|
|||||||
try {
|
try {
|
||||||
const result = await Functions.findOne({ where: { id } })
|
const result = await Functions.findOne({ where: { id } })
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La fonction n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (result.image !== '/images/functions/default.png') {
|
if (result.image !== '/images/functions/default.png') {
|
||||||
const filePath = path.join(__dirname, '..', 'assets', result.image)
|
const filePath = path.join(__dirname, '..', 'assets', result.image)
|
||||||
fs.unlinkSync(filePath) // supprime le fichier
|
fs.unlinkSync(filePath) // supprime le fichier
|
||||||
}
|
}
|
||||||
await result.destroy()
|
await result.destroy()
|
||||||
res.status(200).json({ message: 'La fonction a été correctement supprimé!' })
|
res
|
||||||
|
.status(200)
|
||||||
|
.json({ message: 'La fonction a été correctement supprimé!' })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -217,11 +306,15 @@ exports.deleteFunction = async (req, res, next) => {
|
|||||||
exports.postCategory = async (req, res, next) => {
|
exports.postCategory = async (req, res, next) => {
|
||||||
const { name, color } = req.body
|
const { name, color } = req.body
|
||||||
if (!(name && color)) {
|
if (!(name && color)) {
|
||||||
return errorHandling(next, { message: 'La catégorie doit avoir un nom et une couleur.' })
|
return errorHandling(next, {
|
||||||
|
message: 'La catégorie doit avoir un nom et une couleur.'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const result = await Categories.create({ name, color })
|
const result = await Categories.create({ name, color })
|
||||||
return res.status(201).json({ message: 'La catégorie a bien été crée!', result })
|
return res
|
||||||
|
.status(201)
|
||||||
|
.json({ message: 'La catégorie a bien été crée!', result })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -232,7 +325,9 @@ exports.putCategory = async (req, res, next) => {
|
|||||||
const { name, color } = req.body
|
const { name, color } = req.body
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
if (!(name && color && id)) {
|
if (!(name && color && id)) {
|
||||||
return errorHandling(next, { message: 'La catégorie doit avoir un nom, une couleur et un id.' })
|
return errorHandling(next, {
|
||||||
|
message: 'La catégorie doit avoir un nom, une couleur et un id.'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const category = await Categories.findOne({ where: { id } })
|
const category = await Categories.findOne({ where: { id } })
|
||||||
@ -242,7 +337,9 @@ exports.putCategory = async (req, res, next) => {
|
|||||||
category.name = name
|
category.name = name
|
||||||
category.color = color
|
category.color = color
|
||||||
const result = await category.save()
|
const result = await category.save()
|
||||||
return res.status(200).json({ message: 'La catégorie a bien été modifiée!', result })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ message: 'La catégorie a bien été modifiée!', result })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -257,7 +354,9 @@ exports.deleteCategory = async (req, res, next) => {
|
|||||||
return errorHandling(next, { message: "La catégorie n'existe pas." })
|
return errorHandling(next, { message: "La catégorie n'existe pas." })
|
||||||
}
|
}
|
||||||
await category.destroy()
|
await category.destroy()
|
||||||
return res.status(200).json({ message: 'La catégorie a bien été supprimée!' })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ message: 'La catégorie a bien été supprimée!' })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -269,9 +368,7 @@ exports.getQuotes = async (req, res, next) => {
|
|||||||
where: {
|
where: {
|
||||||
isValidated: 0
|
isValidated: 0
|
||||||
},
|
},
|
||||||
include: [
|
include: [{ model: Users, attributes: ['name', 'logo'] }],
|
||||||
{ model: Users, attributes: ['name', 'logo'] }
|
|
||||||
],
|
|
||||||
order: [['createdAt', 'DESC']]
|
order: [['createdAt', 'DESC']]
|
||||||
}
|
}
|
||||||
return await getPagesHelper({ req, res, next }, Quotes, options)
|
return await getPagesHelper({ req, res, next }, Quotes, options)
|
||||||
@ -282,19 +379,23 @@ exports.putQuote = async (req, res, next) => {
|
|||||||
const { isValid } = req.body
|
const { isValid } = req.body
|
||||||
try {
|
try {
|
||||||
if (typeof isValid !== 'boolean') {
|
if (typeof isValid !== 'boolean') {
|
||||||
return errorHandling(next, { message: 'isValid doit être un booléen.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'isValid doit être un booléen.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const quote = await Quotes.findOne({
|
const quote = await Quotes.findOne({
|
||||||
where: {
|
where: {
|
||||||
id,
|
id,
|
||||||
isValidated: 0
|
isValidated: 0
|
||||||
},
|
},
|
||||||
include: [
|
include: [{ model: Users, attributes: ['name', 'email'] }]
|
||||||
{ model: Users, attributes: ['name', 'email'] }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
if (!quote) {
|
if (!quote) {
|
||||||
return errorHandling(next, { message: "La citation n'existe pas (ou est déjà validé).", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La citation n'existe pas (ou est déjà validé).",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await transporter.sendMail({
|
await transporter.sendMail({
|
||||||
@ -307,10 +408,14 @@ exports.putQuote = async (req, res, next) => {
|
|||||||
if (isValid) {
|
if (isValid) {
|
||||||
quote.isValidated = true
|
quote.isValidated = true
|
||||||
await quote.save()
|
await quote.save()
|
||||||
return res.status(200).json({ message: 'La citation a bien été validée!' })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ message: 'La citation a bien été validée!' })
|
||||||
} else {
|
} else {
|
||||||
await quote.destroy()
|
await quote.destroy()
|
||||||
return res.status(200).json({ imessage: 'La citation a bien été supprimée!' })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ imessage: 'La citation a bien été supprimée!' })
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
@ -4,10 +4,10 @@ const { serverError } = require('../assets/config/errors')
|
|||||||
|
|
||||||
exports.getCategories = (_req, res, next) => {
|
exports.getCategories = (_req, res, next) => {
|
||||||
Categories.findAll()
|
Categories.findAll()
|
||||||
.then((result) => {
|
.then(result => {
|
||||||
res.status(200).json(result)
|
res.status(200).json(result)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
})
|
})
|
||||||
|
@ -9,9 +9,7 @@ exports.getCommentsByFunctionId = async (req, res, next) => {
|
|||||||
const { functionId } = req.params
|
const { functionId } = req.params
|
||||||
const options = {
|
const options = {
|
||||||
where: { functionId },
|
where: { functionId },
|
||||||
include: [
|
include: [{ model: Users, attributes: ['name', 'logo'] }],
|
||||||
{ model: Users, attributes: ['name', 'logo'] }
|
|
||||||
],
|
|
||||||
order: [['createdAt', 'DESC']]
|
order: [['createdAt', 'DESC']]
|
||||||
}
|
}
|
||||||
return await getPagesHelper({ req, res, next }, Comments, options)
|
return await getPagesHelper({ req, res, next }, Comments, options)
|
||||||
@ -21,14 +19,26 @@ exports.postCommentsByFunctionId = async (req, res, next) => {
|
|||||||
const { functionId } = req.params
|
const { functionId } = req.params
|
||||||
const { message } = req.body
|
const { message } = req.body
|
||||||
try {
|
try {
|
||||||
const resultFunction = await Functions.findOne({ where: { id: functionId } })
|
const resultFunction = await Functions.findOne({
|
||||||
|
where: { id: functionId }
|
||||||
|
})
|
||||||
if (!resultFunction) {
|
if (!resultFunction) {
|
||||||
return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La fonction n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (!message) {
|
if (!message) {
|
||||||
return errorHandling(next, { message: 'Vous ne pouvez pas poster de commentaire vide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Vous ne pouvez pas poster de commentaire vide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const comment = await Comments.create({ message, userId: req.userId, functionId })
|
const comment = await Comments.create({
|
||||||
|
message,
|
||||||
|
userId: req.userId,
|
||||||
|
functionId
|
||||||
|
})
|
||||||
return res.status(201).json(comment)
|
return res.status(201).json(comment)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
@ -39,12 +49,19 @@ exports.postCommentsByFunctionId = async (req, res, next) => {
|
|||||||
exports.deleteCommentById = async (req, res, next) => {
|
exports.deleteCommentById = async (req, res, next) => {
|
||||||
const { commentId } = req.params
|
const { commentId } = req.params
|
||||||
try {
|
try {
|
||||||
const comment = await Comments.findOne({ where: { userId: req.userId, id: parseInt(commentId) } })
|
const comment = await Comments.findOne({
|
||||||
|
where: { userId: req.userId, id: parseInt(commentId) }
|
||||||
|
})
|
||||||
if (!comment) {
|
if (!comment) {
|
||||||
return errorHandling(next, { message: "Le commentaire n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "Le commentaire n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
await comment.destroy()
|
await comment.destroy()
|
||||||
return res.status(200).json({ message: 'Le commentaire a bien été supprimé.' })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ message: 'Le commentaire a bien été supprimé.' })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -55,16 +72,26 @@ exports.putCommentsById = async (req, res, next) => {
|
|||||||
const { commentId } = req.params
|
const { commentId } = req.params
|
||||||
const { message } = req.body
|
const { message } = req.body
|
||||||
if (!message) {
|
if (!message) {
|
||||||
return errorHandling(next, { message: 'Vous ne pouvez pas poster de commentaire vide.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'Vous ne pouvez pas poster de commentaire vide.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const comment = await Comments.findOne({ where: { userId: req.userId, id: parseInt(commentId) } })
|
const comment = await Comments.findOne({
|
||||||
|
where: { userId: req.userId, id: parseInt(commentId) }
|
||||||
|
})
|
||||||
if (!comment) {
|
if (!comment) {
|
||||||
return errorHandling(next, { message: "Le commentaire n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "Le commentaire n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
comment.message = message
|
comment.message = message
|
||||||
await comment.save()
|
await comment.save()
|
||||||
return res.status(200).json({ message: 'Le commentaire a bien été modifié.' })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ message: 'Le commentaire a bien été modifié.' })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
|
@ -27,9 +27,14 @@ exports.postFavoriteByFunctionId = async (req, res, next) => {
|
|||||||
const { functionId } = req.params
|
const { functionId } = req.params
|
||||||
const { userId } = req
|
const { userId } = req
|
||||||
try {
|
try {
|
||||||
const resultFunction = await Functions.findOne({ where: { id: functionId } })
|
const resultFunction = await Functions.findOne({
|
||||||
|
where: { id: functionId }
|
||||||
|
})
|
||||||
if (!resultFunction) {
|
if (!resultFunction) {
|
||||||
return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La fonction n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const favorite = await Favorites.findOne({
|
const favorite = await Favorites.findOne({
|
||||||
where: {
|
where: {
|
||||||
@ -41,7 +46,10 @@ exports.postFavoriteByFunctionId = async (req, res, next) => {
|
|||||||
await Favorites.create({ userId, functionId })
|
await Favorites.create({ userId, functionId })
|
||||||
return res.status(201).json({ result: 'Le favoris a bien été ajouté!' })
|
return res.status(201).json({ result: 'Le favoris a bien été ajouté!' })
|
||||||
}
|
}
|
||||||
return errorHandling(next, { message: 'La fonction est déjà en favoris.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'La fonction est déjà en favoris.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -52,9 +60,14 @@ exports.deleteFavoriteByFunctionId = async (req, res, next) => {
|
|||||||
const { functionId } = req.params
|
const { functionId } = req.params
|
||||||
const { userId } = req
|
const { userId } = req
|
||||||
try {
|
try {
|
||||||
const resultFunction = await Functions.findOne({ where: { id: functionId } })
|
const resultFunction = await Functions.findOne({
|
||||||
|
where: { id: functionId }
|
||||||
|
})
|
||||||
if (!resultFunction) {
|
if (!resultFunction) {
|
||||||
return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La fonction n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const favorite = await Favorites.findOne({
|
const favorite = await Favorites.findOne({
|
||||||
where: {
|
where: {
|
||||||
@ -63,10 +76,15 @@ exports.deleteFavoriteByFunctionId = async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!favorite) {
|
if (!favorite) {
|
||||||
return errorHandling(next, { message: "Le fonction n'est pas en favoris.", statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: "Le fonction n'est pas en favoris.",
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
await favorite.destroy()
|
await favorite.destroy()
|
||||||
return res.status(200).json({ message: 'Le fonction a bien été supprimé des favoris.' })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ message: 'Le fonction a bien été supprimé des favoris.' })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
|
@ -10,24 +10,42 @@ const Sequelize = require('sequelize')
|
|||||||
exports.getFunctions = async (req, res, next) => {
|
exports.getFunctions = async (req, res, next) => {
|
||||||
const categoryId = helperQueryNumber(req.query.categoryId, 0)
|
const categoryId = helperQueryNumber(req.query.categoryId, 0)
|
||||||
let { search } = req.query
|
let { search } = req.query
|
||||||
try { search = search.toLowerCase() } catch {};
|
try {
|
||||||
|
search = search.toLowerCase()
|
||||||
|
} catch {}
|
||||||
const options = {
|
const options = {
|
||||||
where: {
|
where: {
|
||||||
isOnline: 1,
|
isOnline: 1,
|
||||||
// Trie par catégorie
|
// Trie par catégorie
|
||||||
...(categoryId !== 0) && { categorieId: categoryId },
|
...(categoryId !== 0 && { categorieId: categoryId }),
|
||||||
// Recherche
|
// Recherche
|
||||||
...(search != null) && {
|
...(search != null && {
|
||||||
[Sequelize.Op.or]: [
|
[Sequelize.Op.or]: [
|
||||||
{ title: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('title')), 'LIKE', `%${search}%`) },
|
{
|
||||||
{ slug: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('slug')), 'LIKE', `%${search}%`) },
|
title: Sequelize.where(
|
||||||
{ description: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('description')), 'LIKE', `%${search}%`) }
|
Sequelize.fn('LOWER', Sequelize.col('title')),
|
||||||
]
|
'LIKE',
|
||||||
}
|
`%${search}%`
|
||||||
|
)
|
||||||
},
|
},
|
||||||
include: [
|
{
|
||||||
{ model: Categories, attributes: ['name', 'color'] }
|
slug: Sequelize.where(
|
||||||
],
|
Sequelize.fn('LOWER', Sequelize.col('slug')),
|
||||||
|
'LIKE',
|
||||||
|
`%${search}%`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: Sequelize.where(
|
||||||
|
Sequelize.fn('LOWER', Sequelize.col('description')),
|
||||||
|
'LIKE',
|
||||||
|
`%${search}%`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
include: [{ model: Categories, attributes: ['name', 'color'] }],
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline']
|
exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline']
|
||||||
},
|
},
|
||||||
@ -43,18 +61,21 @@ exports.getFunctionBySlug = (req, res, next) => {
|
|||||||
attributes: {
|
attributes: {
|
||||||
exclude: ['updatedAt', 'isOnline']
|
exclude: ['updatedAt', 'isOnline']
|
||||||
},
|
},
|
||||||
include: [
|
include: [{ model: Categories, attributes: ['name', 'color'] }]
|
||||||
{ model: Categories, attributes: ['name', 'color'] }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then(result => {
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La fonction n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
try { result.utilizationForm = JSON.parse(result.utilizationForm) } catch {}
|
try {
|
||||||
|
result.utilizationForm = JSON.parse(result.utilizationForm)
|
||||||
|
} catch {}
|
||||||
return res.status(200).json(result)
|
return res.status(200).json(result)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
})
|
})
|
||||||
@ -65,5 +86,8 @@ exports.executeFunctionBySlug = (req, res, next) => {
|
|||||||
if (functionOutput !== undefined) {
|
if (functionOutput !== undefined) {
|
||||||
return functionOutput({ res, next }, req.body)
|
return functionOutput({ res, next }, req.body)
|
||||||
}
|
}
|
||||||
return errorHandling(next, { message: "La fonction n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "La fonction n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,7 @@ exports.getQuotes = async (req, res, next) => {
|
|||||||
where: {
|
where: {
|
||||||
isValidated: 1
|
isValidated: 1
|
||||||
},
|
},
|
||||||
include: [
|
include: [{ model: Users, attributes: ['name', 'logo'] }],
|
||||||
{ model: Users, attributes: ['name', 'logo'] }
|
|
||||||
],
|
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: ['isValidated']
|
exclude: ['isValidated']
|
||||||
},
|
},
|
||||||
@ -27,10 +25,15 @@ exports.postQuote = (req, res, next) => {
|
|||||||
return errorHandling(next, requiredFields)
|
return errorHandling(next, requiredFields)
|
||||||
}
|
}
|
||||||
Quotes.create({ quote, author, userId: req.userId })
|
Quotes.create({ quote, author, userId: req.userId })
|
||||||
.then((_result) => {
|
.then(_result => {
|
||||||
return res.status(200).json({ message: "La citation a bien été ajoutée, elle est en attente de confirmation d'un administrateur." })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
message:
|
||||||
|
"La citation a bien été ajoutée, elle est en attente de confirmation d'un administrateur."
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
})
|
||||||
|
.catch(error => {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
})
|
})
|
||||||
|
@ -36,12 +36,20 @@ exports.putTask = async (req, res, next) => {
|
|||||||
const { isCompleted } = req.body
|
const { isCompleted } = req.body
|
||||||
try {
|
try {
|
||||||
if (typeof isCompleted !== 'boolean') {
|
if (typeof isCompleted !== 'boolean') {
|
||||||
return errorHandling(next, { message: 'isCompleted doit être un booléen.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: 'isCompleted doit être un booléen.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskResult = await Tasks.findOne({ where: { id, userId: req.userId } })
|
const taskResult = await Tasks.findOne({
|
||||||
|
where: { id, userId: req.userId }
|
||||||
|
})
|
||||||
if (!taskResult) {
|
if (!taskResult) {
|
||||||
return errorHandling(next, { message: 'La "tâche à faire" n\'existe pas.', statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: 'La "tâche à faire" n\'existe pas.',
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
taskResult.isCompleted = isCompleted
|
taskResult.isCompleted = isCompleted
|
||||||
const taskSaved = await taskResult.save()
|
const taskSaved = await taskResult.save()
|
||||||
@ -55,12 +63,19 @@ exports.putTask = async (req, res, next) => {
|
|||||||
exports.deleteTask = async (req, res, next) => {
|
exports.deleteTask = async (req, res, next) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
try {
|
try {
|
||||||
const taskResult = await Tasks.findOne({ where: { id, userId: req.userId } })
|
const taskResult = await Tasks.findOne({
|
||||||
|
where: { id, userId: req.userId }
|
||||||
|
})
|
||||||
if (!taskResult) {
|
if (!taskResult) {
|
||||||
return errorHandling(next, { message: 'La "tâche à faire" n\'existe pas.', statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: 'La "tâche à faire" n\'existe pas.',
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
await taskResult.destroy()
|
await taskResult.destroy()
|
||||||
return res.status(200).json({ message: 'La "tâche à faire" a bien été supprimée!' })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ message: 'La "tâche à faire" a bien été supprimée!' })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
|
@ -7,7 +7,13 @@ const uuid = require('uuid')
|
|||||||
const Sequelize = require('sequelize')
|
const Sequelize = require('sequelize')
|
||||||
const errorHandling = require('../assets/utils/errorHandling')
|
const errorHandling = require('../assets/utils/errorHandling')
|
||||||
const { serverError, generalError } = require('../assets/config/errors')
|
const { serverError, generalError } = require('../assets/config/errors')
|
||||||
const { JWT_SECRET, FRONT_END_HOST, EMAIL_INFO, HOST, TOKEN_LIFE } = require('../assets/config/config')
|
const {
|
||||||
|
JWT_SECRET,
|
||||||
|
FRONT_END_HOST,
|
||||||
|
EMAIL_INFO,
|
||||||
|
HOST,
|
||||||
|
TOKEN_LIFE
|
||||||
|
} = require('../assets/config/config')
|
||||||
const transporter = require('../assets/config/transporter')
|
const transporter = require('../assets/config/transporter')
|
||||||
const { emailUserTemplate } = require('../assets/config/emails')
|
const { emailUserTemplate } = require('../assets/config/emails')
|
||||||
const Users = require('../models/users')
|
const Users = require('../models/users')
|
||||||
@ -19,7 +25,12 @@ const Quotes = require('../models/quotes')
|
|||||||
const deleteFilesNameStartWith = require('../assets/utils/deleteFilesNameStartWith')
|
const deleteFilesNameStartWith = require('../assets/utils/deleteFilesNameStartWith')
|
||||||
const getPagesHelper = require('../assets/utils/getPagesHelper')
|
const getPagesHelper = require('../assets/utils/getPagesHelper')
|
||||||
|
|
||||||
async function handleEditUser (res, { name, email, biography, isPublicEmail }, userId, logoName) {
|
async function handleEditUser (
|
||||||
|
res,
|
||||||
|
{ name, email, biography, isPublicEmail },
|
||||||
|
userId,
|
||||||
|
logoName
|
||||||
|
) {
|
||||||
const user = await Users.findOne({ where: { id: userId } })
|
const user = await Users.findOne({ where: { id: userId } })
|
||||||
user.name = name
|
user.name = name
|
||||||
if (user.email !== email) {
|
if (user.email !== email) {
|
||||||
@ -31,7 +42,12 @@ async function handleEditUser (res, { name, email, biography, isPublicEmail }, u
|
|||||||
from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`,
|
from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`,
|
||||||
to: email,
|
to: email,
|
||||||
subject: "FunctionProject - Confirmer l'email",
|
subject: "FunctionProject - Confirmer l'email",
|
||||||
html: emailUserTemplate("Veuillez confirmer l'email", 'Oui, je confirme.', `${HOST}/users/confirm-email/${tempToken}`, 'Si vous avez reçu ce message par erreur, il suffit de le supprimer. Votre email ne serez pas confirmé si vous ne cliquez pas sur le lien de confirmation ci-dessus.')
|
html: emailUserTemplate(
|
||||||
|
"Veuillez confirmer l'email",
|
||||||
|
'Oui, je confirme.',
|
||||||
|
`${HOST}/users/confirm-email/${tempToken}`,
|
||||||
|
'Si vous avez reçu ce message par erreur, il suffit de le supprimer. Votre email ne serez pas confirmé si vous ne cliquez pas sur le lien de confirmation ci-dessus.'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (biography != null) {
|
if (biography != null) {
|
||||||
@ -42,22 +58,48 @@ async function handleEditUser (res, { name, email, biography, isPublicEmail }, u
|
|||||||
user.logo = `/images/users/${logoName}`
|
user.logo = `/images/users/${logoName}`
|
||||||
}
|
}
|
||||||
await user.save()
|
await user.save()
|
||||||
return res.status(200).json({ id: user.id, name: user.name, email: user.email, biography: user.biography, logo: user.logo, isPublicEmail: user.isPublicEmail, isAdmin: user.isAdmin, createdAt: user.createdAt })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
biography: user.biography,
|
||||||
|
logo: user.logo,
|
||||||
|
isPublicEmail: user.isPublicEmail,
|
||||||
|
isAdmin: user.isAdmin,
|
||||||
|
createdAt: user.createdAt
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getUsers = async (req, res, next) => {
|
exports.getUsers = async (req, res, next) => {
|
||||||
let { search } = req.query
|
let { search } = req.query
|
||||||
try { search = search.toLowerCase() } catch {};
|
try {
|
||||||
|
search = search.toLowerCase()
|
||||||
|
} catch {}
|
||||||
const options = {
|
const options = {
|
||||||
where: {
|
where: {
|
||||||
isConfirmed: true,
|
isConfirmed: true,
|
||||||
// Recherche
|
// Recherche
|
||||||
...(search != null) && {
|
...(search != null && {
|
||||||
name: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), 'LIKE', `%${search}%`)
|
name: Sequelize.where(
|
||||||
}
|
Sequelize.fn('LOWER', Sequelize.col('name')),
|
||||||
|
'LIKE',
|
||||||
|
`%${search}%`
|
||||||
|
)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: ['updatedAt', 'isAdmin', 'isConfirmed', 'password', 'tempToken', 'tempExpirationToken', 'isPublicEmail', 'email']
|
exclude: [
|
||||||
|
'updatedAt',
|
||||||
|
'isAdmin',
|
||||||
|
'isConfirmed',
|
||||||
|
'password',
|
||||||
|
'tempToken',
|
||||||
|
'tempExpirationToken',
|
||||||
|
'isPublicEmail',
|
||||||
|
'email'
|
||||||
|
]
|
||||||
},
|
},
|
||||||
order: [['createdAt', 'DESC']]
|
order: [['createdAt', 'DESC']]
|
||||||
}
|
}
|
||||||
@ -69,35 +111,60 @@ exports.putUser = async (req, res, next) => {
|
|||||||
const logo = req.files.logo
|
const logo = req.files.logo
|
||||||
const errors = validationResult(req)
|
const errors = validationResult(req)
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: errors.array()[0].msg,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (logo != null) {
|
if (logo != null) {
|
||||||
if ((!logo || logo.truncated) && (
|
if (
|
||||||
logo.mimetype !== 'image/png' ||
|
(!logo || logo.truncated) &&
|
||||||
|
(logo.mimetype !== 'image/png' ||
|
||||||
logo.mimetype !== 'image/jpg' ||
|
logo.mimetype !== 'image/jpg' ||
|
||||||
logo.mimetype !== 'image/jpeg' ||
|
logo.mimetype !== 'image/jpeg' ||
|
||||||
logo.mimetype !== 'image/gif'
|
logo.mimetype !== 'image/gif')
|
||||||
)) {
|
) {
|
||||||
return errorHandling(next, { message: 'Le profil doit avoir une image valide (PNG, JPG, GIF) et moins de 5mo.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message:
|
||||||
|
'Le profil doit avoir une image valide (PNG, JPG, GIF) et moins de 5mo.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const splitedLogoName = logo.name.split('.')
|
const splitedLogoName = logo.name.split('.')
|
||||||
if (splitedLogoName.length !== 2) return errorHandling(next, serverError)
|
if (splitedLogoName.length !== 2) return errorHandling(next, serverError)
|
||||||
const logoName = name + req.userId + '.' + splitedLogoName[1]
|
const logoName = name + req.userId + '.' + splitedLogoName[1]
|
||||||
// Supprime les anciens logo
|
// Supprime les anciens logo
|
||||||
try {
|
try {
|
||||||
deleteFilesNameStartWith(`${name + req.userId}`, path.join(__dirname, '..', 'assets', 'images', 'users'), async () => {
|
deleteFilesNameStartWith(
|
||||||
logo.mv(path.join(__dirname, '..', 'assets', 'images', 'users', logoName), async (error) => {
|
`${name + req.userId}`,
|
||||||
|
path.join(__dirname, '..', 'assets', 'images', 'users'),
|
||||||
|
async () => {
|
||||||
|
logo.mv(
|
||||||
|
path.join(__dirname, '..', 'assets', 'images', 'users', logoName),
|
||||||
|
async error => {
|
||||||
if (error) return errorHandling(next, serverError)
|
if (error) return errorHandling(next, serverError)
|
||||||
return await handleEditUser(res, { name, email, biography, isPublicEmail }, req.userId, logoName)
|
return await handleEditUser(
|
||||||
})
|
res,
|
||||||
})
|
{ name, email, biography, isPublicEmail },
|
||||||
|
req.userId,
|
||||||
|
logoName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return await handleEditUser(res, { name, email, biography, isPublicEmail }, req.userId, null)
|
return await handleEditUser(
|
||||||
|
res,
|
||||||
|
{ name, email, biography, isPublicEmail },
|
||||||
|
req.userId,
|
||||||
|
null
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -109,7 +176,10 @@ exports.register = async (req, res, next) => {
|
|||||||
const { name, email, password } = req.body
|
const { name, email, password } = req.body
|
||||||
const errors = validationResult(req)
|
const errors = validationResult(req)
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: errors.array()[0].msg,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const hashedPassword = await bcrypt.hash(password, 12)
|
const hashedPassword = await bcrypt.hash(password, 12)
|
||||||
@ -119,9 +189,19 @@ exports.register = async (req, res, next) => {
|
|||||||
from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`,
|
from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`,
|
||||||
to: email,
|
to: email,
|
||||||
subject: "FunctionProject - Confirmer l'inscription",
|
subject: "FunctionProject - Confirmer l'inscription",
|
||||||
html: emailUserTemplate("Veuillez confirmer l'inscription", "Oui, je m'inscris.", `${HOST}/users/confirm-email/${tempToken}`, 'Si vous avez reçu ce message par erreur, il suffit de le supprimer. Vous ne serez pas inscrit si vous ne cliquez pas sur le lien de confirmation ci-dessus.')
|
html: emailUserTemplate(
|
||||||
|
"Veuillez confirmer l'inscription",
|
||||||
|
"Oui, je m'inscris.",
|
||||||
|
`${HOST}/users/confirm-email/${tempToken}`,
|
||||||
|
'Si vous avez reçu ce message par erreur, il suffit de le supprimer. Vous ne serez pas inscrit si vous ne cliquez pas sur le lien de confirmation ci-dessus.'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
.status(201)
|
||||||
|
.json({
|
||||||
|
result:
|
||||||
|
"Vous y êtes presque, veuillez vérifier vos emails pour confirmer l'inscription."
|
||||||
})
|
})
|
||||||
return res.status(201).json({ result: "Vous y êtes presque, veuillez vérifier vos emails pour confirmer l'inscription." })
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -132,24 +212,55 @@ exports.login = async (req, res, next) => {
|
|||||||
const { email, password } = req.body
|
const { email, password } = req.body
|
||||||
const errors = validationResult(req)
|
const errors = validationResult(req)
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: errors.array()[0].msg,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const user = await Users.findOne({ where: { email } })
|
const user = await Users.findOne({ where: { email } })
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return errorHandling(next, { message: "Le mot de passe ou l'adresse email n'est pas valide.", statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: "Le mot de passe ou l'adresse email n'est pas valide.",
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const isEqual = await bcrypt.compare(password, user.password)
|
const isEqual = await bcrypt.compare(password, user.password)
|
||||||
if (!isEqual) {
|
if (!isEqual) {
|
||||||
return errorHandling(next, { message: "Le mot de passe ou l'adresse email n'est pas valide.", statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: "Le mot de passe ou l'adresse email n'est pas valide.",
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (!user.isConfirmed) {
|
if (!user.isConfirmed) {
|
||||||
return errorHandling(next, { message: 'Vous devez valider votre adresse email pour votre première connexion.', statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message:
|
||||||
|
'Vous devez valider votre adresse email pour votre première connexion.',
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const token = jwt.sign({
|
const token = jwt.sign(
|
||||||
email: user.email, userId: user.id
|
{
|
||||||
}, JWT_SECRET, { expiresIn: TOKEN_LIFE })
|
email: user.email,
|
||||||
return res.status(200).json({ token, id: user.id, name: user.name, email: user.email, biography: user.biography, logo: user.logo, isPublicEmail: user.isPublicEmail, isAdmin: user.isAdmin, createdAt: user.createdAt, expiresIn: Math.round(ms(TOKEN_LIFE) / 1000) })
|
userId: user.id
|
||||||
|
},
|
||||||
|
JWT_SECRET,
|
||||||
|
{ expiresIn: TOKEN_LIFE }
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
token,
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
biography: user.biography,
|
||||||
|
logo: user.logo,
|
||||||
|
isPublicEmail: user.isPublicEmail,
|
||||||
|
isAdmin: user.isAdmin,
|
||||||
|
createdAt: user.createdAt,
|
||||||
|
expiresIn: Math.round(ms(TOKEN_LIFE) / 1000)
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -162,9 +273,14 @@ exports.confirmEmail = async (req, res, next) => {
|
|||||||
return errorHandling(next, generalError)
|
return errorHandling(next, generalError)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const user = await Users.findOne({ where: { tempToken, isConfirmed: false } })
|
const user = await Users.findOne({
|
||||||
|
where: { tempToken, isConfirmed: false }
|
||||||
|
})
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return errorHandling(next, { message: "Le token n'est pas valide.", statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: "Le token n'est pas valide.",
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
user.tempToken = null
|
user.tempToken = null
|
||||||
user.isConfirmed = true
|
user.isConfirmed = true
|
||||||
@ -180,12 +296,19 @@ exports.resetPassword = async (req, res, next) => {
|
|||||||
const { email } = req.body
|
const { email } = req.body
|
||||||
const errors = validationResult(req)
|
const errors = validationResult(req)
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: errors.array()[0].msg,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const user = await Users.findOne({ where: { email, tempToken: null } })
|
const user = await Users.findOne({ where: { email, tempToken: null } })
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return errorHandling(next, { message: "L'adresse email n'existe pas ou une demande est déjà en cours.", statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message:
|
||||||
|
"L'adresse email n'existe pas ou une demande est déjà en cours.",
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const tempToken = uuid.v4()
|
const tempToken = uuid.v4()
|
||||||
user.tempExpirationToken = Date.now() + 3600000 // 1 heure
|
user.tempExpirationToken = Date.now() + 3600000 // 1 heure
|
||||||
@ -195,9 +318,19 @@ exports.resetPassword = async (req, res, next) => {
|
|||||||
from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`,
|
from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`,
|
||||||
to: email,
|
to: email,
|
||||||
subject: 'FunctionProject - Réinitialisation du mot de passe',
|
subject: 'FunctionProject - Réinitialisation du mot de passe',
|
||||||
html: emailUserTemplate('Veuillez confirmer la réinitialisation du mot de passe', 'Oui, je change mon mot de passe.', `${FRONT_END_HOST}/users/newPassword?token=${tempToken}`, 'Si vous avez reçu ce message par erreur, il suffit de le supprimer. Votre mot de passe ne sera pas réinitialiser si vous ne cliquez pas sur le lien ci-dessus. Par ailleurs, pour la sécurité de votre compte, la réinitialisation du mot de passe est disponible pendant un délai de 1 heure, passez ce temps, la réinitialisation ne sera plus valide.')
|
html: emailUserTemplate(
|
||||||
|
'Veuillez confirmer la réinitialisation du mot de passe',
|
||||||
|
'Oui, je change mon mot de passe.',
|
||||||
|
`${FRONT_END_HOST}/users/newPassword?token=${tempToken}`,
|
||||||
|
'Si vous avez reçu ce message par erreur, il suffit de le supprimer. Votre mot de passe ne sera pas réinitialiser si vous ne cliquez pas sur le lien ci-dessus. Par ailleurs, pour la sécurité de votre compte, la réinitialisation du mot de passe est disponible pendant un délai de 1 heure, passez ce temps, la réinitialisation ne sera plus valide.'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
result:
|
||||||
|
'Demande de réinitialisation du mot de passe réussi, veuillez vérifier vos emails!'
|
||||||
})
|
})
|
||||||
return res.status(200).json({ result: 'Demande de réinitialisation du mot de passe réussi, veuillez vérifier vos emails!' })
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -208,19 +341,27 @@ exports.newPassword = async (req, res, next) => {
|
|||||||
const { tempToken, password } = req.body
|
const { tempToken, password } = req.body
|
||||||
const errors = validationResult(req)
|
const errors = validationResult(req)
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: errors.array()[0].msg,
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const user = await Users.findOne({ where: { tempToken } })
|
const user = await Users.findOne({ where: { tempToken } })
|
||||||
if (!user && parseInt(user.tempExpirationToken) < Date.now()) {
|
if (!user && parseInt(user.tempExpirationToken) < Date.now()) {
|
||||||
return errorHandling(next, { message: "Le token n'est pas valide.", statusCode: 400 })
|
return errorHandling(next, {
|
||||||
|
message: "Le token n'est pas valide.",
|
||||||
|
statusCode: 400
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const hashedPassword = await bcrypt.hash(password, 12)
|
const hashedPassword = await bcrypt.hash(password, 12)
|
||||||
user.password = hashedPassword
|
user.password = hashedPassword
|
||||||
user.tempToken = null
|
user.tempToken = null
|
||||||
user.tempExpirationToken = null
|
user.tempExpirationToken = null
|
||||||
await user.save()
|
await user.save()
|
||||||
return res.status(200).json({ result: 'Le mot de passe a bien été modifié!' })
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ result: 'Le mot de passe a bien été modifié!' })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
@ -233,30 +374,51 @@ exports.getUserInfo = async (req, res, next) => {
|
|||||||
const user = await Users.findOne({
|
const user = await Users.findOne({
|
||||||
where: { name, isConfirmed: true },
|
where: { name, isConfirmed: true },
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: ['updatedAt', 'isAdmin', 'isConfirmed', 'password', 'tempToken', 'tempExpirationToken']
|
exclude: [
|
||||||
|
'updatedAt',
|
||||||
|
'isAdmin',
|
||||||
|
'isConfirmed',
|
||||||
|
'password',
|
||||||
|
'tempToken',
|
||||||
|
'tempExpirationToken'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return errorHandling(next, { message: "L'utilisateur n'existe pas.", statusCode: 404 })
|
return errorHandling(next, {
|
||||||
|
message: "L'utilisateur n'existe pas.",
|
||||||
|
statusCode: 404
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const favorites = await Favorites.findAll({
|
const favorites = await Favorites.findAll({
|
||||||
where: { userId: user.id },
|
where: { userId: user.id },
|
||||||
include: [
|
include: [
|
||||||
{ model: Functions, attributes: { exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline'] }, include: { model: Categories, attributes: ['name', 'color'] } }
|
{
|
||||||
|
model: Functions,
|
||||||
|
attributes: {
|
||||||
|
exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline']
|
||||||
|
},
|
||||||
|
include: { model: Categories, attributes: ['name', 'color'] }
|
||||||
|
}
|
||||||
],
|
],
|
||||||
order: [['createdAt', 'DESC']],
|
order: [['createdAt', 'DESC']],
|
||||||
limit: 5
|
limit: 5
|
||||||
})
|
})
|
||||||
const favoritesArray = favorites.map((favorite) => favorite.function)
|
const favoritesArray = favorites.map(favorite => favorite.function)
|
||||||
const comments = await Comments.findAll({
|
const comments = await Comments.findAll({
|
||||||
where: { userId: user.id },
|
where: { userId: user.id },
|
||||||
include: [
|
include: [
|
||||||
{ model: Functions, attributes: { exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline'] } }
|
{
|
||||||
|
model: Functions,
|
||||||
|
attributes: {
|
||||||
|
exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline']
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
order: [['createdAt', 'DESC']],
|
order: [['createdAt', 'DESC']],
|
||||||
limit: 5
|
limit: 5
|
||||||
})
|
})
|
||||||
const commentsArray = comments.map((commentObject) => {
|
const commentsArray = comments.map(commentObject => {
|
||||||
return {
|
return {
|
||||||
id: commentObject.id,
|
id: commentObject.id,
|
||||||
message: commentObject.message,
|
message: commentObject.message,
|
||||||
@ -274,7 +436,7 @@ exports.getUserInfo = async (req, res, next) => {
|
|||||||
})
|
})
|
||||||
const userObject = {
|
const userObject = {
|
||||||
// Si Public Email
|
// Si Public Email
|
||||||
...(user.isPublicEmail) && { email: user.email },
|
...(user.isPublicEmail && { email: user.email }),
|
||||||
isPublicEmail: user.isPublicEmail,
|
isPublicEmail: user.isPublicEmail,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
biography: user.biography,
|
biography: user.biography,
|
||||||
|
@ -4,19 +4,28 @@ const Users = require('../models/users')
|
|||||||
|
|
||||||
module.exports = (req, _res, next) => {
|
module.exports = (req, _res, next) => {
|
||||||
if (!req.userId) {
|
if (!req.userId) {
|
||||||
return errorHandling(next, { message: "Vous n'êtes pas connecté.", statusCode: 403 })
|
return errorHandling(next, {
|
||||||
|
message: "Vous n'êtes pas connecté.",
|
||||||
|
statusCode: 403
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Users.findOne({ where: { id: req.userId } })
|
Users.findOne({ where: { id: req.userId } })
|
||||||
.then((user) => {
|
.then(user => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return errorHandling(next, { message: "Le mot de passe ou l'adresse email n'est pas valide.", statusCode: 403 })
|
return errorHandling(next, {
|
||||||
|
message: "Le mot de passe ou l'adresse email n'est pas valide.",
|
||||||
|
statusCode: 403
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (!user.isAdmin) {
|
if (!user.isAdmin) {
|
||||||
return errorHandling(next, { message: "Vous n'êtes pas administrateur.", statusCode: 403 })
|
return errorHandling(next, {
|
||||||
|
message: "Vous n'êtes pas administrateur.",
|
||||||
|
statusCode: 403
|
||||||
|
})
|
||||||
}
|
}
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return errorHandling(next, serverError)
|
return errorHandling(next, serverError)
|
||||||
})
|
})
|
||||||
|
@ -5,18 +5,27 @@ const { JWT_SECRET } = require('../assets/config/config')
|
|||||||
module.exports = (req, _res, next) => {
|
module.exports = (req, _res, next) => {
|
||||||
const token = req.get('Authorization')
|
const token = req.get('Authorization')
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return errorHandling(next, { message: 'Vous devez être connecter pour effectuer cette opération.', statusCode: 403 })
|
return errorHandling(next, {
|
||||||
|
message: 'Vous devez être connecter pour effectuer cette opération.',
|
||||||
|
statusCode: 403
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let decodedToken
|
let decodedToken
|
||||||
try {
|
try {
|
||||||
decodedToken = jwt.verify(token, JWT_SECRET)
|
decodedToken = jwt.verify(token, JWT_SECRET)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorHandling(next, { message: 'Vous devez être connecter pour effectuer cette opération.', statusCode: 403 })
|
return errorHandling(next, {
|
||||||
|
message: 'Vous devez être connecter pour effectuer cette opération.',
|
||||||
|
statusCode: 403
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!decodedToken) {
|
if (!decodedToken) {
|
||||||
return errorHandling(next, { message: 'Vous devez être connecter pour effectuer cette opération.', statusCode: 403 })
|
return errorHandling(next, {
|
||||||
|
message: 'Vous devez être connecter pour effectuer cette opération.',
|
||||||
|
statusCode: 403
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
req.userId = decodedToken.userId
|
req.userId = decodedToken.userId
|
||||||
|
453
api/package-lock.json
generated
453
api/package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "api",
|
"name": "api",
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -89,9 +89,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "13.9.2",
|
"version": "14.0.27",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz",
|
||||||
"integrity": "sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg=="
|
"integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g=="
|
||||||
},
|
},
|
||||||
"abab": {
|
"abab": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
@ -114,9 +114,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "7.2.0",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
|
||||||
"integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ=="
|
"integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA=="
|
||||||
},
|
},
|
||||||
"acorn-globals": {
|
"acorn-globals": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
@ -134,14 +134,14 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"acorn-walk": {
|
"acorn-walk": {
|
||||||
"version": "7.1.1",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
|
||||||
"integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ=="
|
"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA=="
|
||||||
},
|
},
|
||||||
"ajv": {
|
"ajv": {
|
||||||
"version": "6.12.2",
|
"version": "6.12.3",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
|
||||||
"integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
|
"integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
@ -279,9 +279,9 @@
|
|||||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||||
},
|
},
|
||||||
"aws4": {
|
"aws4": {
|
||||||
"version": "1.9.1",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
|
||||||
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
|
"integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
|
||||||
},
|
},
|
||||||
"axios": {
|
"axios": {
|
||||||
"version": "0.19.2",
|
"version": "0.19.2",
|
||||||
@ -301,7 +301,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "5.1.2"
|
"safe-buffer": "5.1.2"
|
||||||
}
|
}
|
||||||
@ -345,6 +344,21 @@
|
|||||||
"qs": "6.7.0",
|
"qs": "6.7.0",
|
||||||
"raw-body": "2.4.0",
|
"raw-body": "2.4.0",
|
||||||
"type-is": "~1.6.17"
|
"type-is": "~1.6.17"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bowser": {
|
"bowser": {
|
||||||
@ -739,9 +753,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.6.9",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
},
|
},
|
||||||
@ -849,11 +863,6 @@
|
|||||||
"streamsearch": "0.1.2"
|
"streamsearch": "0.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dns-prefetch-control": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q=="
|
|
||||||
},
|
|
||||||
"doctrine": {
|
"doctrine": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||||
@ -895,8 +904,7 @@
|
|||||||
"dotenv": {
|
"dotenv": {
|
||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
||||||
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
|
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"dottie": {
|
"dottie": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
@ -1008,9 +1016,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"escodegen": {
|
"escodegen": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.3",
|
||||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
|
||||||
"integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==",
|
"integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"esprima": "^4.0.1",
|
"esprima": "^4.0.1",
|
||||||
"estraverse": "^4.2.0",
|
"estraverse": "^4.2.0",
|
||||||
@ -1142,6 +1150,23 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^2.6.9",
|
"debug": "^2.6.9",
|
||||||
"resolve": "^1.13.1"
|
"resolve": "^1.13.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-module-utils": {
|
"eslint-module-utils": {
|
||||||
@ -1152,6 +1177,23 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^2.6.9",
|
"debug": "^2.6.9",
|
||||||
"pkg-dir": "^2.0.0"
|
"pkg-dir": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-plugin-es": {
|
"eslint-plugin-es": {
|
||||||
@ -1191,6 +1233,15 @@
|
|||||||
"resolve": "^1.11.0"
|
"resolve": "^1.11.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"doctrine": {
|
"doctrine": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
|
||||||
@ -1200,6 +1251,12 @@
|
|||||||
"esutils": "^2.0.2",
|
"esutils": "^2.0.2",
|
||||||
"isarray": "^1.0.0"
|
"isarray": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1353,11 +1410,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||||
},
|
},
|
||||||
"expect-ct": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-6SK3MG/Bbhm8MsgyJAylg+ucIOU71/FzyFalcfu5nY19dH8y/z0tBJU0wrNBXD4B27EoQtqPF/9wqH0iYAd04g=="
|
|
||||||
},
|
|
||||||
"express": {
|
"express": {
|
||||||
"version": "4.17.1",
|
"version": "4.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||||
@ -1393,12 +1445,27 @@
|
|||||||
"type-is": "~1.6.18",
|
"type-is": "~1.6.18",
|
||||||
"utils-merge": "1.0.1",
|
"utils-merge": "1.0.1",
|
||||||
"vary": "~1.1.2"
|
"vary": "~1.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"express-fileupload": {
|
"express-fileupload": {
|
||||||
"version": "1.1.6",
|
"version": "1.1.9",
|
||||||
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.9.tgz",
|
||||||
"integrity": "sha512-w24zPWT8DkoIxSVkbxYPo9hkTiLpCQQzNsLRTCnecBhfbYv+IkIC5uLw2MIUAxBZ+7UMmXPjGxlhzUXo4RcbZw==",
|
"integrity": "sha512-f2w0aoe7lj3NeD8a4MXmYQsqir3Z66I08l9AKq04QbFUAjeZNmPwTlR5Lx2NGwSu/PslsAjGC38MWzo5tTjoBg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"busboy": "^0.3.1"
|
"busboy": "^0.3.1"
|
||||||
}
|
}
|
||||||
@ -1412,19 +1479,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"express-validator": {
|
"express-validator": {
|
||||||
"version": "6.4.0",
|
"version": "6.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.6.0.tgz",
|
||||||
"integrity": "sha512-Fs+x0yDOSiUV+o5jIRloMyBxqpSzJiMM8KQW1IRVv2l49F6ATU0F9uPa+3K6vXNlLlhUjauv2FCGLFPMaNr24w==",
|
"integrity": "sha512-xcephfzFbUssJph/nOSKIdx+I+8GRz5by/8rOIKL6gJikKKKJjnwYH5TG1nIDB6kEalUtZMbOFuSNOp/HHY84Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"validator": "^12.1.0"
|
"validator": "^13.1.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"validator": {
|
|
||||||
"version": "12.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz",
|
|
||||||
"integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ=="
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extend": {
|
"extend": {
|
||||||
@ -1449,9 +1509,9 @@
|
|||||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
|
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
|
||||||
},
|
},
|
||||||
"fast-deep-equal": {
|
"fast-deep-equal": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
},
|
},
|
||||||
"fast-json-stable-stringify": {
|
"fast-json-stable-stringify": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
@ -1507,6 +1567,21 @@
|
|||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"statuses": "~1.5.0",
|
"statuses": "~1.5.0",
|
||||||
"unpipe": "~1.0.0"
|
"unpipe": "~1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"find-root": {
|
"find-root": {
|
||||||
@ -1547,23 +1622,6 @@
|
|||||||
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
|
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "=3.1.0"
|
"debug": "=3.1.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"debug": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
|
||||||
"requires": {
|
|
||||||
"ms": "2.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ms": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forever-agent": {
|
"forever-agent": {
|
||||||
@ -1586,11 +1644,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||||
},
|
},
|
||||||
"frameguard": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g=="
|
|
||||||
},
|
|
||||||
"fresh": {
|
"fresh": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||||
@ -1724,11 +1777,11 @@
|
|||||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||||
},
|
},
|
||||||
"har-validator": {
|
"har-validator": {
|
||||||
"version": "5.1.3",
|
"version": "5.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||||
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
|
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "^6.5.5",
|
"ajv": "^6.12.3",
|
||||||
"har-schema": "^2.0.0"
|
"har-schema": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1760,22 +1813,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"helmet": {
|
"helmet": {
|
||||||
"version": "3.21.3",
|
"version": "3.23.3",
|
||||||
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.23.3.tgz",
|
||||||
"integrity": "sha512-8OjGNdpG3WQhPO71fSy2fT4X3FSNutU1LDeAf+YS+Vil6r+fE7w8per5mNed6egGYbZl3QhKXgFzMYSwys+YQw==",
|
"integrity": "sha512-U3MeYdzPJQhtvqAVBPntVgAvNSOJyagwZwyKsFdyRa8TV3pOKVFljalPOCxbw5Wwf2kncGhmP0qHjyazIdNdSA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
"dns-prefetch-control": "0.2.0",
|
|
||||||
"dont-sniff-mimetype": "1.1.0",
|
"dont-sniff-mimetype": "1.1.0",
|
||||||
"expect-ct": "0.2.0",
|
|
||||||
"feature-policy": "0.3.0",
|
"feature-policy": "0.3.0",
|
||||||
"frameguard": "3.1.0",
|
|
||||||
"helmet-crossdomain": "0.4.0",
|
"helmet-crossdomain": "0.4.0",
|
||||||
"helmet-csp": "2.9.5",
|
"helmet-csp": "2.10.0",
|
||||||
"hide-powered-by": "1.1.0",
|
"hide-powered-by": "1.1.0",
|
||||||
"hpkp": "2.0.0",
|
"hpkp": "2.0.0",
|
||||||
"hsts": "2.2.0",
|
"hsts": "2.2.0",
|
||||||
"ienoopen": "1.1.0",
|
|
||||||
"nocache": "2.1.0",
|
"nocache": "2.1.0",
|
||||||
"referrer-policy": "1.2.0",
|
"referrer-policy": "1.2.0",
|
||||||
"x-xss-protection": "1.3.0"
|
"x-xss-protection": "1.3.0"
|
||||||
@ -1794,9 +1843,9 @@
|
|||||||
"integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA=="
|
"integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA=="
|
||||||
},
|
},
|
||||||
"helmet-csp": {
|
"helmet-csp": {
|
||||||
"version": "2.9.5",
|
"version": "2.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.9.5.tgz",
|
"resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.10.0.tgz",
|
||||||
"integrity": "sha512-w9nps5adqFQwgktVPDbXkARmZot/nr8aegzQas9AXdBSwBFBBefPpDSTV0wtgHlAUdDwY6MZo7qAl9yts3ppJg==",
|
"integrity": "sha512-Rz953ZNEFk8sT2XvewXkYN0Ho4GEZdjAZy4stjiEQV3eN7GDxg1QKmYggH7otDyIA7uGA6XnUMVSgeJwbR5X+w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bowser": "2.9.0",
|
"bowser": "2.9.0",
|
||||||
"camelize": "1.0.0",
|
"camelize": "1.0.0",
|
||||||
@ -1879,11 +1928,6 @@
|
|||||||
"safer-buffer": ">= 2.1.2 < 3"
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ienoopen": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ=="
|
|
||||||
},
|
|
||||||
"ignore": {
|
"ignore": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
||||||
@ -2197,9 +2241,9 @@
|
|||||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
|
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
|
||||||
},
|
},
|
||||||
"jsdom": {
|
"jsdom": {
|
||||||
"version": "16.2.2",
|
"version": "16.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.3.0.tgz",
|
||||||
"integrity": "sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg==",
|
"integrity": "sha512-zggeX5UuEknpdZzv15+MS1dPYG0J/TftiiNunOeNxSl3qr8Z6cIlQpN0IdJa44z9aFxZRIVqRncvEhQ7X5DtZg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"abab": "^2.0.3",
|
"abab": "^2.0.3",
|
||||||
"acorn": "^7.1.1",
|
"acorn": "^7.1.1",
|
||||||
@ -2221,7 +2265,7 @@
|
|||||||
"tough-cookie": "^3.0.1",
|
"tough-cookie": "^3.0.1",
|
||||||
"w3c-hr-time": "^1.0.2",
|
"w3c-hr-time": "^1.0.2",
|
||||||
"w3c-xmlserializer": "^2.0.0",
|
"w3c-xmlserializer": "^2.0.0",
|
||||||
"webidl-conversions": "^6.0.0",
|
"webidl-conversions": "^6.1.0",
|
||||||
"whatwg-encoding": "^1.0.5",
|
"whatwg-encoding": "^1.0.5",
|
||||||
"whatwg-mimetype": "^2.3.0",
|
"whatwg-mimetype": "^2.3.0",
|
||||||
"whatwg-url": "^8.0.0",
|
"whatwg-url": "^8.0.0",
|
||||||
@ -2277,13 +2321,6 @@
|
|||||||
"lodash.once": "^4.0.0",
|
"lodash.once": "^4.0.0",
|
||||||
"ms": "^2.1.1",
|
"ms": "^2.1.1",
|
||||||
"semver": "^5.6.0"
|
"semver": "^5.6.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jsprim": {
|
"jsprim": {
|
||||||
@ -2441,12 +2478,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"lru-cache": {
|
"lru-cache": {
|
||||||
"version": "4.1.5",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||||
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
|
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"pseudomap": "^1.0.2",
|
"yallist": "^3.0.2"
|
||||||
"yallist": "^2.1.2"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"make-dir": {
|
"make-dir": {
|
||||||
@ -2487,16 +2523,16 @@
|
|||||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||||
},
|
},
|
||||||
"mime-db": {
|
"mime-db": {
|
||||||
"version": "1.43.0",
|
"version": "1.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||||
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
|
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
|
||||||
},
|
},
|
||||||
"mime-types": {
|
"mime-types": {
|
||||||
"version": "2.1.26",
|
"version": "2.1.27",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||||
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
|
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"mime-db": "1.43.0"
|
"mime-db": "1.44.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mimic-fn": {
|
"mimic-fn": {
|
||||||
@ -2536,29 +2572,48 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"moment": {
|
"moment": {
|
||||||
"version": "2.24.0",
|
"version": "2.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
|
||||||
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
|
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
|
||||||
},
|
},
|
||||||
"moment-timezone": {
|
"moment-timezone": {
|
||||||
"version": "0.5.28",
|
"version": "0.5.31",
|
||||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.28.tgz",
|
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz",
|
||||||
"integrity": "sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw==",
|
"integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"moment": ">= 2.9.0"
|
"moment": ">= 2.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"morgan": {
|
"morgan": {
|
||||||
"version": "1.9.1",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
|
||||||
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
|
"integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"basic-auth": "~2.0.0",
|
"basic-auth": "~2.0.1",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "~1.1.2",
|
"depd": "~2.0.0",
|
||||||
"on-finished": "~2.3.0",
|
"on-finished": "~2.3.0",
|
||||||
"on-headers": "~1.0.1"
|
"on-headers": "~1.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"depd": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
@ -2589,25 +2644,12 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
|
||||||
"integrity": "sha512-ONHr16SQvKZNSqjQT9gy5z24Jw+uqfO02/ngBSBoqChZ+W8qXX7GPRa1RoUnzGADw8K63R1BXUMzarCVQBpY8Q==",
|
"integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3"
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"lru-cache": {
|
|
||||||
"version": "5.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
|
||||||
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
|
||||||
"requires": {
|
|
||||||
"yallist": "^3.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"yallist": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2617,6 +2659,22 @@
|
|||||||
"integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==",
|
"integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lru-cache": "^4.1.3"
|
"lru-cache": "^4.1.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": {
|
||||||
|
"version": "4.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
|
||||||
|
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
|
||||||
|
"requires": {
|
||||||
|
"pseudomap": "^1.0.2",
|
||||||
|
"yallist": "^2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||||
|
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"natural-compare": {
|
"natural-compare": {
|
||||||
@ -2642,9 +2700,9 @@
|
|||||||
"integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q=="
|
"integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q=="
|
||||||
},
|
},
|
||||||
"nodemailer": {
|
"nodemailer": {
|
||||||
"version": "6.4.6",
|
"version": "6.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.6.tgz",
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.11.tgz",
|
||||||
"integrity": "sha512-/kJ+FYVEm2HuUlw87hjSqTss+GU35D4giOpdSfGp7DO+5h6RlJj7R94YaYHOkoxu1CSaM0d3WRBtCzwXrY6MKA=="
|
"integrity": "sha512-BVZBDi+aJV4O38rxsUh164Dk1NCqgh6Cm0rQSb9SK/DHGll/DrCMnycVDD7msJgZCnmVa8ASo8EZzR7jsgTukQ=="
|
||||||
},
|
},
|
||||||
"nodemon": {
|
"nodemon": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
@ -2793,8 +2851,7 @@
|
|||||||
"on-headers": {
|
"on-headers": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"once": {
|
"once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
@ -3319,19 +3376,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"request-promise-core": {
|
"request-promise-core": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
|
||||||
"integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
|
"integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.15"
|
"lodash": "^4.17.19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"request-promise-native": {
|
"request-promise-native": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
|
||||||
"integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==",
|
"integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"request-promise-core": "1.1.3",
|
"request-promise-core": "1.1.4",
|
||||||
"stealthy-require": "^1.1.1",
|
"stealthy-require": "^1.1.1",
|
||||||
"tough-cookie": "^2.3.3"
|
"tough-cookie": "^2.3.3"
|
||||||
},
|
},
|
||||||
@ -3479,6 +3536,21 @@
|
|||||||
"statuses": "~1.5.0"
|
"statuses": "~1.5.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||||
@ -3492,9 +3564,9 @@
|
|||||||
"integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
|
"integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
|
||||||
},
|
},
|
||||||
"sequelize": {
|
"sequelize": {
|
||||||
"version": "5.21.5",
|
"version": "5.22.3",
|
||||||
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.22.3.tgz",
|
||||||
"integrity": "sha512-n9hR5K4uQGmBGK/Y/iqewCeSFmKVsd0TRnh0tfoLoAkmXbKC4tpeK96RhKs7d+TTMtrJlgt2TNLVBaAxEwC4iw==",
|
"integrity": "sha512-+nxf4TzdrB+PRmoWhR05TP9ukLAurK7qtKcIFv5Vhxm5Z9v+d2PcTT6Ea3YAoIQVkZ47QlT9XWAIUevMT/3l8Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bluebird": "^3.5.0",
|
"bluebird": "^3.5.0",
|
||||||
"cls-bluebird": "^2.1.0",
|
"cls-bluebird": "^2.1.0",
|
||||||
@ -3521,11 +3593,6 @@
|
|||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
},
|
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
@ -3644,11 +3711,6 @@
|
|||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
},
|
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"version": "3.4.0",
|
"version": "3.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||||
@ -3768,9 +3830,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"sqlstring": {
|
"sqlstring": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz",
|
||||||
"integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
|
"integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg=="
|
||||||
},
|
},
|
||||||
"sshpk": {
|
"sshpk": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.1",
|
||||||
@ -4121,6 +4183,23 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^2.2.0"
|
"debug": "^2.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniq": {
|
"uniq": {
|
||||||
@ -4193,9 +4272,9 @@
|
|||||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||||
},
|
},
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"version": "7.0.2",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
|
||||||
"integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw=="
|
"integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="
|
||||||
},
|
},
|
||||||
"v8-compile-cache": {
|
"v8-compile-cache": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
@ -4214,9 +4293,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"validator": {
|
"validator": {
|
||||||
"version": "13.0.0",
|
"version": "13.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/validator/-/validator-13.1.1.tgz",
|
||||||
"integrity": "sha512-anYx5fURbgF04lQV18nEQWZ/3wHGnxiKdG4aL8J+jEDsm98n/sU/bey+tYk6tnGJzm7ioh5FoqrAiQ6m03IgaA=="
|
"integrity": "sha512-8GfPiwzzRoWTg7OV1zva1KvrSemuMkv07MA9TTl91hfhe+wKrsrgVN4H2QSFd/U/FhiU3iWPYVgvbsOGwhyFWw=="
|
||||||
},
|
},
|
||||||
"vary": {
|
"vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
@ -4343,9 +4422,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "7.3.0",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
||||||
"integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w=="
|
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
|
||||||
},
|
},
|
||||||
"x-xss-protection": {
|
"x-xss-protection": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
@ -4375,9 +4454,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "2.1.2",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,11 @@
|
|||||||
"sequelize": "^5.21.5",
|
"sequelize": "^5.21.5",
|
||||||
"smart-request-balancer": "^2.1.1",
|
"smart-request-balancer": "^2.1.1",
|
||||||
"uuid": "^7.0.2",
|
"uuid": "^7.0.2",
|
||||||
"validator": "^13.0.0"
|
"validator": "^13.0.0",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"morgan": "^1.9.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dotenv": "^8.2.0",
|
|
||||||
"morgan": "^1.9.1",
|
|
||||||
"nodemon": "^2.0.4",
|
"nodemon": "^2.0.4",
|
||||||
"snazzy": "^8.0.0",
|
"snazzy": "^8.0.0",
|
||||||
"standard": "^14.3.4"
|
"standard": "^14.3.4"
|
||||||
|
@ -13,7 +13,8 @@ AdminRouter.route('/functions')
|
|||||||
.get(adminController.getFunctions)
|
.get(adminController.getFunctions)
|
||||||
|
|
||||||
// Permet de créé une fonction
|
// Permet de créé une fonction
|
||||||
.post(fileUpload({
|
.post(
|
||||||
|
fileUpload({
|
||||||
useTempFiles: true,
|
useTempFiles: true,
|
||||||
safeFileNames: true,
|
safeFileNames: true,
|
||||||
preserveExtension: Number,
|
preserveExtension: Number,
|
||||||
@ -27,7 +28,7 @@ AdminRouter.route('/functions')
|
|||||||
.withMessage('La fonction doit avoir un titre.')
|
.withMessage('La fonction doit avoir un titre.')
|
||||||
.isLength({ max: 100 })
|
.isLength({ max: 100 })
|
||||||
.withMessage('Le titre est trop long.')
|
.withMessage('Le titre est trop long.')
|
||||||
.custom((title) => {
|
.custom(title => {
|
||||||
if (title === 'undefined') {
|
if (title === 'undefined') {
|
||||||
return Promise.reject(new Error('La fonction doit avoir un titre.'))
|
return Promise.reject(new Error('La fonction doit avoir un titre.'))
|
||||||
}
|
}
|
||||||
@ -39,13 +40,13 @@ AdminRouter.route('/functions')
|
|||||||
.withMessage('La fonction doit avoir un slug.')
|
.withMessage('La fonction doit avoir un slug.')
|
||||||
.isLength({ max: 100 })
|
.isLength({ max: 100 })
|
||||||
.withMessage('Le slug est trop long.')
|
.withMessage('Le slug est trop long.')
|
||||||
.custom((slug) => {
|
.custom(slug => {
|
||||||
if (slug === 'undefined') {
|
if (slug === 'undefined') {
|
||||||
return Promise.reject(new Error('La fonction doit avoir un slug.'))
|
return Promise.reject(new Error('La fonction doit avoir un slug.'))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
.custom(async (slug) => {
|
.custom(async slug => {
|
||||||
try {
|
try {
|
||||||
const FunctionSlug = await Functions.findOne({ where: { slug } })
|
const FunctionSlug = await Functions.findOne({ where: { slug } })
|
||||||
if (FunctionSlug) {
|
if (FunctionSlug) {
|
||||||
@ -62,9 +63,11 @@ AdminRouter.route('/functions')
|
|||||||
.withMessage('La fonction doit avoir une description.')
|
.withMessage('La fonction doit avoir une description.')
|
||||||
.isLength({ max: 255, min: 1 })
|
.isLength({ max: 255, min: 1 })
|
||||||
.withMessage('La description est trop longue.')
|
.withMessage('La description est trop longue.')
|
||||||
.custom((description) => {
|
.custom(description => {
|
||||||
if (description === 'undefined') {
|
if (description === 'undefined') {
|
||||||
return Promise.reject(new Error('La fonction doit avoir une description.'))
|
return Promise.reject(
|
||||||
|
new Error('La fonction doit avoir une description.')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
@ -72,9 +75,11 @@ AdminRouter.route('/functions')
|
|||||||
.not()
|
.not()
|
||||||
.isEmpty()
|
.isEmpty()
|
||||||
.withMessage('La fonction doit avoir une catégorie.')
|
.withMessage('La fonction doit avoir une catégorie.')
|
||||||
.custom(async (categorieId) => {
|
.custom(async categorieId => {
|
||||||
try {
|
try {
|
||||||
const categorieFound = await Categories.findOne({ where: { id: parseInt(categorieId) } })
|
const categorieFound = await Categories.findOne({
|
||||||
|
where: { id: parseInt(categorieId) }
|
||||||
|
})
|
||||||
if (!categorieFound) {
|
if (!categorieFound) {
|
||||||
return Promise.reject(new Error("La catégorie n'existe pas!"))
|
return Promise.reject(new Error("La catégorie n'existe pas!"))
|
||||||
}
|
}
|
||||||
@ -83,14 +88,19 @@ AdminRouter.route('/functions')
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
body('type')
|
body('type').custom(type => {
|
||||||
.custom((type) => {
|
|
||||||
if (!(type === 'article' || type === 'form' || type === 'page')) {
|
if (!(type === 'article' || type === 'form' || type === 'page')) {
|
||||||
return Promise.reject(new Error('Le type de la fonction peut être : article, form ou page.'))
|
return Promise.reject(
|
||||||
|
new Error(
|
||||||
|
'Le type de la fonction peut être : article, form ou page.'
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
], adminController.postFunction)
|
],
|
||||||
|
adminController.postFunction
|
||||||
|
)
|
||||||
|
|
||||||
AdminRouter.route('/functions/:slug')
|
AdminRouter.route('/functions/:slug')
|
||||||
|
|
||||||
@ -100,7 +110,8 @@ AdminRouter.route('/functions/:slug')
|
|||||||
AdminRouter.route('/functions/:id')
|
AdminRouter.route('/functions/:id')
|
||||||
|
|
||||||
// Modifie information basique d'une fonction
|
// Modifie information basique d'une fonction
|
||||||
.put(fileUpload({
|
.put(
|
||||||
|
fileUpload({
|
||||||
useTempFiles: true,
|
useTempFiles: true,
|
||||||
safeFileNames: true,
|
safeFileNames: true,
|
||||||
preserveExtension: Number,
|
preserveExtension: Number,
|
||||||
@ -114,7 +125,7 @@ AdminRouter.route('/functions/:id')
|
|||||||
.withMessage('La fonction doit avoir un titre.')
|
.withMessage('La fonction doit avoir un titre.')
|
||||||
.isLength({ max: 100 })
|
.isLength({ max: 100 })
|
||||||
.withMessage('Le titre est trop long.')
|
.withMessage('Le titre est trop long.')
|
||||||
.custom((title) => {
|
.custom(title => {
|
||||||
if (title === 'undefined') {
|
if (title === 'undefined') {
|
||||||
return Promise.reject(new Error('La fonction doit avoir un titre.'))
|
return Promise.reject(new Error('La fonction doit avoir un titre.'))
|
||||||
}
|
}
|
||||||
@ -126,7 +137,7 @@ AdminRouter.route('/functions/:id')
|
|||||||
.withMessage('La fonction doit avoir un slug.')
|
.withMessage('La fonction doit avoir un slug.')
|
||||||
.isLength({ max: 100 })
|
.isLength({ max: 100 })
|
||||||
.withMessage('Le slug est trop long.')
|
.withMessage('Le slug est trop long.')
|
||||||
.custom((slug) => {
|
.custom(slug => {
|
||||||
if (slug === 'undefined') {
|
if (slug === 'undefined') {
|
||||||
return Promise.reject(new Error('La fonction doit avoir un slug.'))
|
return Promise.reject(new Error('La fonction doit avoir un slug.'))
|
||||||
}
|
}
|
||||||
@ -138,9 +149,11 @@ AdminRouter.route('/functions/:id')
|
|||||||
.withMessage('La fonction doit avoir une description.')
|
.withMessage('La fonction doit avoir une description.')
|
||||||
.isLength({ max: 255, min: 1 })
|
.isLength({ max: 255, min: 1 })
|
||||||
.withMessage('La description est trop longue.')
|
.withMessage('La description est trop longue.')
|
||||||
.custom((description) => {
|
.custom(description => {
|
||||||
if (description === 'undefined') {
|
if (description === 'undefined') {
|
||||||
return Promise.reject(new Error('La fonction doit avoir une description.'))
|
return Promise.reject(
|
||||||
|
new Error('La fonction doit avoir une description.')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
@ -148,9 +161,11 @@ AdminRouter.route('/functions/:id')
|
|||||||
.not()
|
.not()
|
||||||
.isEmpty()
|
.isEmpty()
|
||||||
.withMessage('La fonction doit avoir une catégorie.')
|
.withMessage('La fonction doit avoir une catégorie.')
|
||||||
.custom(async (categorieId) => {
|
.custom(async categorieId => {
|
||||||
try {
|
try {
|
||||||
const categorieFound = await Categories.findOne({ where: { id: parseInt(categorieId) } })
|
const categorieFound = await Categories.findOne({
|
||||||
|
where: { id: parseInt(categorieId) }
|
||||||
|
})
|
||||||
if (!categorieFound) {
|
if (!categorieFound) {
|
||||||
return Promise.reject(new Error("La catégorie n'existe pas!"))
|
return Promise.reject(new Error("La catégorie n'existe pas!"))
|
||||||
}
|
}
|
||||||
@ -159,24 +174,27 @@ AdminRouter.route('/functions/:id')
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
body('type')
|
body('type').custom(type => {
|
||||||
.custom((type) => {
|
|
||||||
if (!(type === 'article' || type === 'form' || type === 'page')) {
|
if (!(type === 'article' || type === 'form' || type === 'page')) {
|
||||||
return Promise.reject(new Error('Le type de la fonction peut être : article, form ou page.'))
|
return Promise.reject(
|
||||||
|
new Error(
|
||||||
|
'Le type de la fonction peut être : article, form ou page.'
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
], adminController.putFunction)
|
],
|
||||||
|
adminController.putFunction
|
||||||
|
)
|
||||||
|
|
||||||
// Supprime une fonction avec son id
|
// Supprime une fonction avec son id
|
||||||
.delete(adminController.deleteFunction)
|
.delete(adminController.deleteFunction)
|
||||||
|
|
||||||
AdminRouter.route('/functions/article/:id')
|
AdminRouter.route('/functions/article/:id')
|
||||||
|
|
||||||
.put(adminController.putFunctionArticle)
|
.put(adminController.putFunctionArticle)
|
||||||
|
|
||||||
AdminRouter.route('/functions/form/:id')
|
AdminRouter.route('/functions/form/:id')
|
||||||
|
|
||||||
.put(adminController.putFunctionForm)
|
.put(adminController.putFunctionForm)
|
||||||
|
|
||||||
AdminRouter.route('/categories')
|
AdminRouter.route('/categories')
|
||||||
|
@ -9,7 +9,10 @@ export default function Footer () {
|
|||||||
<a>FunctionProject</a>
|
<a>FunctionProject</a>
|
||||||
</Link>
|
</Link>
|
||||||
- Version 2.1 <br />
|
- Version 2.1 <br />
|
||||||
<a href='https://divlo.fr/' target='_blank' rel='noopener noreferrer'>Divlo</a> | Tous droits réservés
|
<a href='https://divlo.fr/' target='_blank' rel='noopener noreferrer'>
|
||||||
|
Divlo
|
||||||
|
</a>{' '}
|
||||||
|
| Tous droits réservés
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
)
|
)
|
||||||
|
@ -5,7 +5,7 @@ import useAPI from '../../hooks/useAPI'
|
|||||||
import api from '../../utils/api'
|
import api from '../../utils/api'
|
||||||
import '../../public/css/pages/admin.css'
|
import '../../public/css/pages/admin.css'
|
||||||
|
|
||||||
const AddEditFunction = (props) => {
|
const AddEditFunction = props => {
|
||||||
const [, categories] = useAPI('/categories')
|
const [, categories] = useAPI('/categories')
|
||||||
const [inputState, setInputState] = useState(props.defaultInputState)
|
const [inputState, setInputState] = useState(props.defaultInputState)
|
||||||
const [message, setMessage] = useState('')
|
const [message, setMessage] = useState('')
|
||||||
@ -22,18 +22,29 @@ const AddEditFunction = (props) => {
|
|||||||
}
|
}
|
||||||
}, [categories])
|
}, [categories])
|
||||||
|
|
||||||
const apiCallFunction = (formData) => {
|
const apiCallFunction = formData => {
|
||||||
if (props.isEditing) return api.put(`/admin/functions/${inputState.id}`, formData, { headers: { Authorization: props.user.token } })
|
if (props.isEditing) {
|
||||||
return api.post('/admin/functions', formData, { headers: { Authorization: props.user.token } })
|
return api.put(`/admin/functions/${inputState.id}`, formData, {
|
||||||
|
headers: { Authorization: props.user.token }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return api.post('/admin/functions', formData, {
|
||||||
|
headers: { Authorization: props.user.token }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = (event, isTypeCheck = false) => {
|
const handleChange = (event, isTypeCheck = false) => {
|
||||||
const inputStateNew = { ...inputState }
|
const inputStateNew = { ...inputState }
|
||||||
inputStateNew[event.target.name] = (event.target.files != null) ? event.target.files[0] : (isTypeCheck) ? event.target.checked : event.target.value
|
inputStateNew[event.target.name] =
|
||||||
|
event.target.files != null
|
||||||
|
? event.target.files[0]
|
||||||
|
: isTypeCheck
|
||||||
|
? event.target.checked
|
||||||
|
: event.target.value
|
||||||
setInputState(inputStateNew)
|
setInputState(inputStateNew)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = (event) => {
|
const handleSubmit = event => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const formData = new window.FormData()
|
const formData = new window.FormData()
|
||||||
@ -53,8 +64,10 @@ const AddEditFunction = (props) => {
|
|||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
window.location.reload(true)
|
window.location.reload(true)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`)
|
setMessage(
|
||||||
|
`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`
|
||||||
|
)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -63,23 +76,61 @@ const AddEditFunction = (props) => {
|
|||||||
<>
|
<>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className='form-group'>
|
<div className='form-group'>
|
||||||
<label className='form-label' htmlFor='title'>Titre :</label>
|
<label className='form-label' htmlFor='title'>
|
||||||
<input value={inputState.title} onChange={handleChange} type='text' name='title' id='title' className='form-control' placeholder='(e.g : Nombre aléatoire)' />
|
Titre :
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
value={inputState.title}
|
||||||
|
onChange={handleChange}
|
||||||
|
type='text'
|
||||||
|
name='title'
|
||||||
|
id='title'
|
||||||
|
className='form-control'
|
||||||
|
placeholder='(e.g : Nombre aléatoire)'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='form-group'>
|
<div className='form-group'>
|
||||||
<label className='form-label' htmlFor='slug'>Slug :</label>
|
<label className='form-label' htmlFor='slug'>
|
||||||
<input value={inputState.slug} onChange={handleChange} type='text' name='slug' id='slug' className='form-control' placeholder='(e.g : randomNumber)' />
|
Slug :
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
value={inputState.slug}
|
||||||
|
onChange={handleChange}
|
||||||
|
type='text'
|
||||||
|
name='slug'
|
||||||
|
id='slug'
|
||||||
|
className='form-control'
|
||||||
|
placeholder='(e.g : randomNumber)'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='form-group'>
|
<div className='form-group'>
|
||||||
<label className='form-label' htmlFor='description'>Description :</label>
|
<label className='form-label' htmlFor='description'>
|
||||||
<textarea style={{ height: 'auto' }} value={inputState.description} onChange={handleChange} name='description' id='description' className='form-control' rows='5' />
|
Description :
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
style={{ height: 'auto' }}
|
||||||
|
value={inputState.description}
|
||||||
|
onChange={handleChange}
|
||||||
|
name='description'
|
||||||
|
id='description'
|
||||||
|
className='form-control'
|
||||||
|
rows='5'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='form-group'>
|
<div className='form-group'>
|
||||||
<label className='form-label' htmlFor='type'>Type :</label>
|
<label className='form-label' htmlFor='type'>
|
||||||
<select onChange={handleChange} name='type' id='type' className='form-control' {...(props.isEditing) && { value: inputState.type }}>
|
Type :
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
onChange={handleChange}
|
||||||
|
name='type'
|
||||||
|
id='type'
|
||||||
|
className='form-control'
|
||||||
|
{...(props.isEditing && { value: inputState.type })}
|
||||||
|
>
|
||||||
<option value='form'>Formulaire</option>
|
<option value='form'>Formulaire</option>
|
||||||
<option value='article'>Article</option>
|
<option value='article'>Article</option>
|
||||||
<option value='page'>Page</option>
|
<option value='page'>Page</option>
|
||||||
@ -87,37 +138,68 @@ const AddEditFunction = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='form-group'>
|
<div className='form-group'>
|
||||||
<label className='form-label' htmlFor='categorieId'>Catégorie :</label>
|
<label className='form-label' htmlFor='categorieId'>
|
||||||
<select onChange={handleChange} name='categorieId' id='categorieId' className='form-control' {...(props.isEditing) && { value: inputState.categorieId }}>
|
Catégorie :
|
||||||
{categories.map((category) => (
|
</label>
|
||||||
<option key={category.id} value={category.id} className='Admin__Modal-select-option' style={{ backgroundColor: category.color }}>{category.name}</option>
|
<select
|
||||||
|
onChange={handleChange}
|
||||||
|
name='categorieId'
|
||||||
|
id='categorieId'
|
||||||
|
className='form-control'
|
||||||
|
{...(props.isEditing && { value: inputState.categorieId })}
|
||||||
|
>
|
||||||
|
{categories.map(category => (
|
||||||
|
<option
|
||||||
|
key={category.id}
|
||||||
|
value={category.id}
|
||||||
|
className='Admin__Modal-select-option'
|
||||||
|
style={{ backgroundColor: category.color }}
|
||||||
|
>
|
||||||
|
{category.name}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='form-group'>
|
<div className='form-group'>
|
||||||
<label className='form-label' htmlFor='image'>Image <em>(150x150 recommandé)</em> :</label>
|
<label className='form-label' htmlFor='image'>
|
||||||
|
Image <em>(150x150 recommandé)</em> :
|
||||||
|
</label>
|
||||||
<br />
|
<br />
|
||||||
<input onChange={handleChange} accept='image/jpeg,image/jpg,image/png' type='file' name='image' id='image' />
|
<input
|
||||||
|
onChange={handleChange}
|
||||||
|
accept='image/jpeg,image/jpg,image/png'
|
||||||
|
type='file'
|
||||||
|
name='image'
|
||||||
|
id='image'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(props.isEditing) &&
|
{props.isEditing && (
|
||||||
<div className='form-group custom-control custom-switch'>
|
<div className='form-group custom-control custom-switch'>
|
||||||
<input onChange={(event) => handleChange(event, true)} type='checkbox' name='isOnline' checked={inputState.isOnline} className='custom-control-input' id='isOnline' />
|
<input
|
||||||
<label className='custom-control-label' htmlFor='isOnline'>isOnline</label>
|
onChange={event => handleChange(event, true)}
|
||||||
</div>}
|
type='checkbox'
|
||||||
|
name='isOnline'
|
||||||
|
checked={inputState.isOnline}
|
||||||
|
className='custom-control-input'
|
||||||
|
id='isOnline'
|
||||||
|
/>
|
||||||
|
<label className='custom-control-label' htmlFor='isOnline'>
|
||||||
|
isOnline
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className='form-group text-center'>
|
<div className='form-group text-center'>
|
||||||
<button type='submit' className='btn btn-dark'>Envoyer</button>
|
<button type='submit' className='btn btn-dark'>
|
||||||
|
Envoyer
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div className='form-result text-center'>
|
<div className='form-result text-center'>
|
||||||
{
|
{isLoading ? <Loader /> : htmlParser(message)}
|
||||||
(isLoading)
|
|
||||||
? <Loader />
|
|
||||||
: htmlParser(message)
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -6,19 +6,16 @@ import FunctionArticle from '../FunctionPage/FunctionArticle'
|
|||||||
import 'notyf/notyf.min.css'
|
import 'notyf/notyf.min.css'
|
||||||
import '../../public/css/suneditor.min.css'
|
import '../../public/css/suneditor.min.css'
|
||||||
|
|
||||||
const SunEditor = dynamic(
|
const SunEditor = dynamic(() => import('suneditor-react'), { ssr: false })
|
||||||
() => import('suneditor-react'),
|
|
||||||
{ ssr: false }
|
|
||||||
)
|
|
||||||
|
|
||||||
const EditArticleFunction = (props) => {
|
const EditArticleFunction = props => {
|
||||||
const [htmlContent, setHtmlContent] = useState('')
|
const [htmlContent, setHtmlContent] = useState('')
|
||||||
|
|
||||||
const handleEditorChange = (content) => {
|
const handleEditorChange = content => {
|
||||||
setHtmlContent(content)
|
setHtmlContent(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = async (content) => {
|
const handleSave = async content => {
|
||||||
let Notyf
|
let Notyf
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
Notyf = require('notyf')
|
Notyf = require('notyf')
|
||||||
@ -27,7 +24,11 @@ const EditArticleFunction = (props) => {
|
|||||||
duration: 5000
|
duration: 5000
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
await api.put(`/admin/functions/article/${props.functionInfo.id}`, { article: content }, { headers: { Authorization: props.user.token } })
|
await api.put(
|
||||||
|
`/admin/functions/article/${props.functionInfo.id}`,
|
||||||
|
{ article: content },
|
||||||
|
{ headers: { Authorization: props.user.token } }
|
||||||
|
)
|
||||||
notyf.success('Sauvegardé!')
|
notyf.success('Sauvegardé!')
|
||||||
} catch {
|
} catch {
|
||||||
notyf.error('Erreur!')
|
notyf.error('Erreur!')
|
||||||
@ -36,7 +37,12 @@ const EditArticleFunction = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='container-fluid'>
|
<div className='container-fluid'>
|
||||||
<SunEditor setContents={props.functionInfo.article} lang='fr' onChange={handleEditorChange} setOptions={{ buttonList: complex, callBackSave: handleSave }} />
|
<SunEditor
|
||||||
|
setContents={props.functionInfo.article}
|
||||||
|
lang='fr'
|
||||||
|
onChange={handleEditorChange}
|
||||||
|
setOptions={{ buttonList: complex, callBackSave: handleSave }}
|
||||||
|
/>
|
||||||
<FunctionArticle article={htmlContent} />
|
<FunctionArticle article={htmlContent} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -2,8 +2,10 @@ import { useState } from 'react'
|
|||||||
import api from '../../utils/api'
|
import api from '../../utils/api'
|
||||||
import 'notyf/notyf.min.css'
|
import 'notyf/notyf.min.css'
|
||||||
|
|
||||||
const EditFormFunction = (props) => {
|
const EditFormFunction = props => {
|
||||||
const [inputsArray, setInputsArray] = useState(props.functionInfo.utilizationForm || [])
|
const [inputsArray, setInputsArray] = useState(
|
||||||
|
props.functionInfo.utilizationForm || []
|
||||||
|
)
|
||||||
|
|
||||||
const addInput = () => {
|
const addInput = () => {
|
||||||
const newInputsArray = [...inputsArray]
|
const newInputsArray = [...inputsArray]
|
||||||
@ -11,7 +13,7 @@ const EditFormFunction = (props) => {
|
|||||||
setInputsArray(newInputsArray)
|
setInputsArray(newInputsArray)
|
||||||
}
|
}
|
||||||
|
|
||||||
const addOption = (event) => {
|
const addOption = event => {
|
||||||
const newInputsArray = [...inputsArray]
|
const newInputsArray = [...inputsArray]
|
||||||
const index = event.target.id.split('-')[1]
|
const index = event.target.id.split('-')[1]
|
||||||
const inputObject = newInputsArray[index]
|
const inputObject = newInputsArray[index]
|
||||||
@ -27,7 +29,7 @@ const EditFormFunction = (props) => {
|
|||||||
setInputsArray(newInputsArray)
|
setInputsArray(newInputsArray)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChangeInput = (event) => {
|
const handleChangeInput = event => {
|
||||||
const newInputsArray = [...inputsArray]
|
const newInputsArray = [...inputsArray]
|
||||||
const index = event.target.id.split('-')[1]
|
const index = event.target.id.split('-')[1]
|
||||||
const inputObject = newInputsArray[index]
|
const inputObject = newInputsArray[index]
|
||||||
@ -38,7 +40,7 @@ const EditFormFunction = (props) => {
|
|||||||
setInputsArray(newInputsArray)
|
setInputsArray(newInputsArray)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = async (event) => {
|
const handleSubmit = async event => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
let Notyf
|
let Notyf
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
@ -48,14 +50,18 @@ const EditFormFunction = (props) => {
|
|||||||
duration: 5000
|
duration: 5000
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
await api.put(`/admin/functions/form/${props.functionInfo.id}`, { form: inputsArray }, { headers: { Authorization: props.user.token } })
|
await api.put(
|
||||||
|
`/admin/functions/form/${props.functionInfo.id}`,
|
||||||
|
{ form: inputsArray },
|
||||||
|
{ headers: { Authorization: props.user.token } }
|
||||||
|
)
|
||||||
notyf.success('Sauvegardé!')
|
notyf.success('Sauvegardé!')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notyf.error('Erreur!')
|
notyf.error('Erreur!')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRemoveInput = (event) => {
|
const handleRemoveInput = event => {
|
||||||
const newInputsArray = [...inputsArray]
|
const newInputsArray = [...inputsArray]
|
||||||
const index = event.target.id.split('-')[1]
|
const index = event.target.id.split('-')[1]
|
||||||
newInputsArray.splice(index, 1)
|
newInputsArray.splice(index, 1)
|
||||||
@ -72,39 +78,88 @@ const EditFormFunction = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='container-fluid'>
|
<div className='container-fluid'>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
|
{inputsArray.length > 0 && (
|
||||||
{(inputsArray.length > 0) &&
|
|
||||||
<div className='form-group text-center'>
|
<div className='form-group text-center'>
|
||||||
<button type='submit' className='btn btn-dark'>Sauvegarder</button>
|
<button type='submit' className='btn btn-dark'>
|
||||||
</div>}
|
Sauvegarder
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{inputsArray.map((input, index) => {
|
{inputsArray.map((input, index) => {
|
||||||
return (
|
return (
|
||||||
<div key={index} className='form-group Admin__Input-group'>
|
<div key={index} className='form-group Admin__Input-group'>
|
||||||
|
|
||||||
<div className='text-center'>
|
<div className='text-center'>
|
||||||
<button type='button' onClick={handleRemoveInput} id={`remove-${index}`} className='btn btn-dark'>Supprimer l'input</button>
|
<button
|
||||||
|
type='button'
|
||||||
|
onClick={handleRemoveInput}
|
||||||
|
id={`remove-${index}`}
|
||||||
|
className='btn btn-dark'
|
||||||
|
>
|
||||||
|
Supprimer l'input
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label className='form-label' htmlFor={`name-${index}`}>Nom de l'input :</label>
|
<label className='form-label' htmlFor={`name-${index}`}>
|
||||||
<input value={input.name} onChange={handleChangeInput} type='text' name='name' id={`name-${index}`} className='form-control' placeholder='(e.g : cityName)' />
|
Nom de l'input :
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
value={input.name}
|
||||||
|
onChange={handleChangeInput}
|
||||||
|
type='text'
|
||||||
|
name='name'
|
||||||
|
id={`name-${index}`}
|
||||||
|
className='form-control'
|
||||||
|
placeholder='(e.g : cityName)'
|
||||||
|
/>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<label className='form-label' htmlFor={`label-${index}`}>Label :</label>
|
<label className='form-label' htmlFor={`label-${index}`}>
|
||||||
<input value={input.label} onChange={handleChangeInput} type='text' name='label' id={`label-${index}`} className='form-control' placeholder="(e.g : Entrez le nom d'une ville :)" />
|
Label :
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
value={input.label}
|
||||||
|
onChange={handleChangeInput}
|
||||||
|
type='text'
|
||||||
|
name='label'
|
||||||
|
id={`label-${index}`}
|
||||||
|
className='form-control'
|
||||||
|
placeholder="(e.g : Entrez le nom d'une ville :)"
|
||||||
|
/>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
{(input.type !== 'select') &&
|
{input.type !== 'select' && (
|
||||||
<>
|
<>
|
||||||
<label className='form-label' htmlFor={`placeholder-${index}`}>Placeholder :</label>
|
<label
|
||||||
<input value={input.placeholder} onChange={handleChangeInput} type='text' name='placeholder' id={`placeholder-${index}`} className='form-control' placeholder='(e.g : Paris, FR)' />
|
className='form-label'
|
||||||
|
htmlFor={`placeholder-${index}`}
|
||||||
|
>
|
||||||
|
Placeholder :
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
value={input.placeholder}
|
||||||
|
onChange={handleChangeInput}
|
||||||
|
type='text'
|
||||||
|
name='placeholder'
|
||||||
|
id={`placeholder-${index}`}
|
||||||
|
className='form-control'
|
||||||
|
placeholder='(e.g : Paris, FR)'
|
||||||
|
/>
|
||||||
<br />
|
<br />
|
||||||
</>}
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<label className='form-label' htmlFor={`type-${index}`}>Type :</label>
|
<label className='form-label' htmlFor={`type-${index}`}>
|
||||||
<select value={input.type} onChange={handleChangeInput} name='type' id={`type-${index}`} className='form-control'>
|
Type :
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={input.type}
|
||||||
|
onChange={handleChangeInput}
|
||||||
|
name='type'
|
||||||
|
id={`type-${index}`}
|
||||||
|
className='form-control'
|
||||||
|
>
|
||||||
<option value='text'>text</option>
|
<option value='text'>text</option>
|
||||||
<option value='integer'>Number integer</option>
|
<option value='integer'>Number integer</option>
|
||||||
<option value='float'>Number float</option>
|
<option value='float'>Number float</option>
|
||||||
@ -112,40 +167,87 @@ const EditFormFunction = (props) => {
|
|||||||
<option value='select'>select</option>
|
<option value='select'>select</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
{(input.type === 'select') &&
|
{input.type === 'select' && (
|
||||||
<div style={{ marginTop: '50px' }}>
|
<div style={{ marginTop: '50px' }}>
|
||||||
|
|
||||||
<label className='form-label'>Options :</label>
|
<label className='form-label'>Options :</label>
|
||||||
|
|
||||||
{input.options.map((option, optionIndex) => {
|
{input.options.map((option, optionIndex) => {
|
||||||
return (
|
return (
|
||||||
<div key={optionIndex} style={{ margin: '0 0 30px 0' }} className='form-group Admin__Input-group'>
|
<div
|
||||||
|
key={optionIndex}
|
||||||
|
style={{ margin: '0 0 30px 0' }}
|
||||||
|
className='form-group Admin__Input-group'
|
||||||
|
>
|
||||||
<div className='text-center'>
|
<div className='text-center'>
|
||||||
<button type='button' onClick={() => handleRemoveOption(index, optionIndex)} className='btn btn-dark'>Supprimer l'option</button>
|
<button
|
||||||
|
type='button'
|
||||||
|
onClick={() =>
|
||||||
|
handleRemoveOption(index, optionIndex)}
|
||||||
|
className='btn btn-dark'
|
||||||
|
>
|
||||||
|
Supprimer l'option
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label className='form-label' htmlFor={`optionName-${optionIndex}-${index}`}>Nom de l'option</label>
|
<label
|
||||||
<input onChange={(event) => handleChangeOption(index, optionIndex, event)} value={option.name} id={`optionName-${optionIndex}-${index}`} name='name' type='text' className='form-control' placeholder="Nom de l'option" />
|
className='form-label'
|
||||||
|
htmlFor={`optionName-${optionIndex}-${index}`}
|
||||||
|
>
|
||||||
|
Nom de l'option
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
onChange={event =>
|
||||||
|
handleChangeOption(index, optionIndex, event)}
|
||||||
|
value={option.name}
|
||||||
|
id={`optionName-${optionIndex}-${index}`}
|
||||||
|
name='name'
|
||||||
|
type='text'
|
||||||
|
className='form-control'
|
||||||
|
placeholder="Nom de l'option"
|
||||||
|
/>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<label className='form-label' htmlFor={`optionValue-${optionIndex}-${index}`}>Valeur de l'option</label>
|
<label
|
||||||
<input onChange={(event) => handleChangeOption(index, optionIndex, event)} value={option.value} id={`optionValue-${optionIndex}-${index}`} name='value' type='text' className='form-control' placeholder="Valeur de l'option" />
|
className='form-label'
|
||||||
|
htmlFor={`optionValue-${optionIndex}-${index}`}
|
||||||
|
>
|
||||||
|
Valeur de l'option
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
onChange={event =>
|
||||||
|
handleChangeOption(index, optionIndex, event)}
|
||||||
|
value={option.value}
|
||||||
|
id={`optionValue-${optionIndex}-${index}`}
|
||||||
|
name='value'
|
||||||
|
type='text'
|
||||||
|
className='form-control'
|
||||||
|
placeholder="Valeur de l'option"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<div className='form-group text-center'>
|
<div className='form-group text-center'>
|
||||||
<button id={`optionAdd-${index}`} onClick={addOption} type='button' className='btn btn-dark'>Ajouter une option</button>
|
<button
|
||||||
|
id={`optionAdd-${index}`}
|
||||||
|
onClick={addOption}
|
||||||
|
type='button'
|
||||||
|
className='btn btn-dark'
|
||||||
|
>
|
||||||
|
Ajouter une option
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div style={{ marginBottom: '30px' }} className='form-group text-center'>
|
<div style={{ marginBottom: '30px' }} className='form-group text-center'>
|
||||||
<button type='button' onClick={addInput} className='btn btn-dark'>Ajouter un input</button>
|
<button type='button' onClick={addInput} className='btn btn-dark'>
|
||||||
|
Ajouter un input
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
margin: 0 0 50px 0;
|
margin: 0 0 50px 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all .3s;
|
transition: all 0.3s;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
text-decoration: none !important;
|
text-decoration: none !important;
|
||||||
}
|
}
|
||||||
|
@ -5,51 +5,76 @@ import Loader from '../Loader'
|
|||||||
import { API_URL } from '../../utils/config/config'
|
import { API_URL } from '../../utils/config/config'
|
||||||
import './FunctionCard.css'
|
import './FunctionCard.css'
|
||||||
|
|
||||||
const FunctionCard = memo(forwardRef((props, ref) => {
|
const FunctionCard = memo(
|
||||||
|
forwardRef((props, ref) => {
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
|
||||||
const handleLoad = () => {
|
const handleLoad = () => {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleError = (event) => {
|
const handleError = event => {
|
||||||
event.target.src = API_URL + '/images/functions/default.png'
|
event.target.src = API_URL + '/images/functions/default.png'
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFormOrArticle = (props.type === 'form' || props.type === 'article')
|
const isFormOrArticle = props.type === 'form' || props.type === 'article'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
{
|
{...(props.isAdmin
|
||||||
...(props.isAdmin)
|
|
||||||
? {
|
? {
|
||||||
href: '/admin/[slug]',
|
href: '/admin/[slug]',
|
||||||
as: `/admin/${props.slug}`
|
as: `/admin/${props.slug}`
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
href: (isFormOrArticle) ? '/functions/[slug]' : `/functions/${props.slug}`,
|
href: isFormOrArticle
|
||||||
|
? '/functions/[slug]'
|
||||||
|
: `/functions/${props.slug}`,
|
||||||
as: `/functions/${props.slug}`
|
as: `/functions/${props.slug}`
|
||||||
}
|
})}
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{/* FunctionCard a une hauteur pendant chargement */}
|
{/* FunctionCard a une hauteur pendant chargement */}
|
||||||
<a ref={ref} style={isLoading ? { height: '360px', justifyContent: 'center' } : null} className='FunctionCard col-sm-24 col-md-10 col-xl-7'>
|
<a
|
||||||
|
ref={ref}
|
||||||
|
style={
|
||||||
|
isLoading ? { height: '360px', justifyContent: 'center' } : null
|
||||||
|
}
|
||||||
|
className='FunctionCard col-sm-24 col-md-10 col-xl-7'
|
||||||
|
>
|
||||||
{isLoading && <Loader width='125px' height='125px' />}
|
{isLoading && <Loader width='125px' height='125px' />}
|
||||||
|
|
||||||
<div className={`FunctionCard__container ${isLoading ? 'd-none' : ''}`}>
|
<div
|
||||||
|
className={`FunctionCard__container ${isLoading ? 'd-none' : ''}`}
|
||||||
|
>
|
||||||
<div className='FunctionCard__top'>
|
<div className='FunctionCard__top'>
|
||||||
<img onLoad={handleLoad} onError={handleError} className='FunctionCard__image' alt={props.title} src={API_URL + props.image} />
|
<img
|
||||||
|
onLoad={handleLoad}
|
||||||
|
onError={handleError}
|
||||||
|
className='FunctionCard__image'
|
||||||
|
alt={props.title}
|
||||||
|
src={API_URL + props.image}
|
||||||
|
/>
|
||||||
<h2 className='FunctionCard__title'>{props.title}</h2>
|
<h2 className='FunctionCard__title'>{props.title}</h2>
|
||||||
<p className='FunctionCard__description text-center'>{props.description}</p>
|
<p className='FunctionCard__description text-center'>
|
||||||
|
{props.description}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='FunctionCard__info'>
|
<div className='FunctionCard__info'>
|
||||||
<p className='FunctionCard__category' style={{ backgroundColor: props.categorie.color }}>{props.categorie.name}</p>
|
<p
|
||||||
<p className='FunctionCard__publication-date'>{date.format(new Date(props.createdAt), 'DD/MM/YYYY', true)}</p>
|
className='FunctionCard__category'
|
||||||
|
style={{ backgroundColor: props.categorie.color }}
|
||||||
|
>
|
||||||
|
{props.categorie.name}
|
||||||
|
</p>
|
||||||
|
<p className='FunctionCard__publication-date'>
|
||||||
|
{date.format(new Date(props.createdAt), 'DD/MM/YYYY', true)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
export default FunctionCard
|
export default FunctionCard
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: .7em;
|
border-radius: 0.7em;
|
||||||
margin: 15px 0 15px 0;
|
margin: 15px 0 15px 0;
|
||||||
}
|
}
|
||||||
.CommentCard__container {
|
.CommentCard__container {
|
||||||
|
@ -23,28 +23,42 @@ const CommentCard = forwardRef((props, ref) => {
|
|||||||
props.manageComment.setLoadingComments(true)
|
props.manageComment.setLoadingComments(true)
|
||||||
if (isAuth && user.token != null) {
|
if (isAuth && user.token != null) {
|
||||||
try {
|
try {
|
||||||
await api.delete(`/comments/${props.id}`, { headers: { Authorization: user.token } })
|
await api.delete(`/comments/${props.id}`, {
|
||||||
|
headers: { Authorization: user.token }
|
||||||
|
})
|
||||||
const newCommentsData = { ...props.manageComment.commentsData }
|
const newCommentsData = { ...props.manageComment.commentsData }
|
||||||
const commentIndex = newCommentsData.rows.findIndex((value) => value.id === props.id)
|
const commentIndex = newCommentsData.rows.findIndex(
|
||||||
|
value => value.id === props.id
|
||||||
|
)
|
||||||
newCommentsData.rows.splice(commentIndex, 1)
|
newCommentsData.rows.splice(commentIndex, 1)
|
||||||
props.manageComment.setCommentsData({ hasMore: props.manageComment.commentsData.hasMore, rows: newCommentsData.rows })
|
props.manageComment.setCommentsData({
|
||||||
|
hasMore: props.manageComment.commentsData.hasMore,
|
||||||
|
rows: newCommentsData.rows
|
||||||
|
})
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
props.manageComment.setLoadingComments(false)
|
props.manageComment.setLoadingComments(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = (event) => {
|
const handleChange = event => {
|
||||||
setEditInput(event.target.value)
|
setEditInput(event.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = (event) => {
|
const handleSubmit = event => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
api.put(`/comments/${props.id}`, { message: editInput }, { headers: { Authorization: user.token } })
|
api
|
||||||
.then((_response) => {
|
.put(
|
||||||
|
`/comments/${props.id}`,
|
||||||
|
{ message: editInput },
|
||||||
|
{ headers: { Authorization: user.token } }
|
||||||
|
)
|
||||||
|
.then(_response => {
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`)
|
setMessage(
|
||||||
|
`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,47 +72,73 @@ const CommentCard = forwardRef((props, ref) => {
|
|||||||
<div className='CommentCard__container'>
|
<div className='CommentCard__container'>
|
||||||
<div className='row'>
|
<div className='row'>
|
||||||
<Link href='/users/[name]' as={`/users/${props.user.name}`}>
|
<Link href='/users/[name]' as={`/users/${props.user.name}`}>
|
||||||
<img className='CommentCard__user-logo' src={API_URL + props.user.logo} alt={props.user.name} />
|
<img
|
||||||
|
className='CommentCard__user-logo'
|
||||||
|
src={API_URL + props.user.logo}
|
||||||
|
alt={props.user.name}
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<span className='CommentCard__message-info'>
|
<span className='CommentCard__message-info'>
|
||||||
<Link href='/users/[name]' as={`/users/${props.user.name}`}>
|
<Link href='/users/[name]' as={`/users/${props.user.name}`}>
|
||||||
<a>{props.user.name}</a>
|
<a>{props.user.name}</a>
|
||||||
</Link>
|
</Link>
|
||||||
- {date.format(new Date(props.createdAt), 'DD/MM/YYYY à HH:mm', true)}
|
-{' '}
|
||||||
|
{date.format(new Date(props.createdAt), 'DD/MM/YYYY à HH:mm', true)}
|
||||||
</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 source={editInput} renderers={{ code: CodeBlock }} />
|
<ReactMarkdown
|
||||||
|
source={editInput}
|
||||||
|
renderers={{ code: CodeBlock }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{(isAuth && user.name === props.user.name) &&
|
{isAuth && user.name === props.user.name && (
|
||||||
<p style={{ fontSize: '15px', margin: '15px 0 0 0', fontStyle: 'italic' }}>
|
<p
|
||||||
<a onClick={deleteCommentById} href='#'>supprimer</a>
|
style={{
|
||||||
|
fontSize: '15px',
|
||||||
|
margin: '15px 0 0 0',
|
||||||
|
fontStyle: 'italic'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<a onClick={deleteCommentById} href='#'>
|
||||||
|
supprimer
|
||||||
|
</a>
|
||||||
-
|
-
|
||||||
<a style={{ cursor: 'pointer' }} onClick={editComment}>modifier</a>
|
<a style={{ cursor: 'pointer' }} onClick={editComment}>
|
||||||
</p>}
|
modifier
|
||||||
|
</a>
|
||||||
|
</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'>Modifier le commentaire :</label>
|
<label className='form-label' htmlFor='commentEdit'>
|
||||||
<textarea style={{ height: 'auto' }} value={editInput} onChange={handleChange} name='commentEdit' id='commentEdit' className='form-control' rows='5' placeholder="Idée d'amélioration, avis, remarque, partage d'expérience personnel, ... (Markdown autorisé)" />
|
Modifier le commentaire :
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
style={{ height: 'auto' }}
|
||||||
|
value={editInput}
|
||||||
|
onChange={handleChange}
|
||||||
|
name='commentEdit'
|
||||||
|
id='commentEdit'
|
||||||
|
className='form-control'
|
||||||
|
rows='5'
|
||||||
|
placeholder="Idée d'amélioration, avis, remarque, partage d'expérience personnel, ... (Markdown autorisé)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='form-group' style={{ marginTop: '0.7em' }}>
|
<div className='form-group' style={{ marginTop: '0.7em' }}>
|
||||||
<button type='submit' className='btn btn-dark'>Envoyer</button>
|
<button type='submit' className='btn btn-dark'>
|
||||||
</div>
|
Envoyer
|
||||||
<div className='text-center'>
|
</button>
|
||||||
{htmlParser(message)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className='text-center'>{htmlParser(message)}</div>
|
||||||
</form>
|
</form>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +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) ? htmlParser(article) : <p className='text-center'>L'article n'est pas encore disponible.</p>}
|
{article != null ? (
|
||||||
|
htmlParser(article)
|
||||||
|
) : (
|
||||||
|
<p className='text-center'>L'article n'est pas encore disponible.</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -18,47 +18,67 @@ const FunctionComments = ({ functionId }) => {
|
|||||||
|
|
||||||
// Récupère les commentaires si la page change
|
// Récupère les commentaires si la page change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCommentsData().then((data) => setCommentsData({
|
getCommentsData().then(data =>
|
||||||
|
setCommentsData({
|
||||||
hasMore: data.hasMore,
|
hasMore: data.hasMore,
|
||||||
rows: [...commentsData.rows, ...data.rows]
|
rows: [...commentsData.rows, ...data.rows]
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
}, [pageComments])
|
}, [pageComments])
|
||||||
|
|
||||||
// Permet la pagination au scroll
|
// Permet la pagination au scroll
|
||||||
const observer = useRef()
|
const observer = useRef()
|
||||||
const lastCommentCardRef = useCallback((node) => {
|
const lastCommentCardRef = useCallback(
|
||||||
|
node => {
|
||||||
if (isLoadingComments) return
|
if (isLoadingComments) return
|
||||||
if (observer.current) observer.current.disconnect()
|
if (observer.current) observer.current.disconnect()
|
||||||
observer.current = new window.IntersectionObserver((entries) => {
|
observer.current = new window.IntersectionObserver(
|
||||||
|
entries => {
|
||||||
if (entries[0].isIntersecting && commentsData.hasMore) {
|
if (entries[0].isIntersecting && commentsData.hasMore) {
|
||||||
setPageComments(pageComments + 1)
|
setPageComments(pageComments + 1)
|
||||||
}
|
}
|
||||||
}, { threshold: 1 })
|
},
|
||||||
|
{ threshold: 1 }
|
||||||
|
)
|
||||||
if (node) observer.current.observe(node)
|
if (node) observer.current.observe(node)
|
||||||
}, [isLoadingComments, commentsData.hasMore])
|
},
|
||||||
|
[isLoadingComments, commentsData.hasMore]
|
||||||
|
)
|
||||||
|
|
||||||
const getCommentsData = async () => {
|
const getCommentsData = async () => {
|
||||||
setLoadingComments(true)
|
setLoadingComments(true)
|
||||||
const { data } = await api.get(`/comments/${functionId}/?page=${pageComments}&limit=10`)
|
const { data } = await api.get(
|
||||||
|
`/comments/${functionId}/?page=${pageComments}&limit=10`
|
||||||
|
)
|
||||||
setLoadingComments(false)
|
setLoadingComments(false)
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = (event) => {
|
const handleChange = event => {
|
||||||
const inputStateNew = { ...inputState }
|
const inputStateNew = { ...inputState }
|
||||||
inputStateNew[event.target.name] = event.target.value
|
inputStateNew[event.target.name] = event.target.value
|
||||||
setInputState(inputStateNew)
|
setInputState(inputStateNew)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = async (event) => {
|
const handleSubmit = async event => {
|
||||||
setLoadingComments(true)
|
setLoadingComments(true)
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const token = user.token
|
const token = user.token
|
||||||
if (isAuth && token != null && inputState.commentPost != null) {
|
if (isAuth && token != null && inputState.commentPost != null) {
|
||||||
try {
|
try {
|
||||||
const response = await api.post(`/comments/${functionId}`, { message: inputState.commentPost }, { headers: { Authorization: token } })
|
const response = await api.post(
|
||||||
const comment = { ...response.data, user: { name: user.name, logo: user.logo } }
|
`/comments/${functionId}`,
|
||||||
setCommentsData({ hasMore: commentsData.hasMore, rows: [comment, ...commentsData.rows] })
|
{ message: inputState.commentPost },
|
||||||
|
{ headers: { Authorization: token } }
|
||||||
|
)
|
||||||
|
const comment = {
|
||||||
|
...response.data,
|
||||||
|
user: { name: user.name, logo: user.logo }
|
||||||
|
}
|
||||||
|
setCommentsData({
|
||||||
|
hasMore: commentsData.hasMore,
|
||||||
|
rows: [comment, ...commentsData.rows]
|
||||||
|
})
|
||||||
setInputState({ commentPost: '' })
|
setInputState({ commentPost: '' })
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
@ -70,40 +90,73 @@ 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'>Ajouter un commentaire :</label>
|
<label className='form-label' htmlFor='commentPost'>
|
||||||
<textarea className='FunctionComments__textarea form-control' value={inputState.commentPost} onChange={handleChange} name='commentPost' id='commentPost' placeholder="Idée d'amélioration, avis, remarque, partage d'expérience personnel, ... (Markdown autorisé)" />
|
Ajouter un commentaire :
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
className='FunctionComments__textarea form-control'
|
||||||
|
value={inputState.commentPost}
|
||||||
|
onChange={handleChange}
|
||||||
|
name='commentPost'
|
||||||
|
id='commentPost'
|
||||||
|
placeholder="Idée d'amélioration, avis, remarque, partage d'expérience personnel, ... (Markdown autorisé)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='form-group' style={{ marginTop: '0.7em' }}>
|
<div className='form-group' style={{ marginTop: '0.7em' }}>
|
||||||
<button type='submit' className='btn btn-dark'>Envoyer</button>
|
<button type='submit' className='btn btn-dark'>
|
||||||
|
Envoyer
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)
|
) : (
|
||||||
: (
|
|
||||||
<p className='text-center'>
|
<p className='text-center'>
|
||||||
Vous devez être <Link href='/users/login'><a>connecté</a></Link> pour poster un commentaire.
|
Vous devez être{' '}
|
||||||
|
<Link href='/users/login'>
|
||||||
|
<a>connecté</a>
|
||||||
|
</Link>{' '}
|
||||||
|
pour poster un commentaire.
|
||||||
</p>
|
</p>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='container-fluid'>
|
<div className='container-fluid'>
|
||||||
{isLoadingComments &&
|
{isLoadingComments && (
|
||||||
<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'>
|
||||||
{commentsData.rows.map((comment, index) => {
|
{commentsData.rows.map((comment, index) => {
|
||||||
// Si c'est le dernier élément
|
// Si c'est le dernier élément
|
||||||
if (commentsData.rows.length === index + 1) {
|
if (commentsData.rows.length === index + 1) {
|
||||||
return <CommentCard key={comment.id} ref={lastCommentCardRef} {...comment} manageComment={{ setCommentsData, commentsData, setLoadingComments }} />
|
return (
|
||||||
|
<CommentCard
|
||||||
|
key={comment.id}
|
||||||
|
ref={lastCommentCardRef}
|
||||||
|
{...comment}
|
||||||
|
manageComment={{
|
||||||
|
setCommentsData,
|
||||||
|
commentsData,
|
||||||
|
setLoadingComments
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return <CommentCard key={comment.id} {...comment} manageComment={{ setCommentsData, commentsData, setLoadingComments }} />
|
return (
|
||||||
|
<CommentCard
|
||||||
|
key={comment.id}
|
||||||
|
{...comment}
|
||||||
|
manageComment={{
|
||||||
|
setCommentsData,
|
||||||
|
commentsData,
|
||||||
|
setLoadingComments
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,7 +9,7 @@ import api from '../../utils/api'
|
|||||||
import { API_URL } from '../../utils/config/config'
|
import { API_URL } from '../../utils/config/config'
|
||||||
import '../FunctionCard/FunctionCard.css'
|
import '../FunctionCard/FunctionCard.css'
|
||||||
|
|
||||||
const FunctionComponentTop = (props) => {
|
const FunctionComponentTop = props => {
|
||||||
const { isAuth, user } = useContext(UserContext)
|
const { isAuth, user } = useContext(UserContext)
|
||||||
const [isFavorite, setIsFavorite] = useState(false)
|
const [isFavorite, setIsFavorite] = useState(false)
|
||||||
|
|
||||||
@ -21,7 +21,9 @@ const FunctionComponentTop = (props) => {
|
|||||||
|
|
||||||
const fetchFavorite = async () => {
|
const fetchFavorite = async () => {
|
||||||
try {
|
try {
|
||||||
const favoriteResponse = await api.get(`/favorites/${props.id}`, { headers: { Authorization: user.token } })
|
const favoriteResponse = await api.get(`/favorites/${props.id}`, {
|
||||||
|
headers: { Authorization: user.token }
|
||||||
|
})
|
||||||
setIsFavorite(favoriteResponse.data.isFavorite)
|
setIsFavorite(favoriteResponse.data.isFavorite)
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
@ -30,16 +32,22 @@ const FunctionComponentTop = (props) => {
|
|||||||
if (isAuth && user.token != null) {
|
if (isAuth && user.token != null) {
|
||||||
try {
|
try {
|
||||||
if (isFavorite) {
|
if (isFavorite) {
|
||||||
const response = await api.delete(`/favorites/${props.id}`, { headers: { Authorization: user.token } })
|
const response = await api.delete(`/favorites/${props.id}`, {
|
||||||
|
headers: { Authorization: user.token }
|
||||||
|
})
|
||||||
if (response.status === 200) return setIsFavorite(false)
|
if (response.status === 200) return setIsFavorite(false)
|
||||||
}
|
}
|
||||||
const response = await api.post(`/favorites/${props.id}`, {}, { headers: { Authorization: user.token } })
|
const response = await api.post(
|
||||||
|
`/favorites/${props.id}`,
|
||||||
|
{},
|
||||||
|
{ headers: { Authorization: user.token } }
|
||||||
|
)
|
||||||
if (response.status === 201) return setIsFavorite(true)
|
if (response.status === 201) return setIsFavorite(true)
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleError = (event) => {
|
const handleError = event => {
|
||||||
event.target.src = API_URL + '/images/functions/default.png'
|
event.target.src = API_URL + '/images/functions/default.png'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,18 +55,44 @@ const FunctionComponentTop = (props) => {
|
|||||||
<div className='container-fluid'>
|
<div className='container-fluid'>
|
||||||
<div className='row justify-content-center text-center'>
|
<div className='row justify-content-center text-center'>
|
||||||
<div className='FunctionComponent__top col-24'>
|
<div className='FunctionComponent__top col-24'>
|
||||||
|
{isAuth && (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
onClick={toggleFavorite}
|
||||||
|
{...(isFavorite ? { icon: faStar } : { icon: farStar })}
|
||||||
|
title={
|
||||||
|
isFavorite
|
||||||
|
? 'Retirer la fonction des favoris'
|
||||||
|
: 'Ajouter la fonction aux favoris'
|
||||||
|
}
|
||||||
|
className='FunctionComponent__star-favorite'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{(isAuth) &&
|
<img
|
||||||
<FontAwesomeIcon onClick={toggleFavorite} {...(isFavorite) ? { icon: faStar } : { icon: farStar }} title={(isFavorite) ? 'Retirer la fonction des favoris' : 'Ajouter la fonction aux favoris'} className='FunctionComponent__star-favorite' />}
|
onError={handleError}
|
||||||
|
className='FunctionComponent__image'
|
||||||
<img onError={handleError} className='FunctionComponent__image' src={API_URL + props.image} alt={props.title} />
|
src={API_URL + props.image}
|
||||||
<h1 className='FunctionComponent__title title-important'>{props.title}</h1>
|
alt={props.title}
|
||||||
|
/>
|
||||||
|
<h1 className='FunctionComponent__title title-important'>
|
||||||
|
{props.title}
|
||||||
|
</h1>
|
||||||
<p className='FunctionComponent__description'>{props.description}</p>
|
<p className='FunctionComponent__description'>{props.description}</p>
|
||||||
<div className='FunctionCard__info'>
|
<div className='FunctionCard__info'>
|
||||||
<Link href={`/functions?categoryId=${props.categorieId}`}>
|
<Link href={`/functions?categoryId=${props.categorieId}`}>
|
||||||
<a className='FunctionCard__category' style={{ backgroundColor: props.categorie.color, color: 'inherit' }}>{props.categorie.name}</a>
|
<a
|
||||||
|
className='FunctionCard__category'
|
||||||
|
style={{
|
||||||
|
backgroundColor: props.categorie.color,
|
||||||
|
color: 'inherit'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.categorie.name}
|
||||||
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<p className='FunctionCard__publication-date'>{date.format(new Date(props.createdAt), 'DD/MM/YYYY', true)}</p>
|
<p className='FunctionCard__publication-date'>
|
||||||
|
{date.format(new Date(props.createdAt), 'DD/MM/YYYY', true)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,7 +10,7 @@ import 'react-datepicker/dist/react-datepicker.css'
|
|||||||
|
|
||||||
registerLocale('fr', fr)
|
registerLocale('fr', fr)
|
||||||
|
|
||||||
const FunctionForm = (props) => {
|
const FunctionForm = props => {
|
||||||
const [inputState, setInputState] = useState({})
|
const [inputState, setInputState] = useState({})
|
||||||
const [message, setMessage] = useState('')
|
const [message, setMessage] = useState('')
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
@ -18,7 +18,7 @@ const FunctionForm = (props) => {
|
|||||||
// inputState par défaut
|
// inputState par défaut
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const inputStateNew = { ...inputState }
|
const inputStateNew = { ...inputState }
|
||||||
props.inputsArray.forEach((input) => {
|
props.inputsArray.forEach(input => {
|
||||||
if (input.type === 'select' && input.options.length > 0) {
|
if (input.type === 'select' && input.options.length > 0) {
|
||||||
inputStateNew[input.name] = input.options[0].value
|
inputStateNew[input.name] = input.options[0].value
|
||||||
}
|
}
|
||||||
@ -26,21 +26,22 @@ const FunctionForm = (props) => {
|
|||||||
setInputState(inputStateNew)
|
setInputState(inputStateNew)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleSubmit = (event) => {
|
const handleSubmit = event => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
api.post(`/functions/${props.slug}`, inputState)
|
api
|
||||||
.then((response) => {
|
.post(`/functions/${props.slug}`, inputState)
|
||||||
|
.then(response => {
|
||||||
setMessage(response.data.resultHTML)
|
setMessage(response.data.resultHTML)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
setMessage(error.response.data.message)
|
setMessage(error.response.data.message)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = (event) => {
|
const handleChange = event => {
|
||||||
const inputStateNew = { ...inputState }
|
const inputStateNew = { ...inputState }
|
||||||
inputStateNew[event.target.name] = event.target.value
|
inputStateNew[event.target.name] = event.target.value
|
||||||
setInputState(inputStateNew)
|
setInputState(inputStateNew)
|
||||||
@ -61,16 +62,35 @@ const FunctionForm = (props) => {
|
|||||||
case 'text':
|
case 'text':
|
||||||
return (
|
return (
|
||||||
<div key={index} className='form-group'>
|
<div key={index} className='form-group'>
|
||||||
<label className='form-label' htmlFor={input.name}>{input.label}</label>
|
<label className='form-label' htmlFor={input.name}>
|
||||||
<input onChange={handleChange} type='text' name={input.name} id={input.name} placeholder={input.placeholder} className='form-control' />
|
{input.label}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
onChange={handleChange}
|
||||||
|
type='text'
|
||||||
|
name={input.name}
|
||||||
|
id={input.name}
|
||||||
|
placeholder={input.placeholder}
|
||||||
|
className='form-control'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
case 'integer':
|
case 'integer':
|
||||||
case 'float':
|
case 'float':
|
||||||
return (
|
return (
|
||||||
<div key={index} className='form-group'>
|
<div key={index} className='form-group'>
|
||||||
<label className='form-label' htmlFor={input.name}>{input.label}</label>
|
<label className='form-label' htmlFor={input.name}>
|
||||||
<input onChange={handleChange} type='number' step={(input.type === 'integer') ? '1' : '0.01'} name={input.name} id={input.name} placeholder={input.placeholder} className='form-control' />
|
{input.label}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
onChange={handleChange}
|
||||||
|
type='number'
|
||||||
|
step={input.type === 'integer' ? '1' : '0.01'}
|
||||||
|
name={input.name}
|
||||||
|
id={input.name}
|
||||||
|
placeholder={input.placeholder}
|
||||||
|
className='form-control'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
case 'calendar':
|
case 'calendar':
|
||||||
@ -81,13 +101,14 @@ const FunctionForm = (props) => {
|
|||||||
document.body.appendChild(newScript)
|
document.body.appendChild(newScript)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const DatePicker = dynamic(
|
const DatePicker = dynamic(() => import('react-datepicker'), {
|
||||||
() => import('react-datepicker'),
|
ssr: false
|
||||||
{ ssr: false }
|
})
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
<div key={index} className='form-group'>
|
<div key={index} className='form-group'>
|
||||||
<label className='form-label' htmlFor={input.name}>{input.label}</label>
|
<label className='form-label' htmlFor={input.name}>
|
||||||
|
{input.label}
|
||||||
|
</label>
|
||||||
<br />
|
<br />
|
||||||
<DatePicker
|
<DatePicker
|
||||||
selected={(() => {
|
selected={(() => {
|
||||||
@ -97,7 +118,11 @@ const FunctionForm = (props) => {
|
|||||||
const year = dateArray[2]
|
const year = dateArray[2]
|
||||||
const month = dateArray[1]
|
const month = dateArray[1]
|
||||||
const day = dateArray[0]
|
const day = dateArray[0]
|
||||||
return new Date(year, parseInt(month) - 1, parseInt(day) + 1)
|
return new Date(
|
||||||
|
year,
|
||||||
|
parseInt(month) - 1,
|
||||||
|
parseInt(day) + 1
|
||||||
|
)
|
||||||
}
|
}
|
||||||
throw new Error('Not a valid date')
|
throw new Error('Not a valid date')
|
||||||
} catch {
|
} catch {
|
||||||
@ -108,9 +133,13 @@ const FunctionForm = (props) => {
|
|||||||
dateFormat='dd/MM/yyyy'
|
dateFormat='dd/MM/yyyy'
|
||||||
fixedHeight
|
fixedHeight
|
||||||
placeholderText={input.placeholder}
|
placeholderText={input.placeholder}
|
||||||
onChange={(dateObject) => {
|
onChange={dateObject => {
|
||||||
try {
|
try {
|
||||||
const formattedDate = date.format(dateObject, 'DD/MM/YYYY', true)
|
const formattedDate = date.format(
|
||||||
|
dateObject,
|
||||||
|
'DD/MM/YYYY',
|
||||||
|
true
|
||||||
|
)
|
||||||
handleChange({
|
handleChange({
|
||||||
target: {
|
target: {
|
||||||
name: input.name,
|
name: input.name,
|
||||||
@ -125,33 +154,39 @@ const FunctionForm = (props) => {
|
|||||||
case 'select':
|
case 'select':
|
||||||
return (
|
return (
|
||||||
<div key={index} className='form-group'>
|
<div key={index} className='form-group'>
|
||||||
<label className='form-label' htmlFor={input.name}>{input.label}</label>
|
<label className='form-label' htmlFor={input.name}>
|
||||||
<select onChange={handleChange} name={input.name} id={input.name} value={inputState[input.name] || input.options[0]} className='form-control'>
|
{input.label}
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
onChange={handleChange}
|
||||||
|
name={input.name}
|
||||||
|
id={input.name}
|
||||||
|
value={inputState[input.name] || input.options[0]}
|
||||||
|
className='form-control'
|
||||||
|
>
|
||||||
{input.options.map((option, optionIndex) => {
|
{input.options.map((option, optionIndex) => {
|
||||||
return (
|
return (
|
||||||
<option key={optionIndex} value={option.value}>{option.name}</option>
|
<option key={optionIndex} value={option.value}>
|
||||||
|
{option.name}
|
||||||
|
</option>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return (
|
return <p>Erreur, l'input n'est pas valide...</p>
|
||||||
<p>Erreur, l'input n'est pas valide...</p>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<div className='form-group text-center'>
|
<div className='form-group text-center'>
|
||||||
<button type='submit' className='btn btn-dark'>Envoyer</button>
|
<button type='submit' className='btn btn-dark'>
|
||||||
|
Envoyer
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div className='form-result text-center'>
|
<div className='form-result text-center'>
|
||||||
{
|
{isLoading ? <Loader /> : htmlParser(message)}
|
||||||
(isLoading)
|
|
||||||
? <Loader />
|
|
||||||
: htmlParser(message)
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -4,17 +4,29 @@ import HeadTag from '../HeadTag'
|
|||||||
import FunctionTabsTop from './FunctionTabsTop'
|
import FunctionTabsTop from './FunctionTabsTop'
|
||||||
import FunctionComponentTop from './FunctionComponentTop'
|
import FunctionComponentTop from './FunctionComponentTop'
|
||||||
|
|
||||||
const FunctionPage = (props) => {
|
const FunctionPage = props => {
|
||||||
const [slideIndex, setSlideIndex] = useState(0)
|
const [slideIndex, setSlideIndex] = useState(0)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeadTag title={props.title} description={props.description} image={API_URL + props.image} />
|
<HeadTag
|
||||||
|
title={props.title}
|
||||||
|
description={props.description}
|
||||||
|
image={API_URL + props.image}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className='container-fluid'>
|
<div className='container-fluid'>
|
||||||
<FunctionTabsTop slideIndex={slideIndex} setSlideIndex={setSlideIndex} tabNames={props.tabNames} />
|
<FunctionTabsTop
|
||||||
|
slideIndex={slideIndex}
|
||||||
|
setSlideIndex={setSlideIndex}
|
||||||
|
tabNames={props.tabNames}
|
||||||
|
/>
|
||||||
<FunctionComponentTop {...props} />
|
<FunctionComponentTop {...props} />
|
||||||
<props.FunctionTabManager {...props} slideIndex={slideIndex} setSlideIndex={setSlideIndex} />
|
<props.FunctionTabManager
|
||||||
|
{...props}
|
||||||
|
slideIndex={slideIndex}
|
||||||
|
setSlideIndex={setSlideIndex}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
border: 1px solid #0c0b0b38;
|
border: 1px solid #0c0b0b38;
|
||||||
border-bottom: 0px;
|
border-bottom: 0px;
|
||||||
border-top-left-radius: .375rem;
|
border-top-left-radius: 0.375rem;
|
||||||
border-top-right-radius: .375rem;
|
border-top-right-radius: 0.375rem;
|
||||||
display: block;
|
display: block;
|
||||||
padding: .5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
transition: .2s;
|
transition: 0.2s;
|
||||||
}
|
}
|
||||||
.FunctionTabs__nav-link-active {
|
.FunctionTabs__nav-link-active {
|
||||||
border-color: #d9e2ef #d9e2ef #fff;
|
border-color: #d9e2ef #d9e2ef #fff;
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import SwipeableViews from 'react-swipeable-views'
|
import SwipeableViews from 'react-swipeable-views'
|
||||||
import './FunctionTabs.css'
|
import './FunctionTabs.css'
|
||||||
|
|
||||||
const FunctionTabs = (props) => {
|
const FunctionTabs = props => {
|
||||||
return (
|
return (
|
||||||
<div className='container-fluid'>
|
<div className='container-fluid'>
|
||||||
<SwipeableViews onChangeIndex={(index) => props.setSlideIndex(index)} index={props.slideIndex}>
|
<SwipeableViews
|
||||||
|
onChangeIndex={index => props.setSlideIndex(index)}
|
||||||
|
index={props.slideIndex}
|
||||||
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</SwipeableViews>
|
</SwipeableViews>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const FunctionTabsTop = (props) => {
|
const FunctionTabsTop = props => {
|
||||||
return (
|
return (
|
||||||
<div className='container'>
|
<div className='container'>
|
||||||
<div className='row justify-content-center'>
|
<div className='row justify-content-center'>
|
||||||
@ -7,7 +7,11 @@ const FunctionTabsTop = (props) => {
|
|||||||
return (
|
return (
|
||||||
<li key={index} className='FunctionTabs__nav-item'>
|
<li key={index} className='FunctionTabs__nav-item'>
|
||||||
<a
|
<a
|
||||||
className={`FunctionTabs__nav-link ${(props.slideIndex === index) ? 'FunctionTabs__nav-link-active' : ''}`}
|
className={`FunctionTabs__nav-link ${
|
||||||
|
props.slideIndex === index
|
||||||
|
? 'FunctionTabs__nav-link-active'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
onClick={() => props.setSlideIndex(index)}
|
onClick={() => props.setSlideIndex(index)}
|
||||||
>
|
>
|
||||||
{tabName}
|
{tabName}
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
}
|
}
|
||||||
.Functions__form-control {
|
.Functions__form-control {
|
||||||
display: block;
|
display: block;
|
||||||
height: calc(1.5em + .75rem + 2px);
|
height: calc(1.5em + 0.75rem + 2px);
|
||||||
padding: .375rem .75rem;
|
padding: 0.375rem 0.75rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
@ -13,7 +13,7 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border: 1px solid #ced4da;
|
border: 1px solid #ced4da;
|
||||||
border-radius: .5em;
|
border-radius: 0.5em;
|
||||||
}
|
}
|
||||||
.Functions__search-container {
|
.Functions__search-container {
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
|
@ -7,15 +7,21 @@ import useAPI from '../../hooks/useAPI'
|
|||||||
import './FunctionsList.css'
|
import './FunctionsList.css'
|
||||||
|
|
||||||
let pageFunctions = 1
|
let pageFunctions = 1
|
||||||
const FunctionsList = (props) => {
|
const FunctionsList = props => {
|
||||||
const { categoryId } = useRouter().query
|
const { categoryId } = useRouter().query
|
||||||
|
|
||||||
// State de recherche et de catégories
|
// State de recherche et de catégories
|
||||||
const [, categories] = useAPI('/categories')
|
const [, categories] = useAPI('/categories')
|
||||||
const [inputSearch, setInputSearch] = useState({ search: '', selectedCategory: categoryId || '0' })
|
const [inputSearch, setInputSearch] = useState({
|
||||||
|
search: '',
|
||||||
|
selectedCategory: categoryId || '0'
|
||||||
|
})
|
||||||
|
|
||||||
// State pour afficher les fonctions
|
// State pour afficher les fonctions
|
||||||
const [functionsData, setFunctionsData] = useState({ hasMore: true, rows: [] })
|
const [functionsData, setFunctionsData] = useState({
|
||||||
|
hasMore: true,
|
||||||
|
rows: []
|
||||||
|
})
|
||||||
const [isLoadingFunctions, setLoadingFunctions] = useState(true)
|
const [isLoadingFunctions, setLoadingFunctions] = useState(true)
|
||||||
|
|
||||||
// Récupère la catégorie avec la query categoryId
|
// Récupère la catégorie avec la query categoryId
|
||||||
@ -28,19 +34,21 @@ const FunctionsList = (props) => {
|
|||||||
// Récupère les fonctions si la catégorie/recherche change
|
// Récupère les fonctions si la catégorie/recherche change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
pageFunctions = 1
|
pageFunctions = 1
|
||||||
getFunctionsData().then((data) => setFunctionsData(data))
|
getFunctionsData().then(data => setFunctionsData(data))
|
||||||
}, [inputSearch])
|
}, [inputSearch])
|
||||||
|
|
||||||
// Permet la pagination au scroll
|
// Permet la pagination au scroll
|
||||||
const observer = useRef()
|
const observer = useRef()
|
||||||
const lastFunctionCardRef = useCallback((node) => {
|
const lastFunctionCardRef = useCallback(
|
||||||
|
node => {
|
||||||
if (isLoadingFunctions) return
|
if (isLoadingFunctions) return
|
||||||
if (observer.current) observer.current.disconnect()
|
if (observer.current) observer.current.disconnect()
|
||||||
observer.current = new window.IntersectionObserver((entries) => {
|
observer.current = new window.IntersectionObserver(
|
||||||
|
entries => {
|
||||||
if (entries[0].isIntersecting && functionsData.hasMore) {
|
if (entries[0].isIntersecting && functionsData.hasMore) {
|
||||||
pageFunctions += 1
|
pageFunctions += 1
|
||||||
getFunctionsData().then((data) => {
|
getFunctionsData().then(data => {
|
||||||
setFunctionsData((oldData) => {
|
setFunctionsData(oldData => {
|
||||||
return {
|
return {
|
||||||
hasMore: data.hasMore,
|
hasMore: data.hasMore,
|
||||||
rows: [...oldData.rows, ...data.rows]
|
rows: [...oldData.rows, ...data.rows]
|
||||||
@ -48,23 +56,32 @@ const FunctionsList = (props) => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, { threshold: 1 })
|
},
|
||||||
|
{ threshold: 1 }
|
||||||
|
)
|
||||||
if (node) observer.current.observe(node)
|
if (node) observer.current.observe(node)
|
||||||
}, [isLoadingFunctions, functionsData.hasMore])
|
},
|
||||||
|
[isLoadingFunctions, functionsData.hasMore]
|
||||||
|
)
|
||||||
|
|
||||||
const getFunctionsData = async () => {
|
const getFunctionsData = async () => {
|
||||||
setLoadingFunctions(true)
|
setLoadingFunctions(true)
|
||||||
const URL = `${(props.isAdmin) ? '/admin/functions' : '/functions'}?page=${pageFunctions}&limit=10&categoryId=${inputSearch.selectedCategory}&search=${inputSearch.search}`
|
const URL = `${
|
||||||
|
props.isAdmin ? '/admin/functions' : '/functions'
|
||||||
|
}?page=${pageFunctions}&limit=10&categoryId=${
|
||||||
|
inputSearch.selectedCategory
|
||||||
|
}&search=${inputSearch.search}`
|
||||||
const { data } = await api.get(URL, {
|
const { data } = await api.get(URL, {
|
||||||
headers: {
|
headers: {
|
||||||
...(props.isAdmin && props.token != null) && { Authorization: props.token }
|
...(props.isAdmin &&
|
||||||
|
props.token != null && { Authorization: props.token })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setLoadingFunctions(false)
|
setLoadingFunctions(false)
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = (event) => {
|
const handleChange = event => {
|
||||||
const inputSearchNew = { ...inputSearch }
|
const inputSearchNew = { ...inputSearch }
|
||||||
inputSearchNew[event.target.name] = event.target.value
|
inputSearchNew[event.target.name] = event.target.value
|
||||||
setInputSearch(inputSearchNew)
|
setInputSearch(inputSearchNew)
|
||||||
@ -72,27 +89,58 @@ const FunctionsList = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='container text-center'>
|
<div className='container text-center'>
|
||||||
<div className='row justify-content-center'>
|
<div className='row justify-content-center'>{props.children}</div>
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='Functions__search-container row justify-content-center'>
|
<div className='Functions__search-container row justify-content-center'>
|
||||||
<select name='selectedCategory' value={inputSearch.selectedCategory} onChange={handleChange} className='Functions__select Functions__form-control'>
|
<select
|
||||||
|
name='selectedCategory'
|
||||||
|
value={inputSearch.selectedCategory}
|
||||||
|
onChange={handleChange}
|
||||||
|
className='Functions__select Functions__form-control'
|
||||||
|
>
|
||||||
<option value='0'>Toutes catégories</option>
|
<option value='0'>Toutes catégories</option>
|
||||||
{categories.map((category) => (
|
{categories.map(category => (
|
||||||
<option key={category.id} value={category.id} className='Functions__select-option' style={{ backgroundColor: category.color }}>{category.name}</option>
|
<option
|
||||||
|
key={category.id}
|
||||||
|
value={category.id}
|
||||||
|
className='Functions__select-option'
|
||||||
|
style={{ backgroundColor: category.color }}
|
||||||
|
>
|
||||||
|
{category.name}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<input value={inputSearch.search} onChange={handleChange} type='search' className='Functions__form-control Functions__search-input' name='search' id='search' placeholder='🔎 Rechercher...' />
|
<input
|
||||||
|
value={inputSearch.search}
|
||||||
|
onChange={handleChange}
|
||||||
|
type='search'
|
||||||
|
className='Functions__form-control Functions__search-input'
|
||||||
|
name='search'
|
||||||
|
id='search'
|
||||||
|
placeholder='🔎 Rechercher...'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='row justify-content-center'>
|
<div className='row justify-content-center'>
|
||||||
{functionsData.rows.map((currentFunction, index) => {
|
{functionsData.rows.map((currentFunction, index) => {
|
||||||
// Si c'est le dernier élément
|
// Si c'est le dernier élément
|
||||||
if (functionsData.rows.length === index + 1) {
|
if (functionsData.rows.length === index + 1) {
|
||||||
return <FunctionCard isAdmin={props.isAdmin} key={currentFunction.id} ref={lastFunctionCardRef} {...currentFunction} />
|
return (
|
||||||
|
<FunctionCard
|
||||||
|
isAdmin={props.isAdmin}
|
||||||
|
key={currentFunction.id}
|
||||||
|
ref={lastFunctionCardRef}
|
||||||
|
{...currentFunction}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return <FunctionCard isAdmin={props.isAdmin} key={currentFunction.id} {...currentFunction} />
|
return (
|
||||||
|
<FunctionCard
|
||||||
|
isAdmin={props.isAdmin}
|
||||||
|
key={currentFunction.id}
|
||||||
|
{...currentFunction}
|
||||||
|
/>
|
||||||
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{isLoadingFunctions && <Loader />}
|
{isLoadingFunctions && <Loader />}
|
||||||
|
@ -36,7 +36,8 @@ const HeadTag = ({ title, image, description }) => (
|
|||||||
|
|
||||||
HeadTag.defaultProps = {
|
HeadTag.defaultProps = {
|
||||||
title: 'FunctionProject',
|
title: 'FunctionProject',
|
||||||
description: "Apprenez la programmation grâce à l'apprentissage par projet alias fonction.",
|
description:
|
||||||
|
"Apprenez la programmation grâce à l'apprentissage par projet alias fonction.",
|
||||||
image: '/images/FunctionProject_icon_small.png'
|
image: '/images/FunctionProject_icon_small.png'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: .5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
|
|
||||||
border-bottom: var(--border-header-footer);
|
border-bottom: var(--border-header-footer);
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
@ -36,8 +36,8 @@
|
|||||||
/* Brand */
|
/* Brand */
|
||||||
.Header__brand-link {
|
.Header__brand-link {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-top: .3125rem;
|
padding-top: 0.3125rem;
|
||||||
padding-bottom: .3125rem;
|
padding-bottom: 0.3125rem;
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
@ -89,18 +89,21 @@
|
|||||||
}
|
}
|
||||||
.navbar-link {
|
.navbar-link {
|
||||||
display: block;
|
display: block;
|
||||||
padding: .5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Details Styling */
|
/* Details Styling */
|
||||||
.navbar-link:hover {
|
.navbar-link:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: rgba(255, 255, 255, .75);
|
color: rgba(255, 255, 255, 0.75);
|
||||||
}
|
}
|
||||||
.navbar-link, .navbar-link-active {
|
.navbar-link,
|
||||||
color: rgba(255, 255, 255, .5);
|
.navbar-link-active {
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
}
|
}
|
||||||
.navbar-link-active, .navbar-link-active:hover, .Header__brand-link {
|
.navbar-link-active,
|
||||||
|
.navbar-link-active:hover,
|
||||||
|
.Header__brand-link {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
.navbar-item {
|
.navbar-item {
|
||||||
@ -108,7 +111,7 @@
|
|||||||
}
|
}
|
||||||
.navbar-link {
|
.navbar-link {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding: .5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hamburger Icon */
|
/* Hamburger Icon */
|
||||||
@ -118,11 +121,13 @@
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 1px solid rgba(255, 255, 255, .1);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
border-radius: .25rem;
|
border-radius: 0.25rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.Header__hamburger > span, .Header__hamburger > span::before, .Header__hamburger > span::after {
|
.Header__hamburger > span,
|
||||||
|
.Header__hamburger > span::before,
|
||||||
|
.Header__hamburger > span::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 22px;
|
width: 22px;
|
||||||
height: 1.3px;
|
height: 1.3px;
|
||||||
@ -132,11 +137,12 @@
|
|||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
transition: background-color .3s ease-in-out;
|
transition: background-color 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
.Header__hamburger > span::before, .Header__hamburger > span::after {
|
.Header__hamburger > span::before,
|
||||||
|
.Header__hamburger > span::after {
|
||||||
content: '';
|
content: '';
|
||||||
transition: transform .3s ease-in-out;
|
transition: transform 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
.Header__hamburger > span::before {
|
.Header__hamburger > span::before {
|
||||||
transform: translateY(-8px);
|
transform: translateY(-8px);
|
||||||
|
@ -17,56 +17,74 @@ export default function Header () {
|
|||||||
return (
|
return (
|
||||||
<header className='Header'>
|
<header className='Header'>
|
||||||
<div className='container'>
|
<div className='container'>
|
||||||
|
|
||||||
{/* Brand */}
|
{/* Brand */}
|
||||||
<Link href='/'>
|
<Link href='/'>
|
||||||
<a className='Header__brand-link'>
|
<a className='Header__brand-link'>
|
||||||
<img id='brand-link__logo' src='/images/FunctionProject_brand-logo.png' alt='FunctionProject' />
|
<img
|
||||||
<img id='brand-link__logo-small-screen' src='/images/FunctionProject_icon_small.png' alt='FunctionProject' />
|
id='brand-link__logo'
|
||||||
|
src='/images/FunctionProject_brand-logo.png'
|
||||||
|
alt='FunctionProject'
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
id='brand-link__logo-small-screen'
|
||||||
|
src='/images/FunctionProject_icon_small.png'
|
||||||
|
alt='FunctionProject'
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Hamburger icon on Mobile */}
|
{/* Hamburger icon on Mobile */}
|
||||||
<div onClick={toggleNavbar} className={`Header__hamburger ${(isActive) ? 'Header__hamburger-active' : ''}`}>
|
<div
|
||||||
|
onClick={toggleNavbar}
|
||||||
|
className={`Header__hamburger ${
|
||||||
|
isActive ? 'Header__hamburger-active' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<span />
|
<span />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Navigation */}
|
{/* Navigation */}
|
||||||
<nav className='Header__navbar'>
|
<nav className='Header__navbar'>
|
||||||
<ul className={`navbar__list ${(isActive) ? 'navbar__list-active' : ''}`}>
|
<ul
|
||||||
|
className={`navbar__list ${isActive ? 'navbar__list-active' : ''}`}
|
||||||
|
>
|
||||||
<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}`}>
|
||||||
<a className={`navbar-link ${pathname === '/users/[name]' ? 'navbar-link-active' : null}`}>Mon Profil</a>
|
<a
|
||||||
|
className={`navbar-link ${
|
||||||
|
pathname === '/users/[name]'
|
||||||
|
? 'navbar-link-active'
|
||||||
|
: null
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Mon Profil
|
||||||
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className='navbar-item'>
|
<li className='navbar-item'>
|
||||||
<Link href='/'>
|
<Link href='/'>
|
||||||
<a onClick={logoutUser} className='navbar-link'>Se déconnecter</a>
|
<a onClick={logoutUser} className='navbar-link'>
|
||||||
|
Se déconnecter
|
||||||
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
)
|
)}
|
||||||
}
|
{isAuth && user.isAdmin && (
|
||||||
{
|
|
||||||
(isAuth && user.isAdmin) &&
|
|
||||||
<NavigationLink name='Admin' path='/admin' />
|
<NavigationLink name='Admin' path='/admin' />
|
||||||
}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
@ -8,7 +8,11 @@ export default function NavigationLink (props) {
|
|||||||
return (
|
return (
|
||||||
<li className='navbar-item'>
|
<li className='navbar-item'>
|
||||||
<Link href={props.path}>
|
<Link href={props.path}>
|
||||||
<a className={`navbar-link ${pathname === props.path ? 'navbar-link-active' : null}`}>
|
<a
|
||||||
|
className={`navbar-link ${
|
||||||
|
pathname === props.path ? 'navbar-link-active' : null
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{props.name}
|
{props.name}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
const Loader = ({ width, height, speed }) => (
|
const Loader = ({ width, height, speed }) => (
|
||||||
<svg width={width} height={height} viewBox='0 0 100 100'>
|
<svg width={width} height={height} viewBox='0 0 100 100'>
|
||||||
<g transform='translate(50 50) rotate(0) scale(1 1) translate(-50 -50)'>
|
<g transform='translate(50 50) rotate(0) scale(1 1) translate(-50 -50)'>
|
||||||
<image style={{ transformOrigin: '50% 50%', animation: `${speed} linear 0s infinite normal forwards running Loader__spin` }} x='0' y='0' width='100' height='100' href='/images/FunctionProject_icon.png' />
|
<image
|
||||||
|
style={{
|
||||||
|
transformOrigin: '50% 50%',
|
||||||
|
animation: `${speed} linear 0s infinite normal forwards running Loader__spin`
|
||||||
|
}}
|
||||||
|
x='0'
|
||||||
|
y='0'
|
||||||
|
width='100'
|
||||||
|
height='100'
|
||||||
|
href='/images/FunctionProject_icon.png'
|
||||||
|
/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
const Modal = (props) => (
|
const Modal = props => (
|
||||||
<div className='Modal container-fluid'>
|
<div className='Modal container-fluid'>
|
||||||
<div className='Modal__content'>
|
<div className='Modal__content'>{props.children}</div>
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,7 +22,9 @@ function UserContextProvider (props) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isAuth) {
|
if (isAuth) {
|
||||||
setMessageLogin('<p class="form-error"><b>Erreur:</b> Vous devez être déconnecter avant de vous connecter.</p>')
|
setMessageLogin(
|
||||||
|
'<p class="form-error"><b>Erreur:</b> Vous devez être déconnecter avant de vous connecter.</p>'
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
setMessageLogin('')
|
setMessageLogin('')
|
||||||
}
|
}
|
||||||
@ -43,10 +45,14 @@ function UserContextProvider (props) {
|
|||||||
cookies.set('user', newUser, { path: '/', maxAge: newUser.expiresIn })
|
cookies.set('user', newUser, { path: '/', maxAge: newUser.expiresIn })
|
||||||
setUser(newUser)
|
setUser(newUser)
|
||||||
setIsAuth(true)
|
setIsAuth(true)
|
||||||
setMessageLogin('<p class="form-success"><b>Succès:</b> Connexion réussi!</p>')
|
setMessageLogin(
|
||||||
|
'<p class="form-success"><b>Succès:</b> Connexion réussi!</p>'
|
||||||
|
)
|
||||||
setLoginLoading(false)
|
setLoginLoading(false)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setMessageLogin(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`)
|
setMessageLogin(
|
||||||
|
`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`
|
||||||
|
)
|
||||||
setLoginLoading(false)
|
setLoginLoading(false)
|
||||||
setIsAuth(false)
|
setIsAuth(false)
|
||||||
setUser(null)
|
setUser(null)
|
||||||
@ -54,7 +60,17 @@ function UserContextProvider (props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UserContext.Provider value={{ user, loginUser, logoutUser, loginLoading, messageLogin, isAuth, setMessageLogin }}>
|
<UserContext.Provider
|
||||||
|
value={{
|
||||||
|
user,
|
||||||
|
loginUser,
|
||||||
|
logoutUser,
|
||||||
|
loginLoading,
|
||||||
|
messageLogin,
|
||||||
|
isAuth,
|
||||||
|
setMessageLogin
|
||||||
|
}}
|
||||||
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</UserContext.Provider>
|
</UserContext.Provider>
|
||||||
)
|
)
|
||||||
|
@ -2,8 +2,8 @@ import { useContext } from 'react'
|
|||||||
import { UserContext } from '../contexts/UserContext'
|
import { UserContext } from '../contexts/UserContext'
|
||||||
import redirect from '../utils/redirect'
|
import redirect from '../utils/redirect'
|
||||||
|
|
||||||
const withoutAuth = (WrappedComponent) => {
|
const withoutAuth = WrappedComponent => {
|
||||||
const Component = (props) => {
|
const Component = props => {
|
||||||
const { isAuth, user } = useContext(UserContext)
|
const { isAuth, user } = useContext(UserContext)
|
||||||
|
|
||||||
if (isAuth) return redirect({}, `/users/${user.name}`)
|
if (isAuth) return redirect({}, `/users/${user.name}`)
|
||||||
|
@ -14,11 +14,11 @@ function useAPI (url, defaultData = [], method = 'get', options = {}) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api[method](url, options)
|
api[method](url, options)
|
||||||
.then((result) => {
|
.then(result => {
|
||||||
setData(result.data)
|
setData(result.data)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
setHasError(true)
|
setHasError(true)
|
||||||
console.error(error)
|
console.error(error)
|
||||||
})
|
})
|
||||||
|
550
website/package-lock.json
generated
550
website/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
/* GENERAL */
|
/* GENERAL */
|
||||||
:root {
|
:root {
|
||||||
--border-header-footer: 3px rgba(255, 255, 255, .7) solid;
|
--border-header-footer: 3px rgba(255, 255, 255, 0.7) solid;
|
||||||
--background-color: #181818;
|
--background-color: #181818;
|
||||||
--text-color: rgb(222, 222, 222);
|
--text-color: rgb(222, 222, 222);
|
||||||
--important: #ffd800;
|
--important: #ffd800;
|
||||||
@ -41,7 +41,7 @@ a:hover {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
div[aria-hidden="true"] > * {
|
div[aria-hidden='true'] > * {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.notyf__message {
|
.notyf__message {
|
||||||
@ -83,7 +83,8 @@ div[aria-hidden="true"] > * {
|
|||||||
color: var(--important);
|
color: var(--important);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
a, .important {
|
a,
|
||||||
|
.important {
|
||||||
color: var(--important);
|
color: var(--important);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
@ -96,8 +97,8 @@ a, .important {
|
|||||||
.form-control {
|
.form-control {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(1.5em + .75rem + 2px);
|
height: calc(1.5em + 0.75rem + 2px);
|
||||||
padding: .375rem .75rem;
|
padding: 0.375rem 0.75rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
@ -105,17 +106,17 @@ a, .important {
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border: 1px solid #ced4da;
|
border: 1px solid #ced4da;
|
||||||
border-radius: .25rem;
|
border-radius: 0.25rem;
|
||||||
}
|
}
|
||||||
.form-label {
|
.form-label {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: .5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
.form-result {
|
.form-result {
|
||||||
margin: 30px;
|
margin: 30px;
|
||||||
}
|
}
|
||||||
.form-success {
|
.form-success {
|
||||||
color: #90EE90;
|
color: #90ee90;
|
||||||
}
|
}
|
||||||
.form-error {
|
.form-error {
|
||||||
color: #ff7f7f;
|
color: #ff7f7f;
|
||||||
@ -123,17 +124,18 @@ a, .important {
|
|||||||
.btn {
|
.btn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
padding: .375rem .75rem;
|
padding: 0.375rem 0.75rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
border-radius: .25rem;
|
border-radius: 0.25rem;
|
||||||
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
|
||||||
|
border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||||
}
|
}
|
||||||
.btn-lg {
|
.btn-lg {
|
||||||
padding: .5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
border-radius: .3rem;
|
border-radius: 0.3rem;
|
||||||
}
|
}
|
||||||
.btn-dark:hover {
|
.btn-dark:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@ -178,50 +180,56 @@ a, .important {
|
|||||||
left: -2.25rem;
|
left: -2.25rem;
|
||||||
width: 1.75rem;
|
width: 1.75rem;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
border-radius: .5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
.custom-control-label::before {
|
.custom-control-label::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: .25rem;
|
top: 0.25rem;
|
||||||
left: -1.5rem;
|
left: -1.5rem;
|
||||||
display: block;
|
display: block;
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
content: "";
|
content: '';
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: #adb5bd solid 1px;
|
border: #adb5bd solid 1px;
|
||||||
}
|
}
|
||||||
.custom-switch .custom-control-input:checked ~ .custom-control-label::after {
|
.custom-switch .custom-control-input:checked ~ .custom-control-label::after {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
-webkit-transform: translateX(.75rem);
|
-webkit-transform: translateX(0.75rem);
|
||||||
transform: translateX(.75rem);
|
transform: translateX(0.75rem);
|
||||||
}
|
}
|
||||||
.custom-switch .custom-control-label::after {
|
.custom-switch .custom-control-label::after {
|
||||||
top: calc(.25rem + 2px);
|
top: calc(0.25rem + 2px);
|
||||||
left: calc(-2.25rem + 2px);
|
left: calc(-2.25rem + 2px);
|
||||||
width: calc(1rem - 4px);
|
width: calc(1rem - 4px);
|
||||||
height: calc(1rem - 4px);
|
height: calc(1rem - 4px);
|
||||||
background-color: #adb5bd;
|
background-color: #adb5bd;
|
||||||
border-radius: .5rem;
|
border-radius: 0.5rem;
|
||||||
transition: background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;
|
transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,
|
||||||
transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;
|
||||||
transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;
|
transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out,
|
||||||
|
border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||||
|
transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out,
|
||||||
|
border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out,
|
||||||
|
-webkit-transform 0.15s ease-in-out;
|
||||||
}
|
}
|
||||||
.custom-control-label::after {
|
.custom-control-label::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: .25rem;
|
top: 0.25rem;
|
||||||
left: -1.5rem;
|
left: -1.5rem;
|
||||||
display: block;
|
display: block;
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
content: "";
|
content: '';
|
||||||
background: no-repeat 50%/50% 50%;
|
background: no-repeat 50%/50% 50%;
|
||||||
}
|
}
|
||||||
.table-column {
|
.table-column {
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
.table, th, td {
|
.table,
|
||||||
|
th,
|
||||||
|
td {
|
||||||
border: 1px solid var(--text-color);
|
border: 1px solid var(--text-color);
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
@ -62,11 +62,126 @@
|
|||||||
margin-left: -15px;
|
margin-left: -15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col-13, .col-14, .col-15, .col-16, .col-17, .col-18, .col-19, .col-20, .col-21, .col-22, .col-23, .col-24,
|
.col-1,
|
||||||
.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-13, .col-sm-14, .col-sm-15, .col-sm-16, .col-sm-17, .col-sm-18, .col-sm-19, .col-sm-20, .col-sm-21, .col-sm-22, .col-sm-23, .col-sm-24,
|
.col-2,
|
||||||
.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md-13, .col-md-14, .col-md-15, .col-md-16, .col-md-17, .col-md-18, .col-md-19, .col-md-20, .col-md-21, .col-md-22, .col-md-23, .col-md-24,
|
.col-3,
|
||||||
.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-13, .col-lg-14, .col-lg-15, .col-lg-16, .col-lg-17, .col-lg-18, .col-lg-19, .col-lg-20, .col-lg-21, .col-lg-22, .col-lg-23, .col-lg-24,
|
.col-4,
|
||||||
.col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl-13, .col-xl-14, .col-xl-15, .col-xl-16, .col-xl-17, .col-xl-18, .col-xl-19, .col-xl-20, .col-xl-21, .col-xl-22, .col-xl-23, .col-xl-24 {
|
.col-5,
|
||||||
|
.col-6,
|
||||||
|
.col-7,
|
||||||
|
.col-8,
|
||||||
|
.col-9,
|
||||||
|
.col-10,
|
||||||
|
.col-11,
|
||||||
|
.col-12,
|
||||||
|
.col-13,
|
||||||
|
.col-14,
|
||||||
|
.col-15,
|
||||||
|
.col-16,
|
||||||
|
.col-17,
|
||||||
|
.col-18,
|
||||||
|
.col-19,
|
||||||
|
.col-20,
|
||||||
|
.col-21,
|
||||||
|
.col-22,
|
||||||
|
.col-23,
|
||||||
|
.col-24,
|
||||||
|
.col-sm-1,
|
||||||
|
.col-sm-2,
|
||||||
|
.col-sm-3,
|
||||||
|
.col-sm-4,
|
||||||
|
.col-sm-5,
|
||||||
|
.col-sm-6,
|
||||||
|
.col-sm-7,
|
||||||
|
.col-sm-8,
|
||||||
|
.col-sm-9,
|
||||||
|
.col-sm-10,
|
||||||
|
.col-sm-11,
|
||||||
|
.col-sm-12,
|
||||||
|
.col-sm-13,
|
||||||
|
.col-sm-14,
|
||||||
|
.col-sm-15,
|
||||||
|
.col-sm-16,
|
||||||
|
.col-sm-17,
|
||||||
|
.col-sm-18,
|
||||||
|
.col-sm-19,
|
||||||
|
.col-sm-20,
|
||||||
|
.col-sm-21,
|
||||||
|
.col-sm-22,
|
||||||
|
.col-sm-23,
|
||||||
|
.col-sm-24,
|
||||||
|
.col-md-1,
|
||||||
|
.col-md-2,
|
||||||
|
.col-md-3,
|
||||||
|
.col-md-4,
|
||||||
|
.col-md-5,
|
||||||
|
.col-md-6,
|
||||||
|
.col-md-7,
|
||||||
|
.col-md-8,
|
||||||
|
.col-md-9,
|
||||||
|
.col-md-10,
|
||||||
|
.col-md-11,
|
||||||
|
.col-md-12,
|
||||||
|
.col-md-13,
|
||||||
|
.col-md-14,
|
||||||
|
.col-md-15,
|
||||||
|
.col-md-16,
|
||||||
|
.col-md-17,
|
||||||
|
.col-md-18,
|
||||||
|
.col-md-19,
|
||||||
|
.col-md-20,
|
||||||
|
.col-md-21,
|
||||||
|
.col-md-22,
|
||||||
|
.col-md-23,
|
||||||
|
.col-md-24,
|
||||||
|
.col-lg-1,
|
||||||
|
.col-lg-2,
|
||||||
|
.col-lg-3,
|
||||||
|
.col-lg-4,
|
||||||
|
.col-lg-5,
|
||||||
|
.col-lg-6,
|
||||||
|
.col-lg-7,
|
||||||
|
.col-lg-8,
|
||||||
|
.col-lg-9,
|
||||||
|
.col-lg-10,
|
||||||
|
.col-lg-11,
|
||||||
|
.col-lg-12,
|
||||||
|
.col-lg-13,
|
||||||
|
.col-lg-14,
|
||||||
|
.col-lg-15,
|
||||||
|
.col-lg-16,
|
||||||
|
.col-lg-17,
|
||||||
|
.col-lg-18,
|
||||||
|
.col-lg-19,
|
||||||
|
.col-lg-20,
|
||||||
|
.col-lg-21,
|
||||||
|
.col-lg-22,
|
||||||
|
.col-lg-23,
|
||||||
|
.col-lg-24,
|
||||||
|
.col-xl-1,
|
||||||
|
.col-xl-2,
|
||||||
|
.col-xl-3,
|
||||||
|
.col-xl-4,
|
||||||
|
.col-xl-5,
|
||||||
|
.col-xl-6,
|
||||||
|
.col-xl-7,
|
||||||
|
.col-xl-8,
|
||||||
|
.col-xl-9,
|
||||||
|
.col-xl-10,
|
||||||
|
.col-xl-11,
|
||||||
|
.col-xl-12,
|
||||||
|
.col-xl-13,
|
||||||
|
.col-xl-14,
|
||||||
|
.col-xl-15,
|
||||||
|
.col-xl-16,
|
||||||
|
.col-xl-17,
|
||||||
|
.col-xl-18,
|
||||||
|
.col-xl-19,
|
||||||
|
.col-xl-20,
|
||||||
|
.col-xl-21,
|
||||||
|
.col-xl-22,
|
||||||
|
.col-xl-23,
|
||||||
|
.col-xl-24 {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
|
36
website/public/css/normalize.css
vendored
36
website/public/css/normalize.css
vendored
@ -174,7 +174,8 @@ textarea {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
button,
|
button,
|
||||||
input { /* 1 */
|
input {
|
||||||
|
/* 1 */
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +185,8 @@ input { /* 1 */
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
button,
|
button,
|
||||||
select { /* 1 */
|
select {
|
||||||
|
/* 1 */
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,9 +195,9 @@ select { /* 1 */
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
button,
|
button,
|
||||||
[type="button"],
|
[type='button'],
|
||||||
[type="reset"],
|
[type='reset'],
|
||||||
[type="submit"] {
|
[type='submit'] {
|
||||||
-webkit-appearance: button;
|
-webkit-appearance: button;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,9 +206,9 @@ button,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
button::-moz-focus-inner,
|
button::-moz-focus-inner,
|
||||||
[type="button"]::-moz-focus-inner,
|
[type='button']::-moz-focus-inner,
|
||||||
[type="reset"]::-moz-focus-inner,
|
[type='reset']::-moz-focus-inner,
|
||||||
[type="submit"]::-moz-focus-inner {
|
[type='submit']::-moz-focus-inner {
|
||||||
border-style: none;
|
border-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
@ -216,9 +218,9 @@ button::-moz-focus-inner,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
button:-moz-focusring,
|
button:-moz-focusring,
|
||||||
[type="button"]:-moz-focusring,
|
[type='button']:-moz-focusring,
|
||||||
[type="reset"]:-moz-focusring,
|
[type='reset']:-moz-focusring,
|
||||||
[type="submit"]:-moz-focusring {
|
[type='submit']:-moz-focusring {
|
||||||
outline: 1px dotted ButtonText;
|
outline: 1px dotted ButtonText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,8 +269,8 @@ textarea {
|
|||||||
* 2. Remove the padding in IE 10.
|
* 2. Remove the padding in IE 10.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[type="checkbox"],
|
[type='checkbox'],
|
||||||
[type="radio"] {
|
[type='radio'] {
|
||||||
box-sizing: border-box; /* 1 */
|
box-sizing: border-box; /* 1 */
|
||||||
padding: 0; /* 2 */
|
padding: 0; /* 2 */
|
||||||
}
|
}
|
||||||
@ -277,8 +279,8 @@ textarea {
|
|||||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[type="number"]::-webkit-inner-spin-button,
|
[type='number']::-webkit-inner-spin-button,
|
||||||
[type="number"]::-webkit-outer-spin-button {
|
[type='number']::-webkit-outer-spin-button {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +289,7 @@ textarea {
|
|||||||
* 2. Correct the outline style in Safari.
|
* 2. Correct the outline style in Safari.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[type="search"] {
|
[type='search'] {
|
||||||
-webkit-appearance: textfield; /* 1 */
|
-webkit-appearance: textfield; /* 1 */
|
||||||
outline-offset: -2px; /* 2 */
|
outline-offset: -2px; /* 2 */
|
||||||
}
|
}
|
||||||
@ -296,7 +298,7 @@ textarea {
|
|||||||
* Remove the inner padding in Chrome and Safari on macOS.
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[type="search"]::-webkit-search-decoration {
|
[type='search']::-webkit-search-decoration {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
@ -27,7 +27,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
.Chronometer__container {
|
.Chronometer__container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
.ManageToDo__container {
|
.ManageToDo__container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
margin: 40px 40px;
|
margin: 40px 40px;
|
||||||
|
@ -10,6 +10,6 @@
|
|||||||
.Home__image-width {
|
.Home__image-width {
|
||||||
width: 13em;
|
width: 13em;
|
||||||
}
|
}
|
||||||
div[aria-hidden="false"] {
|
div[aria-hidden='false'] {
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
}
|
}
|
@ -9,7 +9,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
}
|
}
|
||||||
.Register-Login__row {
|
.Register-Login__row {
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ app.prepare().then(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
/* Server */
|
/* Server */
|
||||||
server.listen(PORT, (error) => {
|
server.listen(PORT, error => {
|
||||||
if (error) throw error
|
if (error) throw error
|
||||||
console.log(`> Ready on http://localhost:${PORT}`)
|
console.log(`> Ready on http://localhost:${PORT}`)
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user