🎨 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'))
|
||||
|
||||
/* 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) => {
|
||||
console.log(error)
|
||||
const { statusCode, message } = error
|
||||
@ -76,8 +78,11 @@ Tasks.belongsTo(Users, { constraints: false })
|
||||
|
||||
/* Server */
|
||||
// sequelize.sync({ force: true })
|
||||
sequelize.sync()
|
||||
sequelize
|
||||
.sync()
|
||||
.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>
|
||||
<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;">
|
||||
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>
|
||||
<p style="margin: 0 0 12px 0;">
|
||||
<a style="color: #ffd800;" href="${frontendLink}/functions/randomQuote">Lien vers la fonction randomQuote de FunctionProject.</a>
|
||||
</p>
|
||||
${(!isValid) ? `
|
||||
${
|
||||
!isValid
|
||||
? `
|
||||
<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>.
|
||||
</p>
|
||||
` : ''}
|
||||
`
|
||||
: ''
|
||||
}
|
||||
<div>
|
||||
<p style="padding:0 0 10px 0">
|
||||
La citation en question : <br/>
|
||||
|
@ -16,14 +16,24 @@ function armstrongNumber (number) {
|
||||
let resultString = ''
|
||||
for (let index = 0; index < numberStringLength; index++) {
|
||||
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 isArmstrongNumber = (result === number)
|
||||
const isArmstrongNumber = result === number
|
||||
return {
|
||||
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
|
||||
|
||||
// S'il n'y a pas les champs obligatoire
|
||||
if (!(number)) {
|
||||
if (!number) {
|
||||
return errorHandling(next, requiredFields)
|
||||
}
|
||||
|
||||
// Si ce n'est pas un nombre
|
||||
number = parseInt(number)
|
||||
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))
|
||||
|
@ -2,7 +2,10 @@ const errorHandling = require('../../utils/errorHandling')
|
||||
const moment = require('moment')
|
||||
const { requiredFields } = require('../../config/errors')
|
||||
|
||||
function calculateAge (currentDate, { birthDateDay, birthDateMonth, birthDateYear }) {
|
||||
function calculateAge (
|
||||
currentDate,
|
||||
{ birthDateDay, birthDateMonth, birthDateYear }
|
||||
) {
|
||||
const day = currentDate.getDate()
|
||||
const month = currentDate.getMonth()
|
||||
const currentDateMoment = moment([currentDate.getFullYear(), month, day])
|
||||
@ -15,7 +18,7 @@ function calculateAge (currentDate, { birthDateDay, birthDateMonth, birthDateYea
|
||||
birthDateMoment.add(ageMonths, 'months')
|
||||
const ageDays = currentDateMoment.diff(birthDateMoment, 'days')
|
||||
|
||||
const isBirthday = (birthDateDay === day && birthDateMonth === month)
|
||||
const isBirthday = birthDateDay === day && birthDateMonth === month
|
||||
return { ageYears, ageMonths, ageDays, isBirthday }
|
||||
}
|
||||
|
||||
@ -24,20 +27,27 @@ module.exports = ({ res, next }, argsObject) => {
|
||||
const { birthDate } = argsObject
|
||||
|
||||
// S'il n'y a pas les champs obligatoire
|
||||
if (!(birthDate)) {
|
||||
if (!birthDate) {
|
||||
return errorHandling(next, requiredFields)
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
// Si ce n'est pas une date valide
|
||||
const currentDate = new Date()
|
||||
const birthDateObject = new Date(birthDateYear, birthDateMonth, birthDateDay)
|
||||
const result = calculateAge(currentDate, { birthDateYear, birthDateMonth, birthDateDay })
|
||||
if ((currentDate < birthDateObject) || isNaN(result.ageYears)) {
|
||||
return errorHandling(next, { message: 'Veuillez rentré une date valide...', statusCode: 400 })
|
||||
const result = calculateAge(currentDate, {
|
||||
birthDateYear,
|
||||
birthDateMonth,
|
||||
birthDateDay
|
||||
})
|
||||
if (currentDate < birthDateObject || isNaN(result.ageYears)) {
|
||||
return errorHandling(next, {
|
||||
message: 'Veuillez rentré une date valide...',
|
||||
statusCode: 400
|
||||
})
|
||||
}
|
||||
|
||||
let resultHTML
|
||||
|
@ -15,23 +15,39 @@ module.exports = ({ res, next }, argsObject) => {
|
||||
// Si ce n'est pas un nombre
|
||||
number = parseFloat(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}`)
|
||||
.then((response) => {
|
||||
axios
|
||||
.get(`https://api.exchangeratesapi.io/latest?base=${baseCurrency}`)
|
||||
.then(response => {
|
||||
const rate = response.data.rates[finalCurrency]
|
||||
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 dateObject = new Date(response.data.date)
|
||||
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 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 })
|
||||
})
|
||||
.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 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.
|
||||
@ -21,7 +47,11 @@ function convertDistance (firstValue, unitFirstValue, unitFinalValue) {
|
||||
const result = firstValue * Math.pow(10, difference)
|
||||
return {
|
||||
result,
|
||||
resultHTML: `<p>${formatNumberResult(firstValue)} ${unitFirstValue} = ${formatNumberResult(result)} ${unitFinalValue}</p>`
|
||||
resultHTML: `<p>${formatNumberResult(
|
||||
firstValue
|
||||
)} ${unitFirstValue} = ${formatNumberResult(
|
||||
result
|
||||
)} ${unitFinalValue}</p>`
|
||||
}
|
||||
}
|
||||
return false
|
||||
@ -39,7 +69,10 @@ module.exports = ({ res, next }, argsObject) => {
|
||||
// Si ce n'est pas un nombre
|
||||
number = parseFloat(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)
|
||||
|
@ -73,7 +73,9 @@ function binaryToHexadecimal (value) {
|
||||
if (isNaN(value)) {
|
||||
return false
|
||||
} else {
|
||||
return parseInt(value).toString(16).toUpperCase()
|
||||
return parseInt(value)
|
||||
.toString(16)
|
||||
.toUpperCase()
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,10 +142,15 @@ function numberUnicodeToText (string) {
|
||||
function textToBinary (s) {
|
||||
try {
|
||||
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++) {
|
||||
chr = s.charCodeAt(i).toString(2)
|
||||
while (chr.length % 8 !== 0) { chr = '0' + chr }
|
||||
while (chr.length % 8 !== 0) {
|
||||
chr = '0' + chr
|
||||
}
|
||||
out += chr
|
||||
}
|
||||
return out.replace(/(\d{8})/g, '$1 ').replace(/(^\s+|\s+$)/, '')
|
||||
@ -161,10 +168,13 @@ function textToBinary (s) {
|
||||
function binaryToText (s) {
|
||||
try {
|
||||
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) {
|
||||
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)
|
||||
} catch (error) {
|
||||
@ -181,10 +191,13 @@ function binaryToText (s) {
|
||||
function textToHexadecimal (s) {
|
||||
try {
|
||||
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++) {
|
||||
chr = s.charCodeAt(i).toString(16)
|
||||
out += (chr.length % 2 === 0) ? chr : '0' + chr
|
||||
out += chr.length % 2 === 0 ? chr : '0' + chr
|
||||
out += ' '
|
||||
}
|
||||
return out.toUpperCase()
|
||||
@ -209,7 +222,20 @@ function hexadecimalToText (s) {
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
return convertEncoding[option](value)
|
||||
}
|
||||
@ -225,7 +251,10 @@ module.exports = ({ res, next }, argsObject) => {
|
||||
// Si la fonction n'existe pas
|
||||
// eslint-disable-next-line
|
||||
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)
|
||||
|
@ -51,7 +51,7 @@ function convertArabicToRoman (nombre) {
|
||||
*/
|
||||
function convertRomanToArabic (string) {
|
||||
let result = 0
|
||||
correspondancesRomainArabe.forEach((correspondance) => {
|
||||
correspondancesRomainArabe.forEach(correspondance => {
|
||||
while (string.indexOf(correspondance[1]) === 0) {
|
||||
// Ajout de la valeur décimale au résultat
|
||||
result += correspondance[0]
|
||||
@ -68,7 +68,7 @@ function convertRomanToArabic (string) {
|
||||
/* OUTPUTS */
|
||||
const convertRomanToArabicOutput = ({ res, next }, number) => {
|
||||
// S'il n'y a pas les champs obligatoire
|
||||
if (!(number)) {
|
||||
if (!number) {
|
||||
return errorHandling(next, requiredFields)
|
||||
}
|
||||
|
||||
@ -80,26 +80,44 @@ const convertRomanToArabicOutput = ({ res, next }, number) => {
|
||||
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) => {
|
||||
// S'il n'y a pas les champs obligatoire
|
||||
if (!(number)) {
|
||||
if (!number) {
|
||||
return errorHandling(next, requiredFields)
|
||||
}
|
||||
|
||||
// Si ce n'est pas un nombre
|
||||
number = parseInt(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)
|
||||
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 }) {
|
||||
return convertRomanArabicObject[option]({ res, next }, value)
|
||||
}
|
||||
@ -115,7 +133,10 @@ module.exports = ({ res, next }, argsObject) => {
|
||||
// Si la fonction n'existe pas
|
||||
// eslint-disable-next-line
|
||||
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 })
|
||||
|
@ -12,15 +12,17 @@ const formatNumberResult = require('../secondary/formatNumberResult')
|
||||
function convertTemperature (degree, unit) {
|
||||
let temperatureValue = 0
|
||||
if (unit === '°C') {
|
||||
temperatureValue = (degree - 32) * 5 / 9
|
||||
temperatureValue = ((degree - 32) * 5) / 9
|
||||
} else if (unit === '°F') {
|
||||
temperatureValue = ((degree * 9 / 5) + 32)
|
||||
temperatureValue = (degree * 9) / 5 + 32
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return {
|
||||
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
|
||||
degree = parseFloat(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)
|
||||
|
@ -20,27 +20,36 @@ module.exports = ({ res, next }, argsObject) => {
|
||||
let { counter } = argsObject
|
||||
|
||||
// S'il n'y a pas les champs obligatoire
|
||||
if (!(counter)) {
|
||||
if (!counter) {
|
||||
return errorHandling(next, requiredFields)
|
||||
}
|
||||
|
||||
// Si ce n'est pas un nombre
|
||||
counter = parseInt(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
|
||||
const LIMIT_COUNTER = 51
|
||||
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 resultFormatted = result.map((number) => formatNumberResult(number))
|
||||
const resultFormatted = result.map(number => formatNumberResult(number))
|
||||
return res.status(200).json({
|
||||
result,
|
||||
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 result = ''
|
||||
|
||||
arrayString.forEach((element) => {
|
||||
arrayString.forEach(element => {
|
||||
if (element.length > stringLength) {
|
||||
result = element
|
||||
stringLength = element.length
|
||||
@ -27,7 +27,7 @@ module.exports = ({ res, next }, argsObject) => {
|
||||
const { string } = argsObject
|
||||
|
||||
// S'il n'y a pas les champs obligatoire
|
||||
if (!(string)) {
|
||||
if (!string) {
|
||||
return errorHandling(next, requiredFields)
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,14 @@ function heapAlgorithm (string) {
|
||||
|
||||
for (let indexString = 0; indexString < string.length; 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)
|
||||
for (let indexPermutation = 0; indexPermutation < innerPermutations.length; indexPermutation++) {
|
||||
for (
|
||||
let indexPermutation = 0;
|
||||
indexPermutation < innerPermutations.length;
|
||||
indexPermutation++
|
||||
) {
|
||||
results.push(firstChar + innerPermutations[indexPermutation])
|
||||
}
|
||||
}
|
||||
@ -32,19 +37,26 @@ module.exports = ({ res, next }, argsObject) => {
|
||||
const { string } = argsObject
|
||||
|
||||
// S'il n'y a pas les champs obligatoire
|
||||
if (!(string)) {
|
||||
if (!string) {
|
||||
return errorHandling(next, requiredFields)
|
||||
}
|
||||
|
||||
// Si la chaîne de caractère dépasse LIMIT_CHARACTERS caractères
|
||||
const LIMIT_CHARACTERS = 7
|
||||
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)
|
||||
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/>`
|
||||
result.forEach((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/>`
|
||||
result.forEach(string => {
|
||||
resultHTML += string + '<br/>'
|
||||
})
|
||||
resultHTML += '</p>'
|
||||
|
@ -8,7 +8,10 @@ const { requiredFields } = require('../../config/errors')
|
||||
* @example reverseString('Hello') → 'olleH'
|
||||
*/
|
||||
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
|
||||
|
||||
// S'il n'y a pas les champs obligatoire
|
||||
if (!(string)) {
|
||||
if (!string) {
|
||||
return errorHandling(next, requiredFields)
|
||||
}
|
||||
|
||||
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()
|
||||
@ -43,6 +49,10 @@ module.exports = ({ res, next }, argsObject) => {
|
||||
return res.status(200).json({
|
||||
isPalindrome: isPalindromeResult,
|
||||
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
|
||||
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
|
||||
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
|
||||
@ -31,20 +38,33 @@ module.exports = async ({ res, next }, argsObject) => {
|
||||
const urlInDatabase = await shortLinks.findOne({ where: { url } })
|
||||
if (urlInDatabase) {
|
||||
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à
|
||||
const shortcutInDatabase = await shortLinks.findOne({ where: { shortcut: shortcutName } })
|
||||
const shortcutInDatabase = await shortLinks.findOne({
|
||||
where: { shortcut: shortcutName }
|
||||
})
|
||||
if (shortcutInDatabase) {
|
||||
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
|
||||
const result = await shortLinks.create({ url, shortcut: shortcutName })
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
|
@ -26,11 +26,21 @@ const randomNumberOutput = ({ res, next }, argsObject) => {
|
||||
min = parseInt(min)
|
||||
max = parseInt(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)
|
||||
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
|
||||
|
@ -8,9 +8,7 @@ module.exports = async ({ res, next }, _argsObject) => {
|
||||
try {
|
||||
const quote = await Quotes.findOne({
|
||||
order: sequelize.random(),
|
||||
include: [
|
||||
{ model: Users, attributes: ['name', 'logo'] }
|
||||
],
|
||||
include: [{ model: Users, attributes: ['name', 'logo'] }],
|
||||
attributes: {
|
||||
exclude: ['isValidated']
|
||||
},
|
||||
|
@ -23,15 +23,18 @@ function getRandomArrayElement (array) {
|
||||
|
||||
async function getAmazonProductList (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 { document } = (new JSDOM(data)).window
|
||||
const { data } = await axios.get(
|
||||
`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 productsList = []
|
||||
for (const indexProduct in amazonProductList) {
|
||||
try {
|
||||
const elementProduct = amazonProductList[indexProduct]
|
||||
const productImage = elementProduct.querySelector('.s-image')
|
||||
const originalPrice = elementProduct.querySelector('.a-price-whole').innerHTML
|
||||
const originalPrice = elementProduct.querySelector('.a-price-whole')
|
||||
.innerHTML
|
||||
productsList.push({
|
||||
name: productImage.alt,
|
||||
image: productImage.src,
|
||||
|
@ -29,28 +29,41 @@ module.exports = ({ res, next }, argsObject) => {
|
||||
const { numbersList } = argsObject
|
||||
|
||||
// S'il n'y a pas les champs obligatoire
|
||||
if (!(numbersList)) {
|
||||
if (!numbersList) {
|
||||
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
|
||||
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
|
||||
const LIMIT_ARRAY_LENGTH = 31
|
||||
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 resultFormatted = result.map((number) => formatNumberResult(number))
|
||||
const resultFormatted = result.map(number => formatNumberResult(number))
|
||||
return res.status(200).json({
|
||||
result,
|
||||
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
|
||||
|
||||
// S'il n'y a pas les champs obligatoire
|
||||
if (!(cityName)) {
|
||||
if (!cityName) {
|
||||
return errorHandling(next, requiredFields)
|
||||
}
|
||||
|
||||
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)
|
||||
queue.request(() => {
|
||||
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 showDateTimeValue = dateTimeUTC((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 })
|
||||
})
|
||||
.catch(() => errorHandling(next, { message: "La ville n'existe pas (dans l'API de openweathermap.org).", statusCode: 404 }))
|
||||
}, 'everyone', 'weatherRequest')
|
||||
queue.request(
|
||||
() => {
|
||||
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 showDateTimeValue = dateTimeUTC(
|
||||
(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 })
|
||||
})
|
||||
.catch(() =>
|
||||
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 minute = ('0' + timeNow.getMinutes()).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 = {
|
||||
year: year,
|
||||
month: month,
|
||||
|
@ -1,9 +1,14 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const { DATABASE } = require('../config/config')
|
||||
|
||||
const sequelize = new Sequelize(DATABASE.name, DATABASE.user, DATABASE.password, {
|
||||
dialect: 'mysql',
|
||||
host: DATABASE.host
|
||||
})
|
||||
const sequelize = new Sequelize(
|
||||
DATABASE.name,
|
||||
DATABASE.user,
|
||||
DATABASE.password,
|
||||
{
|
||||
dialect: 'mysql',
|
||||
host: DATABASE.host
|
||||
}
|
||||
)
|
||||
|
||||
module.exports = sequelize
|
||||
|
@ -12,7 +12,11 @@ const DEFAULT_OPTIONS = {
|
||||
* @param {*} Model Model Sequelize
|
||||
* @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 limit = helperQueryNumber(req.query.limit, 10)
|
||||
const offset = (page - 1) * limit
|
||||
@ -23,7 +27,7 @@ async function getPagesHelper ({ req, res, next }, Model, options = DEFAULT_OPTI
|
||||
...options
|
||||
})
|
||||
const { count, rows } = result
|
||||
const hasMore = (page * limit) < count
|
||||
const hasMore = page * limit < count
|
||||
return res.status(200).json({ totalItems: count, hasMore, rows })
|
||||
} catch (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 { 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.slug = slug
|
||||
resultFunction.description = description
|
||||
@ -32,23 +37,41 @@ const handleEditFunction = async (res, resultFunction, { title, slug, descriptio
|
||||
exports.getFunctions = async (req, res, next) => {
|
||||
const categoryId = helperQueryNumber(req.query.categoryId, 0)
|
||||
let search = req.query.search
|
||||
try { search = search.toLowerCase() } catch {};
|
||||
try {
|
||||
search = search.toLowerCase()
|
||||
} catch {}
|
||||
const options = {
|
||||
where: {
|
||||
// Trie par catégorie
|
||||
...(categoryId !== 0) && { categorieId: categoryId },
|
||||
...(categoryId !== 0 && { categorieId: categoryId }),
|
||||
// Recherche
|
||||
...(search != null) && {
|
||||
...(search != null && {
|
||||
[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}%`) },
|
||||
{ description: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('description')), 'LIKE', `%${search}%`) }
|
||||
{
|
||||
title: Sequelize.where(
|
||||
Sequelize.fn('LOWER', Sequelize.col('title')),
|
||||
'LIKE',
|
||||
`%${search}%`
|
||||
)
|
||||
},
|
||||
{
|
||||
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'] }
|
||||
],
|
||||
include: [{ model: Categories, attributes: ['name', 'color'] }],
|
||||
attributes: {
|
||||
exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline']
|
||||
},
|
||||
@ -61,18 +84,21 @@ exports.getFunctionBySlug = (req, res, next) => {
|
||||
const { slug } = req.params
|
||||
Functions.findOne({
|
||||
where: { slug },
|
||||
include: [
|
||||
{ model: Categories, attributes: ['name', 'color'] }
|
||||
]
|
||||
include: [{ model: Categories, attributes: ['name', 'color'] }]
|
||||
})
|
||||
.then((result) => {
|
||||
.then(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)
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
})
|
||||
@ -83,28 +109,49 @@ exports.postFunction = (req, res, next) => {
|
||||
const image = req.files.image
|
||||
const errors = validationResult(req)
|
||||
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) && (
|
||||
image.mimetype !== 'image/png' ||
|
||||
image.mimetype !== 'image/jpg' ||
|
||||
image.mimetype !== 'image/jpeg'
|
||||
)) {
|
||||
return errorHandling(next, { message: 'La fonction doit avoir une image valide.', statusCode: 400 })
|
||||
if (
|
||||
(!image || image.truncated) &&
|
||||
(image.mimetype !== 'image/png' ||
|
||||
image.mimetype !== 'image/jpg' ||
|
||||
image.mimetype !== 'image/jpeg')
|
||||
) {
|
||||
return errorHandling(next, {
|
||||
message: 'La fonction doit avoir une image valide.',
|
||||
statusCode: 400
|
||||
})
|
||||
}
|
||||
const splitedImageName = image.name.split('.')
|
||||
if (splitedImageName.length !== 2) return errorHandling(next, serverError)
|
||||
const imageName = slug + '.' + splitedImageName[1]
|
||||
image.mv(path.join(__dirname, '..', 'assets', 'images', 'functions') + '/' + imageName, async (error) => {
|
||||
if (error) return errorHandling(next, serverError)
|
||||
try {
|
||||
const result = await Functions.create({ title, slug, description, type, categorieId, image: `/images/functions/${imageName}` })
|
||||
return res.status(201).json({ message: 'La fonction a été correctement ajouté!', result })
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
image.mv(
|
||||
path.join(__dirname, '..', 'assets', 'images', 'functions') +
|
||||
'/' +
|
||||
imageName,
|
||||
async error => {
|
||||
if (error) return errorHandling(next, serverError)
|
||||
try {
|
||||
const result = await Functions.create({
|
||||
title,
|
||||
slug,
|
||||
description,
|
||||
type,
|
||||
categorieId,
|
||||
image: `/images/functions/${imageName}`
|
||||
})
|
||||
return res
|
||||
.status(201)
|
||||
.json({ message: 'La fonction a été correctement ajouté!', result })
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
exports.putFunction = async (req, res, next) => {
|
||||
@ -113,43 +160,74 @@ exports.putFunction = async (req, res, next) => {
|
||||
const image = req.files.image
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
||||
return errorHandling(next, {
|
||||
message: errors.array()[0].msg,
|
||||
statusCode: 400
|
||||
})
|
||||
}
|
||||
try {
|
||||
// Vérifie si la fonction existe
|
||||
const resultFunction = await Functions.findOne({ where: { id } })
|
||||
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à
|
||||
const FunctionSlug = await Functions.findOne({ where: { slug } })
|
||||
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
|
||||
if (image != null) {
|
||||
if (image.truncated && (
|
||||
image.mimetype !== 'image/png' ||
|
||||
image.mimetype !== 'image/jpg' ||
|
||||
image.mimetype !== 'image/jpeg'
|
||||
)) {
|
||||
return errorHandling(next, { message: 'La fonction doit avoir une image valide.', statusCode: 400 })
|
||||
if (
|
||||
image.truncated &&
|
||||
(image.mimetype !== 'image/png' ||
|
||||
image.mimetype !== 'image/jpg' ||
|
||||
image.mimetype !== 'image/jpeg')
|
||||
) {
|
||||
return errorHandling(next, {
|
||||
message: 'La fonction doit avoir une image valide.',
|
||||
statusCode: 400
|
||||
})
|
||||
}
|
||||
const splitedImageName = image.name.split('.')
|
||||
if (splitedImageName.length !== 2) return errorHandling(next, serverError)
|
||||
const imageName = slug + '.' + splitedImageName[1]
|
||||
// Supprime les anciennes images
|
||||
const functionPath = path.join(__dirname, '..', 'assets', 'images', 'functions')
|
||||
const functionPath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'assets',
|
||||
'images',
|
||||
'functions'
|
||||
)
|
||||
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)
|
||||
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 {
|
||||
return await handleEditFunction(res, resultFunction, { title, slug, description, type, categorieId, isOnline })
|
||||
return await handleEditFunction(res, resultFunction, {
|
||||
title,
|
||||
slug,
|
||||
description,
|
||||
type,
|
||||
categorieId,
|
||||
isOnline
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
@ -165,7 +243,10 @@ exports.putFunctionArticle = async (req, res, next) => {
|
||||
// Vérifie si la fonction existe
|
||||
const resultFunction = await Functions.findOne({ where: { id } })
|
||||
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
|
||||
const result = await resultFunction.save()
|
||||
@ -184,7 +265,10 @@ exports.putFunctionForm = async (req, res, next) => {
|
||||
// Vérifie si la fonction existe
|
||||
const resultFunction = await Functions.findOne({ where: { id } })
|
||||
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)
|
||||
const result = await resultFunction.save()
|
||||
@ -200,14 +284,19 @@ exports.deleteFunction = async (req, res, next) => {
|
||||
try {
|
||||
const result = await Functions.findOne({ where: { id } })
|
||||
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') {
|
||||
const filePath = path.join(__dirname, '..', 'assets', result.image)
|
||||
fs.unlinkSync(filePath) // supprime le fichier
|
||||
}
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -217,11 +306,15 @@ exports.deleteFunction = async (req, res, next) => {
|
||||
exports.postCategory = async (req, res, next) => {
|
||||
const { name, color } = req.body
|
||||
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 {
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -232,7 +325,9 @@ exports.putCategory = async (req, res, next) => {
|
||||
const { name, color } = req.body
|
||||
const { id } = req.params
|
||||
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 {
|
||||
const category = await Categories.findOne({ where: { id } })
|
||||
@ -242,7 +337,9 @@ exports.putCategory = async (req, res, next) => {
|
||||
category.name = name
|
||||
category.color = color
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -257,7 +354,9 @@ exports.deleteCategory = async (req, res, next) => {
|
||||
return errorHandling(next, { message: "La catégorie n'existe pas." })
|
||||
}
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -269,9 +368,7 @@ exports.getQuotes = async (req, res, next) => {
|
||||
where: {
|
||||
isValidated: 0
|
||||
},
|
||||
include: [
|
||||
{ model: Users, attributes: ['name', 'logo'] }
|
||||
],
|
||||
include: [{ model: Users, attributes: ['name', 'logo'] }],
|
||||
order: [['createdAt', 'DESC']]
|
||||
}
|
||||
return await getPagesHelper({ req, res, next }, Quotes, options)
|
||||
@ -282,19 +379,23 @@ exports.putQuote = async (req, res, next) => {
|
||||
const { isValid } = req.body
|
||||
try {
|
||||
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({
|
||||
where: {
|
||||
id,
|
||||
isValidated: 0
|
||||
},
|
||||
include: [
|
||||
{ model: Users, attributes: ['name', 'email'] }
|
||||
]
|
||||
include: [{ model: Users, attributes: ['name', 'email'] }]
|
||||
})
|
||||
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({
|
||||
@ -307,10 +408,14 @@ exports.putQuote = async (req, res, next) => {
|
||||
if (isValid) {
|
||||
quote.isValidated = true
|
||||
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 {
|
||||
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) {
|
||||
console.log(error)
|
||||
|
@ -4,10 +4,10 @@ const { serverError } = require('../assets/config/errors')
|
||||
|
||||
exports.getCategories = (_req, res, next) => {
|
||||
Categories.findAll()
|
||||
.then((result) => {
|
||||
.then(result => {
|
||||
res.status(200).json(result)
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
})
|
||||
|
@ -9,9 +9,7 @@ exports.getCommentsByFunctionId = async (req, res, next) => {
|
||||
const { functionId } = req.params
|
||||
const options = {
|
||||
where: { functionId },
|
||||
include: [
|
||||
{ model: Users, attributes: ['name', 'logo'] }
|
||||
],
|
||||
include: [{ model: Users, attributes: ['name', 'logo'] }],
|
||||
order: [['createdAt', 'DESC']]
|
||||
}
|
||||
return await getPagesHelper({ req, res, next }, Comments, options)
|
||||
@ -21,14 +19,26 @@ exports.postCommentsByFunctionId = async (req, res, next) => {
|
||||
const { functionId } = req.params
|
||||
const { message } = req.body
|
||||
try {
|
||||
const resultFunction = await Functions.findOne({ where: { id: functionId } })
|
||||
const resultFunction = await Functions.findOne({
|
||||
where: { id: functionId }
|
||||
})
|
||||
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) {
|
||||
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)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
@ -39,12 +49,19 @@ exports.postCommentsByFunctionId = async (req, res, next) => {
|
||||
exports.deleteCommentById = async (req, res, next) => {
|
||||
const { commentId } = req.params
|
||||
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) {
|
||||
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()
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -55,16 +72,26 @@ exports.putCommentsById = async (req, res, next) => {
|
||||
const { commentId } = req.params
|
||||
const { message } = req.body
|
||||
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 {
|
||||
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) {
|
||||
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
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
|
@ -27,9 +27,14 @@ exports.postFavoriteByFunctionId = async (req, res, next) => {
|
||||
const { functionId } = req.params
|
||||
const { userId } = req
|
||||
try {
|
||||
const resultFunction = await Functions.findOne({ where: { id: functionId } })
|
||||
const resultFunction = await Functions.findOne({
|
||||
where: { id: functionId }
|
||||
})
|
||||
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({
|
||||
where: {
|
||||
@ -41,7 +46,10 @@ exports.postFavoriteByFunctionId = async (req, res, next) => {
|
||||
await Favorites.create({ userId, functionId })
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -52,9 +60,14 @@ exports.deleteFavoriteByFunctionId = async (req, res, next) => {
|
||||
const { functionId } = req.params
|
||||
const { userId } = req
|
||||
try {
|
||||
const resultFunction = await Functions.findOne({ where: { id: functionId } })
|
||||
const resultFunction = await Functions.findOne({
|
||||
where: { id: functionId }
|
||||
})
|
||||
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({
|
||||
where: {
|
||||
@ -63,10 +76,15 @@ exports.deleteFavoriteByFunctionId = async (req, res, next) => {
|
||||
}
|
||||
})
|
||||
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()
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
|
@ -10,24 +10,42 @@ const Sequelize = require('sequelize')
|
||||
exports.getFunctions = async (req, res, next) => {
|
||||
const categoryId = helperQueryNumber(req.query.categoryId, 0)
|
||||
let { search } = req.query
|
||||
try { search = search.toLowerCase() } catch {};
|
||||
try {
|
||||
search = search.toLowerCase()
|
||||
} catch {}
|
||||
const options = {
|
||||
where: {
|
||||
isOnline: 1,
|
||||
// Trie par catégorie
|
||||
...(categoryId !== 0) && { categorieId: categoryId },
|
||||
...(categoryId !== 0 && { categorieId: categoryId }),
|
||||
// Recherche
|
||||
...(search != null) && {
|
||||
...(search != null && {
|
||||
[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}%`) },
|
||||
{ description: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('description')), 'LIKE', `%${search}%`) }
|
||||
{
|
||||
title: Sequelize.where(
|
||||
Sequelize.fn('LOWER', Sequelize.col('title')),
|
||||
'LIKE',
|
||||
`%${search}%`
|
||||
)
|
||||
},
|
||||
{
|
||||
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'] }
|
||||
],
|
||||
include: [{ model: Categories, attributes: ['name', 'color'] }],
|
||||
attributes: {
|
||||
exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline']
|
||||
},
|
||||
@ -43,18 +61,21 @@ exports.getFunctionBySlug = (req, res, next) => {
|
||||
attributes: {
|
||||
exclude: ['updatedAt', 'isOnline']
|
||||
},
|
||||
include: [
|
||||
{ model: Categories, attributes: ['name', 'color'] }
|
||||
]
|
||||
include: [{ model: Categories, attributes: ['name', 'color'] }]
|
||||
})
|
||||
.then((result) => {
|
||||
.then(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)
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
})
|
||||
@ -65,5 +86,8 @@ exports.executeFunctionBySlug = (req, res, next) => {
|
||||
if (functionOutput !== undefined) {
|
||||
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: {
|
||||
isValidated: 1
|
||||
},
|
||||
include: [
|
||||
{ model: Users, attributes: ['name', 'logo'] }
|
||||
],
|
||||
include: [{ model: Users, attributes: ['name', 'logo'] }],
|
||||
attributes: {
|
||||
exclude: ['isValidated']
|
||||
},
|
||||
@ -27,10 +25,15 @@ exports.postQuote = (req, res, next) => {
|
||||
return errorHandling(next, requiredFields)
|
||||
}
|
||||
Quotes.create({ quote, author, userId: req.userId })
|
||||
.then((_result) => {
|
||||
return res.status(200).json({ message: "La citation a bien été ajoutée, elle est en attente de confirmation d'un administrateur." })
|
||||
.then(_result => {
|
||||
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)
|
||||
return errorHandling(next, serverError)
|
||||
})
|
||||
|
@ -36,12 +36,20 @@ exports.putTask = async (req, res, next) => {
|
||||
const { isCompleted } = req.body
|
||||
try {
|
||||
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) {
|
||||
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
|
||||
const taskSaved = await taskResult.save()
|
||||
@ -55,12 +63,19 @@ exports.putTask = async (req, res, next) => {
|
||||
exports.deleteTask = async (req, res, next) => {
|
||||
const { id } = req.params
|
||||
try {
|
||||
const taskResult = await Tasks.findOne({ where: { id, userId: req.userId } })
|
||||
const taskResult = await Tasks.findOne({
|
||||
where: { id, userId: req.userId }
|
||||
})
|
||||
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()
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
|
@ -7,7 +7,13 @@ const uuid = require('uuid')
|
||||
const Sequelize = require('sequelize')
|
||||
const errorHandling = require('../assets/utils/errorHandling')
|
||||
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 { emailUserTemplate } = require('../assets/config/emails')
|
||||
const Users = require('../models/users')
|
||||
@ -19,7 +25,12 @@ const Quotes = require('../models/quotes')
|
||||
const deleteFilesNameStartWith = require('../assets/utils/deleteFilesNameStartWith')
|
||||
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 } })
|
||||
user.name = name
|
||||
if (user.email !== email) {
|
||||
@ -31,7 +42,12 @@ async function handleEditUser (res, { name, email, biography, isPublicEmail }, u
|
||||
from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`,
|
||||
to: 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) {
|
||||
@ -42,22 +58,48 @@ async function handleEditUser (res, { name, email, biography, isPublicEmail }, u
|
||||
user.logo = `/images/users/${logoName}`
|
||||
}
|
||||
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) => {
|
||||
let { search } = req.query
|
||||
try { search = search.toLowerCase() } catch {};
|
||||
try {
|
||||
search = search.toLowerCase()
|
||||
} catch {}
|
||||
const options = {
|
||||
where: {
|
||||
isConfirmed: true,
|
||||
// Recherche
|
||||
...(search != null) && {
|
||||
name: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), 'LIKE', `%${search}%`)
|
||||
}
|
||||
...(search != null && {
|
||||
name: Sequelize.where(
|
||||
Sequelize.fn('LOWER', Sequelize.col('name')),
|
||||
'LIKE',
|
||||
`%${search}%`
|
||||
)
|
||||
})
|
||||
},
|
||||
attributes: {
|
||||
exclude: ['updatedAt', 'isAdmin', 'isConfirmed', 'password', 'tempToken', 'tempExpirationToken', 'isPublicEmail', 'email']
|
||||
exclude: [
|
||||
'updatedAt',
|
||||
'isAdmin',
|
||||
'isConfirmed',
|
||||
'password',
|
||||
'tempToken',
|
||||
'tempExpirationToken',
|
||||
'isPublicEmail',
|
||||
'email'
|
||||
]
|
||||
},
|
||||
order: [['createdAt', 'DESC']]
|
||||
}
|
||||
@ -69,35 +111,60 @@ exports.putUser = async (req, res, next) => {
|
||||
const logo = req.files.logo
|
||||
const errors = validationResult(req)
|
||||
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 || logo.truncated) && (
|
||||
logo.mimetype !== 'image/png' ||
|
||||
logo.mimetype !== 'image/jpg' ||
|
||||
logo.mimetype !== 'image/jpeg' ||
|
||||
logo.mimetype !== 'image/gif'
|
||||
)) {
|
||||
return errorHandling(next, { message: 'Le profil doit avoir une image valide (PNG, JPG, GIF) et moins de 5mo.', statusCode: 400 })
|
||||
if (
|
||||
(!logo || logo.truncated) &&
|
||||
(logo.mimetype !== 'image/png' ||
|
||||
logo.mimetype !== 'image/jpg' ||
|
||||
logo.mimetype !== 'image/jpeg' ||
|
||||
logo.mimetype !== 'image/gif')
|
||||
) {
|
||||
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('.')
|
||||
if (splitedLogoName.length !== 2) return errorHandling(next, serverError)
|
||||
const logoName = name + req.userId + '.' + splitedLogoName[1]
|
||||
// Supprime les anciens logo
|
||||
try {
|
||||
deleteFilesNameStartWith(`${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)
|
||||
return await handleEditUser(res, { name, email, biography, isPublicEmail }, req.userId, logoName)
|
||||
})
|
||||
})
|
||||
deleteFilesNameStartWith(
|
||||
`${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)
|
||||
return await handleEditUser(
|
||||
res,
|
||||
{ name, email, biography, isPublicEmail },
|
||||
req.userId,
|
||||
logoName
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -109,7 +176,10 @@ exports.register = async (req, res, next) => {
|
||||
const { name, email, password } = req.body
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
||||
return errorHandling(next, {
|
||||
message: errors.array()[0].msg,
|
||||
statusCode: 400
|
||||
})
|
||||
}
|
||||
try {
|
||||
const hashedPassword = await bcrypt.hash(password, 12)
|
||||
@ -119,9 +189,19 @@ exports.register = async (req, res, next) => {
|
||||
from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`,
|
||||
to: email,
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -132,24 +212,55 @@ exports.login = async (req, res, next) => {
|
||||
const { email, password } = req.body
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
||||
return errorHandling(next, {
|
||||
message: errors.array()[0].msg,
|
||||
statusCode: 400
|
||||
})
|
||||
}
|
||||
try {
|
||||
const user = await Users.findOne({ where: { email } })
|
||||
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)
|
||||
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) {
|
||||
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({
|
||||
email: user.email, 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) })
|
||||
const token = jwt.sign(
|
||||
{
|
||||
email: user.email,
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -162,9 +273,14 @@ exports.confirmEmail = async (req, res, next) => {
|
||||
return errorHandling(next, generalError)
|
||||
}
|
||||
try {
|
||||
const user = await Users.findOne({ where: { tempToken, isConfirmed: false } })
|
||||
const user = await Users.findOne({
|
||||
where: { tempToken, isConfirmed: false }
|
||||
})
|
||||
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.isConfirmed = true
|
||||
@ -180,12 +296,19 @@ exports.resetPassword = async (req, res, next) => {
|
||||
const { email } = req.body
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
||||
return errorHandling(next, {
|
||||
message: errors.array()[0].msg,
|
||||
statusCode: 400
|
||||
})
|
||||
}
|
||||
try {
|
||||
const user = await Users.findOne({ where: { email, tempToken: null } })
|
||||
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()
|
||||
user.tempExpirationToken = Date.now() + 3600000 // 1 heure
|
||||
@ -195,9 +318,19 @@ exports.resetPassword = async (req, res, next) => {
|
||||
from: `"FunctionProject" <${EMAIL_INFO.auth.user}>`,
|
||||
to: email,
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -208,19 +341,27 @@ exports.newPassword = async (req, res, next) => {
|
||||
const { tempToken, password } = req.body
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return errorHandling(next, { message: errors.array()[0].msg, statusCode: 400 })
|
||||
return errorHandling(next, {
|
||||
message: errors.array()[0].msg,
|
||||
statusCode: 400
|
||||
})
|
||||
}
|
||||
try {
|
||||
const user = await Users.findOne({ where: { tempToken } })
|
||||
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)
|
||||
user.password = hashedPassword
|
||||
user.tempToken = null
|
||||
user.tempExpirationToken = null
|
||||
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) {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
@ -233,30 +374,51 @@ exports.getUserInfo = async (req, res, next) => {
|
||||
const user = await Users.findOne({
|
||||
where: { name, isConfirmed: true },
|
||||
attributes: {
|
||||
exclude: ['updatedAt', 'isAdmin', 'isConfirmed', 'password', 'tempToken', 'tempExpirationToken']
|
||||
exclude: [
|
||||
'updatedAt',
|
||||
'isAdmin',
|
||||
'isConfirmed',
|
||||
'password',
|
||||
'tempToken',
|
||||
'tempExpirationToken'
|
||||
]
|
||||
}
|
||||
})
|
||||
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({
|
||||
where: { userId: user.id },
|
||||
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']],
|
||||
limit: 5
|
||||
})
|
||||
const favoritesArray = favorites.map((favorite) => favorite.function)
|
||||
const favoritesArray = favorites.map(favorite => favorite.function)
|
||||
const comments = await Comments.findAll({
|
||||
where: { userId: user.id },
|
||||
include: [
|
||||
{ model: Functions, attributes: { exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline'] } }
|
||||
{
|
||||
model: Functions,
|
||||
attributes: {
|
||||
exclude: ['updatedAt', 'utilizationForm', 'article', 'isOnline']
|
||||
}
|
||||
}
|
||||
],
|
||||
order: [['createdAt', 'DESC']],
|
||||
limit: 5
|
||||
})
|
||||
const commentsArray = comments.map((commentObject) => {
|
||||
const commentsArray = comments.map(commentObject => {
|
||||
return {
|
||||
id: commentObject.id,
|
||||
message: commentObject.message,
|
||||
@ -274,7 +436,7 @@ exports.getUserInfo = async (req, res, next) => {
|
||||
})
|
||||
const userObject = {
|
||||
// Si Public Email
|
||||
...(user.isPublicEmail) && { email: user.email },
|
||||
...(user.isPublicEmail && { email: user.email }),
|
||||
isPublicEmail: user.isPublicEmail,
|
||||
name: user.name,
|
||||
biography: user.biography,
|
||||
|
@ -4,19 +4,28 @@ const Users = require('../models/users')
|
||||
|
||||
module.exports = (req, _res, next) => {
|
||||
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 } })
|
||||
.then((user) => {
|
||||
.then(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) {
|
||||
return errorHandling(next, { message: "Vous n'êtes pas administrateur.", statusCode: 403 })
|
||||
return errorHandling(next, {
|
||||
message: "Vous n'êtes pas administrateur.",
|
||||
statusCode: 403
|
||||
})
|
||||
}
|
||||
next()
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
return errorHandling(next, serverError)
|
||||
})
|
||||
|
@ -5,18 +5,27 @@ const { JWT_SECRET } = require('../assets/config/config')
|
||||
module.exports = (req, _res, next) => {
|
||||
const token = req.get('Authorization')
|
||||
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
|
||||
try {
|
||||
decodedToken = jwt.verify(token, JWT_SECRET)
|
||||
} 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) {
|
||||
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
|
||||
|
453
api/package-lock.json
generated
453
api/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "api",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -89,9 +89,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "13.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.2.tgz",
|
||||
"integrity": "sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg=="
|
||||
"version": "14.0.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz",
|
||||
"integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g=="
|
||||
},
|
||||
"abab": {
|
||||
"version": "2.0.3",
|
||||
@ -114,9 +114,9 @@
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz",
|
||||
"integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ=="
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
|
||||
"integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA=="
|
||||
},
|
||||
"acorn-globals": {
|
||||
"version": "6.0.0",
|
||||
@ -134,14 +134,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"acorn-walk": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz",
|
||||
"integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ=="
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
|
||||
"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA=="
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
|
||||
"integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
|
||||
"version": "6.12.3",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
|
||||
"integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@ -279,9 +279,9 @@
|
||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
|
||||
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
|
||||
"integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.19.2",
|
||||
@ -301,7 +301,6 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.2"
|
||||
}
|
||||
@ -345,6 +344,21 @@
|
||||
"qs": "6.7.0",
|
||||
"raw-body": "2.4.0",
|
||||
"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": {
|
||||
@ -739,9 +753,9 @@
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"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"
|
||||
},
|
||||
@ -849,11 +863,6 @@
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
@ -895,8 +904,7 @@
|
||||
"dotenv": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
|
||||
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
|
||||
},
|
||||
"dottie": {
|
||||
"version": "2.0.2",
|
||||
@ -1008,9 +1016,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"escodegen": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz",
|
||||
"integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==",
|
||||
"version": "1.14.3",
|
||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
|
||||
"integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
|
||||
"requires": {
|
||||
"esprima": "^4.0.1",
|
||||
"estraverse": "^4.2.0",
|
||||
@ -1142,6 +1150,23 @@
|
||||
"requires": {
|
||||
"debug": "^2.6.9",
|
||||
"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": {
|
||||
@ -1152,6 +1177,23 @@
|
||||
"requires": {
|
||||
"debug": "^2.6.9",
|
||||
"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": {
|
||||
@ -1191,6 +1233,15 @@
|
||||
"resolve": "^1.11.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"
|
||||
}
|
||||
},
|
||||
"doctrine": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
|
||||
@ -1200,6 +1251,12 @@
|
||||
"esutils": "^2.0.2",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||
@ -1393,12 +1445,27 @@
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"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": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.6.tgz",
|
||||
"integrity": "sha512-w24zPWT8DkoIxSVkbxYPo9hkTiLpCQQzNsLRTCnecBhfbYv+IkIC5uLw2MIUAxBZ+7UMmXPjGxlhzUXo4RcbZw==",
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.9.tgz",
|
||||
"integrity": "sha512-f2w0aoe7lj3NeD8a4MXmYQsqir3Z66I08l9AKq04QbFUAjeZNmPwTlR5Lx2NGwSu/PslsAjGC38MWzo5tTjoBg==",
|
||||
"requires": {
|
||||
"busboy": "^0.3.1"
|
||||
}
|
||||
@ -1412,19 +1479,12 @@
|
||||
}
|
||||
},
|
||||
"express-validator": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.4.0.tgz",
|
||||
"integrity": "sha512-Fs+x0yDOSiUV+o5jIRloMyBxqpSzJiMM8KQW1IRVv2l49F6ATU0F9uPa+3K6vXNlLlhUjauv2FCGLFPMaNr24w==",
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.6.0.tgz",
|
||||
"integrity": "sha512-xcephfzFbUssJph/nOSKIdx+I+8GRz5by/8rOIKL6gJikKKKJjnwYH5TG1nIDB6kEalUtZMbOFuSNOp/HHY84Q==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.15",
|
||||
"validator": "^12.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"validator": {
|
||||
"version": "12.2.0",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz",
|
||||
"integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ=="
|
||||
}
|
||||
"validator": "^13.1.1"
|
||||
}
|
||||
},
|
||||
"extend": {
|
||||
@ -1449,9 +1509,9 @@
|
||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
|
||||
"integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
},
|
||||
"fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
@ -1507,6 +1567,21 @@
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "~1.5.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": {
|
||||
@ -1547,23 +1622,6 @@
|
||||
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
|
||||
"requires": {
|
||||
"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": {
|
||||
@ -1586,11 +1644,6 @@
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
"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": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
@ -1724,11 +1777,11 @@
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
|
||||
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||
"requires": {
|
||||
"ajv": "^6.5.5",
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
}
|
||||
},
|
||||
@ -1760,22 +1813,18 @@
|
||||
"dev": true
|
||||
},
|
||||
"helmet": {
|
||||
"version": "3.21.3",
|
||||
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.21.3.tgz",
|
||||
"integrity": "sha512-8OjGNdpG3WQhPO71fSy2fT4X3FSNutU1LDeAf+YS+Vil6r+fE7w8per5mNed6egGYbZl3QhKXgFzMYSwys+YQw==",
|
||||
"version": "3.23.3",
|
||||
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.23.3.tgz",
|
||||
"integrity": "sha512-U3MeYdzPJQhtvqAVBPntVgAvNSOJyagwZwyKsFdyRa8TV3pOKVFljalPOCxbw5Wwf2kncGhmP0qHjyazIdNdSA==",
|
||||
"requires": {
|
||||
"depd": "2.0.0",
|
||||
"dns-prefetch-control": "0.2.0",
|
||||
"dont-sniff-mimetype": "1.1.0",
|
||||
"expect-ct": "0.2.0",
|
||||
"feature-policy": "0.3.0",
|
||||
"frameguard": "3.1.0",
|
||||
"helmet-crossdomain": "0.4.0",
|
||||
"helmet-csp": "2.9.5",
|
||||
"helmet-csp": "2.10.0",
|
||||
"hide-powered-by": "1.1.0",
|
||||
"hpkp": "2.0.0",
|
||||
"hsts": "2.2.0",
|
||||
"ienoopen": "1.1.0",
|
||||
"nocache": "2.1.0",
|
||||
"referrer-policy": "1.2.0",
|
||||
"x-xss-protection": "1.3.0"
|
||||
@ -1794,9 +1843,9 @@
|
||||
"integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA=="
|
||||
},
|
||||
"helmet-csp": {
|
||||
"version": "2.9.5",
|
||||
"resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.9.5.tgz",
|
||||
"integrity": "sha512-w9nps5adqFQwgktVPDbXkARmZot/nr8aegzQas9AXdBSwBFBBefPpDSTV0wtgHlAUdDwY6MZo7qAl9yts3ppJg==",
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.10.0.tgz",
|
||||
"integrity": "sha512-Rz953ZNEFk8sT2XvewXkYN0Ho4GEZdjAZy4stjiEQV3eN7GDxg1QKmYggH7otDyIA7uGA6XnUMVSgeJwbR5X+w==",
|
||||
"requires": {
|
||||
"bowser": "2.9.0",
|
||||
"camelize": "1.0.0",
|
||||
@ -1879,11 +1928,6 @@
|
||||
"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": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
||||
@ -2197,9 +2241,9 @@
|
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
|
||||
},
|
||||
"jsdom": {
|
||||
"version": "16.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.2.tgz",
|
||||
"integrity": "sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg==",
|
||||
"version": "16.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.3.0.tgz",
|
||||
"integrity": "sha512-zggeX5UuEknpdZzv15+MS1dPYG0J/TftiiNunOeNxSl3qr8Z6cIlQpN0IdJa44z9aFxZRIVqRncvEhQ7X5DtZg==",
|
||||
"requires": {
|
||||
"abab": "^2.0.3",
|
||||
"acorn": "^7.1.1",
|
||||
@ -2221,7 +2265,7 @@
|
||||
"tough-cookie": "^3.0.1",
|
||||
"w3c-hr-time": "^1.0.2",
|
||||
"w3c-xmlserializer": "^2.0.0",
|
||||
"webidl-conversions": "^6.0.0",
|
||||
"webidl-conversions": "^6.1.0",
|
||||
"whatwg-encoding": "^1.0.5",
|
||||
"whatwg-mimetype": "^2.3.0",
|
||||
"whatwg-url": "^8.0.0",
|
||||
@ -2277,13 +2321,6 @@
|
||||
"lodash.once": "^4.0.0",
|
||||
"ms": "^2.1.1",
|
||||
"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": {
|
||||
@ -2441,12 +2478,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"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==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
||||
"requires": {
|
||||
"pseudomap": "^1.0.2",
|
||||
"yallist": "^2.1.2"
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"make-dir": {
|
||||
@ -2487,16 +2523,16 @@
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.43.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
||||
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.26",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
|
||||
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
|
||||
"version": "2.1.27",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||
"requires": {
|
||||
"mime-db": "1.43.0"
|
||||
"mime-db": "1.44.0"
|
||||
}
|
||||
},
|
||||
"mimic-fn": {
|
||||
@ -2536,29 +2572,48 @@
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.24.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
|
||||
"version": "2.27.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
|
||||
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
|
||||
},
|
||||
"moment-timezone": {
|
||||
"version": "0.5.28",
|
||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.28.tgz",
|
||||
"integrity": "sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw==",
|
||||
"version": "0.5.31",
|
||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz",
|
||||
"integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==",
|
||||
"requires": {
|
||||
"moment": ">= 2.9.0"
|
||||
}
|
||||
},
|
||||
"morgan": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
|
||||
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
|
||||
"dev": true,
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
|
||||
"integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
|
||||
"requires": {
|
||||
"basic-auth": "~2.0.0",
|
||||
"basic-auth": "~2.0.1",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"depd": "~2.0.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": {
|
||||
@ -2589,25 +2644,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"iconv-lite": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.1.tgz",
|
||||
"integrity": "sha512-ONHr16SQvKZNSqjQT9gy5z24Jw+uqfO02/ngBSBoqChZ+W8qXX7GPRa1RoUnzGADw8K63R1BXUMzarCVQBpY8Q==",
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
|
||||
"integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==",
|
||||
"requires": {
|
||||
"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==",
|
||||
"requires": {
|
||||
"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": {
|
||||
@ -2642,9 +2700,9 @@
|
||||
"integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q=="
|
||||
},
|
||||
"nodemailer": {
|
||||
"version": "6.4.6",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.6.tgz",
|
||||
"integrity": "sha512-/kJ+FYVEm2HuUlw87hjSqTss+GU35D4giOpdSfGp7DO+5h6RlJj7R94YaYHOkoxu1CSaM0d3WRBtCzwXrY6MKA=="
|
||||
"version": "6.4.11",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.11.tgz",
|
||||
"integrity": "sha512-BVZBDi+aJV4O38rxsUh164Dk1NCqgh6Cm0rQSb9SK/DHGll/DrCMnycVDD7msJgZCnmVa8ASo8EZzR7jsgTukQ=="
|
||||
},
|
||||
"nodemon": {
|
||||
"version": "2.0.4",
|
||||
@ -2793,8 +2851,7 @@
|
||||
"on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
@ -3319,19 +3376,19 @@
|
||||
}
|
||||
},
|
||||
"request-promise-core": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
|
||||
"integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
|
||||
"integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.15"
|
||||
"lodash": "^4.17.19"
|
||||
}
|
||||
},
|
||||
"request-promise-native": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz",
|
||||
"integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==",
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
|
||||
"integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
|
||||
"requires": {
|
||||
"request-promise-core": "1.1.3",
|
||||
"request-promise-core": "1.1.4",
|
||||
"stealthy-require": "^1.1.1",
|
||||
"tough-cookie": "^2.3.3"
|
||||
},
|
||||
@ -3479,6 +3536,21 @@
|
||||
"statuses": "~1.5.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"
|
||||
},
|
||||
"dependencies": {
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||
@ -3492,9 +3564,9 @@
|
||||
"integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
|
||||
},
|
||||
"sequelize": {
|
||||
"version": "5.21.5",
|
||||
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.21.5.tgz",
|
||||
"integrity": "sha512-n9hR5K4uQGmBGK/Y/iqewCeSFmKVsd0TRnh0tfoLoAkmXbKC4tpeK96RhKs7d+TTMtrJlgt2TNLVBaAxEwC4iw==",
|
||||
"version": "5.22.3",
|
||||
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.22.3.tgz",
|
||||
"integrity": "sha512-+nxf4TzdrB+PRmoWhR05TP9ukLAurK7qtKcIFv5Vhxm5Z9v+d2PcTT6Ea3YAoIQVkZ47QlT9XWAIUevMT/3l8Q==",
|
||||
"requires": {
|
||||
"bluebird": "^3.5.0",
|
||||
"cls-bluebird": "^2.1.0",
|
||||
@ -3521,11 +3593,6 @@
|
||||
"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": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
@ -3644,11 +3711,6 @@
|
||||
"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": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
@ -3768,9 +3830,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"sqlstring": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
|
||||
"integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz",
|
||||
"integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg=="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.16.1",
|
||||
@ -4121,6 +4183,23 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"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": {
|
||||
@ -4193,9 +4272,9 @@
|
||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz",
|
||||
"integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw=="
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
|
||||
"integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.1.1",
|
||||
@ -4214,9 +4293,9 @@
|
||||
}
|
||||
},
|
||||
"validator": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.0.0.tgz",
|
||||
"integrity": "sha512-anYx5fURbgF04lQV18nEQWZ/3wHGnxiKdG4aL8J+jEDsm98n/sU/bey+tYk6tnGJzm7ioh5FoqrAiQ6m03IgaA=="
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.1.1.tgz",
|
||||
"integrity": "sha512-8GfPiwzzRoWTg7OV1zva1KvrSemuMkv07MA9TTl91hfhe+wKrsrgVN4H2QSFd/U/FhiU3iWPYVgvbsOGwhyFWw=="
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
@ -4343,9 +4422,9 @@
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz",
|
||||
"integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w=="
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
||||
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
|
||||
},
|
||||
"x-xss-protection": {
|
||||
"version": "1.3.0",
|
||||
@ -4375,9 +4454,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,11 @@
|
||||
"sequelize": "^5.21.5",
|
||||
"smart-request-balancer": "^2.1.1",
|
||||
"uuid": "^7.0.2",
|
||||
"validator": "^13.0.0"
|
||||
"validator": "^13.0.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"morgan": "^1.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dotenv": "^8.2.0",
|
||||
"morgan": "^1.9.1",
|
||||
"nodemon": "^2.0.4",
|
||||
"snazzy": "^8.0.0",
|
||||
"standard": "^14.3.4"
|
||||
|
@ -9,197 +9,215 @@ const AdminRouter = Router()
|
||||
|
||||
AdminRouter.route('/functions')
|
||||
|
||||
// Récupère les fonctions
|
||||
// Récupère les fonctions
|
||||
.get(adminController.getFunctions)
|
||||
|
||||
// Permet de créé une fonction
|
||||
.post(fileUpload({
|
||||
useTempFiles: true,
|
||||
safeFileNames: true,
|
||||
preserveExtension: Number,
|
||||
limits: { fileSize: 5 * 1024 * 1024 }, // 5mb,
|
||||
parseNested: true
|
||||
}),
|
||||
[
|
||||
body('title')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir un titre.')
|
||||
.isLength({ max: 100 })
|
||||
.withMessage('Le titre est trop long.')
|
||||
.custom((title) => {
|
||||
if (title === 'undefined') {
|
||||
return Promise.reject(new Error('La fonction doit avoir un titre.'))
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('slug')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir un slug.')
|
||||
.isLength({ max: 100 })
|
||||
.withMessage('Le slug est trop long.')
|
||||
.custom((slug) => {
|
||||
if (slug === 'undefined') {
|
||||
return Promise.reject(new Error('La fonction doit avoir un slug.'))
|
||||
}
|
||||
return true
|
||||
})
|
||||
.custom(async (slug) => {
|
||||
try {
|
||||
const FunctionSlug = await Functions.findOne({ where: { slug } })
|
||||
if (FunctionSlug) {
|
||||
return Promise.reject(new Error('Le slug existe déjà...'))
|
||||
// Permet de créé une fonction
|
||||
.post(
|
||||
fileUpload({
|
||||
useTempFiles: true,
|
||||
safeFileNames: true,
|
||||
preserveExtension: Number,
|
||||
limits: { fileSize: 5 * 1024 * 1024 }, // 5mb,
|
||||
parseNested: true
|
||||
}),
|
||||
[
|
||||
body('title')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir un titre.')
|
||||
.isLength({ max: 100 })
|
||||
.withMessage('Le titre est trop long.')
|
||||
.custom(title => {
|
||||
if (title === 'undefined') {
|
||||
return Promise.reject(new Error('La fonction doit avoir un titre.'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('description')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir une description.')
|
||||
.isLength({ max: 255, min: 1 })
|
||||
.withMessage('La description est trop longue.')
|
||||
.custom((description) => {
|
||||
if (description === 'undefined') {
|
||||
return Promise.reject(new Error('La fonction doit avoir une description.'))
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('categorieId')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir une catégorie.')
|
||||
.custom(async (categorieId) => {
|
||||
try {
|
||||
const categorieFound = await Categories.findOne({ where: { id: parseInt(categorieId) } })
|
||||
if (!categorieFound) {
|
||||
return Promise.reject(new Error("La catégorie n'existe pas!"))
|
||||
return true
|
||||
}),
|
||||
body('slug')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir un slug.')
|
||||
.isLength({ max: 100 })
|
||||
.withMessage('Le slug est trop long.')
|
||||
.custom(slug => {
|
||||
if (slug === 'undefined') {
|
||||
return Promise.reject(new Error('La fonction doit avoir un slug.'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('type')
|
||||
.custom((type) => {
|
||||
return true
|
||||
})
|
||||
.custom(async slug => {
|
||||
try {
|
||||
const FunctionSlug = await Functions.findOne({ where: { slug } })
|
||||
if (FunctionSlug) {
|
||||
return Promise.reject(new Error('Le slug existe déjà...'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('description')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir une description.')
|
||||
.isLength({ max: 255, min: 1 })
|
||||
.withMessage('La description est trop longue.')
|
||||
.custom(description => {
|
||||
if (description === 'undefined') {
|
||||
return Promise.reject(
|
||||
new Error('La fonction doit avoir une description.')
|
||||
)
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('categorieId')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir une catégorie.')
|
||||
.custom(async categorieId => {
|
||||
try {
|
||||
const categorieFound = await Categories.findOne({
|
||||
where: { id: parseInt(categorieId) }
|
||||
})
|
||||
if (!categorieFound) {
|
||||
return Promise.reject(new Error("La catégorie n'existe pas!"))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('type').custom(type => {
|
||||
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
|
||||
})
|
||||
], adminController.postFunction)
|
||||
],
|
||||
adminController.postFunction
|
||||
)
|
||||
|
||||
AdminRouter.route('/functions/:slug')
|
||||
|
||||
// Récupère les informations d'une fonction
|
||||
// Récupère les informations d'une fonction
|
||||
.get(adminController.getFunctionBySlug)
|
||||
|
||||
AdminRouter.route('/functions/:id')
|
||||
|
||||
// Modifie information basique d'une fonction
|
||||
.put(fileUpload({
|
||||
useTempFiles: true,
|
||||
safeFileNames: true,
|
||||
preserveExtension: Number,
|
||||
limits: { fileSize: 5 * 1024 * 1024 }, // 5mb,
|
||||
parseNested: true
|
||||
}),
|
||||
[
|
||||
body('title')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir un titre.')
|
||||
.isLength({ max: 100 })
|
||||
.withMessage('Le titre est trop long.')
|
||||
.custom((title) => {
|
||||
if (title === 'undefined') {
|
||||
return Promise.reject(new Error('La fonction doit avoir un titre.'))
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('slug')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir un slug.')
|
||||
.isLength({ max: 100 })
|
||||
.withMessage('Le slug est trop long.')
|
||||
.custom((slug) => {
|
||||
if (slug === 'undefined') {
|
||||
return Promise.reject(new Error('La fonction doit avoir un slug.'))
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('description')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir une description.')
|
||||
.isLength({ max: 255, min: 1 })
|
||||
.withMessage('La description est trop longue.')
|
||||
.custom((description) => {
|
||||
if (description === 'undefined') {
|
||||
return Promise.reject(new Error('La fonction doit avoir une description.'))
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('categorieId')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir une catégorie.')
|
||||
.custom(async (categorieId) => {
|
||||
try {
|
||||
const categorieFound = await Categories.findOne({ where: { id: parseInt(categorieId) } })
|
||||
if (!categorieFound) {
|
||||
return Promise.reject(new Error("La catégorie n'existe pas!"))
|
||||
// Modifie information basique d'une fonction
|
||||
.put(
|
||||
fileUpload({
|
||||
useTempFiles: true,
|
||||
safeFileNames: true,
|
||||
preserveExtension: Number,
|
||||
limits: { fileSize: 5 * 1024 * 1024 }, // 5mb,
|
||||
parseNested: true
|
||||
}),
|
||||
[
|
||||
body('title')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir un titre.')
|
||||
.isLength({ max: 100 })
|
||||
.withMessage('Le titre est trop long.')
|
||||
.custom(title => {
|
||||
if (title === 'undefined') {
|
||||
return Promise.reject(new Error('La fonction doit avoir un titre.'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('type')
|
||||
.custom((type) => {
|
||||
return true
|
||||
}),
|
||||
body('slug')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir un slug.')
|
||||
.isLength({ max: 100 })
|
||||
.withMessage('Le slug est trop long.')
|
||||
.custom(slug => {
|
||||
if (slug === 'undefined') {
|
||||
return Promise.reject(new Error('La fonction doit avoir un slug.'))
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('description')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir une description.')
|
||||
.isLength({ max: 255, min: 1 })
|
||||
.withMessage('La description est trop longue.')
|
||||
.custom(description => {
|
||||
if (description === 'undefined') {
|
||||
return Promise.reject(
|
||||
new Error('La fonction doit avoir une description.')
|
||||
)
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('categorieId')
|
||||
.not()
|
||||
.isEmpty()
|
||||
.withMessage('La fonction doit avoir une catégorie.')
|
||||
.custom(async categorieId => {
|
||||
try {
|
||||
const categorieFound = await Categories.findOne({
|
||||
where: { id: parseInt(categorieId) }
|
||||
})
|
||||
if (!categorieFound) {
|
||||
return Promise.reject(new Error("La catégorie n'existe pas!"))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
return true
|
||||
}),
|
||||
body('type').custom(type => {
|
||||
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
|
||||
})
|
||||
], adminController.putFunction)
|
||||
],
|
||||
adminController.putFunction
|
||||
)
|
||||
|
||||
// Supprime une fonction avec son id
|
||||
// Supprime une fonction avec son id
|
||||
.delete(adminController.deleteFunction)
|
||||
|
||||
AdminRouter.route('/functions/article/:id')
|
||||
|
||||
.put(adminController.putFunctionArticle)
|
||||
|
||||
AdminRouter.route('/functions/form/:id')
|
||||
|
||||
.put(adminController.putFunctionForm)
|
||||
|
||||
AdminRouter.route('/categories')
|
||||
|
||||
// Crée une catégorie
|
||||
// Crée une catégorie
|
||||
.post(adminController.postCategory)
|
||||
|
||||
AdminRouter.route('/categories/:id')
|
||||
|
||||
// Modifier une catégorie avec son id
|
||||
// Modifier une catégorie avec son id
|
||||
.put(adminController.putCategory)
|
||||
|
||||
// Supprime une catégorie avec son id
|
||||
// Supprime une catégorie avec son id
|
||||
.delete(adminController.deleteCategory)
|
||||
|
||||
AdminRouter.route('/quotes')
|
||||
|
||||
// Récupère les citations pas encore validées
|
||||
// Récupère les citations pas encore validées
|
||||
.get(adminController.getQuotes)
|
||||
|
||||
AdminRouter.route('/quotes/:id')
|
||||
|
||||
// Valide ou supprime une citation
|
||||
// Valide ou supprime une citation
|
||||
.put(adminController.putQuote)
|
||||
|
||||
module.exports = AdminRouter
|
||||
|
@ -5,7 +5,7 @@ const CategoriesRouter = Router()
|
||||
|
||||
CategoriesRouter.route('/')
|
||||
|
||||
// Récupère les catégories
|
||||
// Récupère les catégories
|
||||
.get(categoriesController.getCategories)
|
||||
|
||||
module.exports = CategoriesRouter
|
||||
|
@ -6,18 +6,18 @@ const CommentsRouter = Router()
|
||||
|
||||
CommentsRouter.route('/:commentId')
|
||||
|
||||
// Modifier un commentaire
|
||||
// Modifier un commentaire
|
||||
.put(isAuth, commentsController.putCommentsById)
|
||||
|
||||
// Supprime un commentaire
|
||||
// Supprime un commentaire
|
||||
.delete(isAuth, commentsController.deleteCommentById)
|
||||
|
||||
CommentsRouter.route('/:functionId')
|
||||
|
||||
// Récupère les commentaires
|
||||
// Récupère les commentaires
|
||||
.get(commentsController.getCommentsByFunctionId)
|
||||
|
||||
// Permet à un utilisateur de poster un commentaire sur une fonction
|
||||
// Permet à un utilisateur de poster un commentaire sur une fonction
|
||||
.post(isAuth, commentsController.postCommentsByFunctionId)
|
||||
|
||||
module.exports = CommentsRouter
|
||||
|
@ -6,13 +6,13 @@ const FavoritesRouter = Router()
|
||||
|
||||
FavoritesRouter.route('/:functionId')
|
||||
|
||||
// Récupère si une fonction est en favoris (d'un utilisateur)
|
||||
// Récupère si une fonction est en favoris (d'un utilisateur)
|
||||
.get(isAuth, favoritesController.getFavoriteByFunctionId)
|
||||
|
||||
// Permet à un utilisateur d'ajouter une fonction aux favoris
|
||||
// Permet à un utilisateur d'ajouter une fonction aux favoris
|
||||
.post(isAuth, favoritesController.postFavoriteByFunctionId)
|
||||
|
||||
// Supprime une fonction des favoris d'un utilisateur
|
||||
// Supprime une fonction des favoris d'un utilisateur
|
||||
.delete(isAuth, favoritesController.deleteFavoriteByFunctionId)
|
||||
|
||||
module.exports = FavoritesRouter
|
||||
|
@ -5,15 +5,15 @@ const FunctionsRouter = Router()
|
||||
|
||||
FunctionsRouter.route('/')
|
||||
|
||||
// Récupère les fonctions
|
||||
// Récupère les fonctions
|
||||
.get(functionsController.getFunctions)
|
||||
|
||||
FunctionsRouter.route('/:slug')
|
||||
|
||||
// Récupère les informations de la fonction par son slug
|
||||
// Récupère les informations de la fonction par son slug
|
||||
.get(functionsController.getFunctionBySlug)
|
||||
|
||||
// Exécute la fonction demandée en paramètre
|
||||
// Exécute la fonction demandée en paramètre
|
||||
.post(functionsController.executeFunctionBySlug)
|
||||
|
||||
module.exports = FunctionsRouter
|
||||
|
@ -6,10 +6,10 @@ const QuotesRouter = Router()
|
||||
|
||||
QuotesRouter.route('/')
|
||||
|
||||
// Récupère les citations
|
||||
// Récupère les citations
|
||||
.get(quotesController.getQuotes)
|
||||
|
||||
// Proposer une citation
|
||||
// Proposer une citation
|
||||
.post(isAuth, quotesController.postQuote)
|
||||
|
||||
module.exports = QuotesRouter
|
||||
|
@ -6,18 +6,18 @@ const TasksRouter = Router()
|
||||
|
||||
TasksRouter.route('/')
|
||||
|
||||
// Récupère les tâches à faire d'un user
|
||||
// Récupère les tâches à faire d'un user
|
||||
.get(isAuth, tasksController.getTasks)
|
||||
|
||||
// Poster une nouvelle tâche à faire
|
||||
// Poster une nouvelle tâche à faire
|
||||
.post(isAuth, tasksController.postTask)
|
||||
|
||||
TasksRouter.route('/:id')
|
||||
|
||||
// Permet de mettre une tâche à faire en isCompleted ou !isCompleted
|
||||
// Permet de mettre une tâche à faire en isCompleted ou !isCompleted
|
||||
.put(isAuth, tasksController.putTask)
|
||||
|
||||
// Supprimer une tâche à faire
|
||||
// Supprimer une tâche à faire
|
||||
.delete(isAuth, tasksController.deleteTask)
|
||||
|
||||
module.exports = TasksRouter
|
||||
|
@ -1,10 +1,10 @@
|
||||
.footer {
|
||||
border-top: var(--border-header-footer);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-top: var(--border-header-footer);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.footer-text {
|
||||
line-height: 2.5;
|
||||
line-height: 2.5;
|
||||
}
|
@ -8,8 +8,11 @@ export default function Footer () {
|
||||
<Link href='/about'>
|
||||
<a>FunctionProject</a>
|
||||
</Link>
|
||||
- Version 2.1 <br />
|
||||
<a href='https://divlo.fr/' target='_blank' rel='noopener noreferrer'>Divlo</a> | Tous droits réservés
|
||||
- Version 2.1 <br />
|
||||
<a href='https://divlo.fr/' target='_blank' rel='noopener noreferrer'>
|
||||
Divlo
|
||||
</a>{' '}
|
||||
| Tous droits réservés
|
||||
</p>
|
||||
</footer>
|
||||
)
|
||||
|
@ -5,7 +5,7 @@ import useAPI from '../../hooks/useAPI'
|
||||
import api from '../../utils/api'
|
||||
import '../../public/css/pages/admin.css'
|
||||
|
||||
const AddEditFunction = (props) => {
|
||||
const AddEditFunction = props => {
|
||||
const [, categories] = useAPI('/categories')
|
||||
const [inputState, setInputState] = useState(props.defaultInputState)
|
||||
const [message, setMessage] = useState('')
|
||||
@ -22,18 +22,29 @@ const AddEditFunction = (props) => {
|
||||
}
|
||||
}, [categories])
|
||||
|
||||
const apiCallFunction = (formData) => {
|
||||
if (props.isEditing) 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 apiCallFunction = formData => {
|
||||
if (props.isEditing) {
|
||||
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 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)
|
||||
}
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault()
|
||||
setIsLoading(true)
|
||||
const formData = new window.FormData()
|
||||
@ -53,8 +64,10 @@ const AddEditFunction = (props) => {
|
||||
setIsLoading(false)
|
||||
window.location.reload(true)
|
||||
})
|
||||
.catch((error) => {
|
||||
setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`)
|
||||
.catch(error => {
|
||||
setMessage(
|
||||
`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`
|
||||
)
|
||||
setIsLoading(false)
|
||||
})
|
||||
}
|
||||
@ -63,23 +76,61 @@ const AddEditFunction = (props) => {
|
||||
<>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='form-group'>
|
||||
<label className='form-label' htmlFor='title'>Titre :</label>
|
||||
<input value={inputState.title} onChange={handleChange} type='text' name='title' id='title' className='form-control' placeholder='(e.g : Nombre aléatoire)' />
|
||||
<label className='form-label' htmlFor='title'>
|
||||
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 className='form-group'>
|
||||
<label className='form-label' htmlFor='slug'>Slug :</label>
|
||||
<input value={inputState.slug} onChange={handleChange} type='text' name='slug' id='slug' className='form-control' placeholder='(e.g : randomNumber)' />
|
||||
<label className='form-label' htmlFor='slug'>
|
||||
Slug :
|
||||
</label>
|
||||
<input
|
||||
value={inputState.slug}
|
||||
onChange={handleChange}
|
||||
type='text'
|
||||
name='slug'
|
||||
id='slug'
|
||||
className='form-control'
|
||||
placeholder='(e.g : randomNumber)'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label className='form-label' htmlFor='description'>Description :</label>
|
||||
<textarea style={{ height: 'auto' }} value={inputState.description} onChange={handleChange} name='description' id='description' className='form-control' rows='5' />
|
||||
<label className='form-label' htmlFor='description'>
|
||||
Description :
|
||||
</label>
|
||||
<textarea
|
||||
style={{ height: 'auto' }}
|
||||
value={inputState.description}
|
||||
onChange={handleChange}
|
||||
name='description'
|
||||
id='description'
|
||||
className='form-control'
|
||||
rows='5'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label className='form-label' htmlFor='type'>Type :</label>
|
||||
<select onChange={handleChange} name='type' id='type' className='form-control' {...(props.isEditing) && { value: inputState.type }}>
|
||||
<label className='form-label' htmlFor='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='article'>Article</option>
|
||||
<option value='page'>Page</option>
|
||||
@ -87,37 +138,68 @@ const AddEditFunction = (props) => {
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label className='form-label' htmlFor='categorieId'>Catégorie :</label>
|
||||
<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>
|
||||
<label className='form-label' htmlFor='categorieId'>
|
||||
Catégorie :
|
||||
</label>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<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 />
|
||||
<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>
|
||||
|
||||
{(props.isEditing) &&
|
||||
{props.isEditing && (
|
||||
<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' />
|
||||
<label className='custom-control-label' htmlFor='isOnline'>isOnline</label>
|
||||
</div>}
|
||||
<input
|
||||
onChange={event => handleChange(event, true)}
|
||||
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'>
|
||||
<button type='submit' className='btn btn-dark'>Envoyer</button>
|
||||
<button type='submit' className='btn btn-dark'>
|
||||
Envoyer
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className='form-result text-center'>
|
||||
{
|
||||
(isLoading)
|
||||
? <Loader />
|
||||
: htmlParser(message)
|
||||
}
|
||||
{isLoading ? <Loader /> : htmlParser(message)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
@ -6,19 +6,16 @@ import FunctionArticle from '../FunctionPage/FunctionArticle'
|
||||
import 'notyf/notyf.min.css'
|
||||
import '../../public/css/suneditor.min.css'
|
||||
|
||||
const SunEditor = dynamic(
|
||||
() => import('suneditor-react'),
|
||||
{ ssr: false }
|
||||
)
|
||||
const SunEditor = dynamic(() => import('suneditor-react'), { ssr: false })
|
||||
|
||||
const EditArticleFunction = (props) => {
|
||||
const EditArticleFunction = props => {
|
||||
const [htmlContent, setHtmlContent] = useState('')
|
||||
|
||||
const handleEditorChange = (content) => {
|
||||
const handleEditorChange = content => {
|
||||
setHtmlContent(content)
|
||||
}
|
||||
|
||||
const handleSave = async (content) => {
|
||||
const handleSave = async content => {
|
||||
let Notyf
|
||||
if (typeof window !== 'undefined') {
|
||||
Notyf = require('notyf')
|
||||
@ -27,7 +24,11 @@ const EditArticleFunction = (props) => {
|
||||
duration: 5000
|
||||
})
|
||||
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é!')
|
||||
} catch {
|
||||
notyf.error('Erreur!')
|
||||
@ -36,7 +37,12 @@ const EditArticleFunction = (props) => {
|
||||
|
||||
return (
|
||||
<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} />
|
||||
</div>
|
||||
)
|
||||
|
@ -2,8 +2,10 @@ import { useState } from 'react'
|
||||
import api from '../../utils/api'
|
||||
import 'notyf/notyf.min.css'
|
||||
|
||||
const EditFormFunction = (props) => {
|
||||
const [inputsArray, setInputsArray] = useState(props.functionInfo.utilizationForm || [])
|
||||
const EditFormFunction = props => {
|
||||
const [inputsArray, setInputsArray] = useState(
|
||||
props.functionInfo.utilizationForm || []
|
||||
)
|
||||
|
||||
const addInput = () => {
|
||||
const newInputsArray = [...inputsArray]
|
||||
@ -11,7 +13,7 @@ const EditFormFunction = (props) => {
|
||||
setInputsArray(newInputsArray)
|
||||
}
|
||||
|
||||
const addOption = (event) => {
|
||||
const addOption = event => {
|
||||
const newInputsArray = [...inputsArray]
|
||||
const index = event.target.id.split('-')[1]
|
||||
const inputObject = newInputsArray[index]
|
||||
@ -27,7 +29,7 @@ const EditFormFunction = (props) => {
|
||||
setInputsArray(newInputsArray)
|
||||
}
|
||||
|
||||
const handleChangeInput = (event) => {
|
||||
const handleChangeInput = event => {
|
||||
const newInputsArray = [...inputsArray]
|
||||
const index = event.target.id.split('-')[1]
|
||||
const inputObject = newInputsArray[index]
|
||||
@ -38,7 +40,7 @@ const EditFormFunction = (props) => {
|
||||
setInputsArray(newInputsArray)
|
||||
}
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
const handleSubmit = async event => {
|
||||
event.preventDefault()
|
||||
let Notyf
|
||||
if (typeof window !== 'undefined') {
|
||||
@ -48,14 +50,18 @@ const EditFormFunction = (props) => {
|
||||
duration: 5000
|
||||
})
|
||||
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é!')
|
||||
} catch (error) {
|
||||
notyf.error('Erreur!')
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemoveInput = (event) => {
|
||||
const handleRemoveInput = event => {
|
||||
const newInputsArray = [...inputsArray]
|
||||
const index = event.target.id.split('-')[1]
|
||||
newInputsArray.splice(index, 1)
|
||||
@ -72,39 +78,88 @@ const EditFormFunction = (props) => {
|
||||
|
||||
return (
|
||||
<div className='container-fluid'>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
|
||||
{(inputsArray.length > 0) &&
|
||||
{inputsArray.length > 0 && (
|
||||
<div className='form-group text-center'>
|
||||
<button type='submit' className='btn btn-dark'>Sauvegarder</button>
|
||||
</div>}
|
||||
<button type='submit' className='btn btn-dark'>
|
||||
Sauvegarder
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{inputsArray.map((input, index) => {
|
||||
return (
|
||||
<div key={index} className='form-group Admin__Input-group'>
|
||||
|
||||
<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>
|
||||
|
||||
<label className='form-label' htmlFor={`name-${index}`}>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)' />
|
||||
<label className='form-label' htmlFor={`name-${index}`}>
|
||||
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 />
|
||||
|
||||
<label className='form-label' htmlFor={`label-${index}`}>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 :)" />
|
||||
<label className='form-label' htmlFor={`label-${index}`}>
|
||||
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 />
|
||||
|
||||
{(input.type !== 'select') &&
|
||||
{input.type !== 'select' && (
|
||||
<>
|
||||
<label 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)' />
|
||||
<label
|
||||
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 />
|
||||
</>}
|
||||
</>
|
||||
)}
|
||||
|
||||
<label className='form-label' htmlFor={`type-${index}`}>Type :</label>
|
||||
<select value={input.type} onChange={handleChangeInput} name='type' id={`type-${index}`} className='form-control'>
|
||||
<label className='form-label' htmlFor={`type-${index}`}>
|
||||
Type :
|
||||
</label>
|
||||
<select
|
||||
value={input.type}
|
||||
onChange={handleChangeInput}
|
||||
name='type'
|
||||
id={`type-${index}`}
|
||||
className='form-control'
|
||||
>
|
||||
<option value='text'>text</option>
|
||||
<option value='integer'>Number integer</option>
|
||||
<option value='float'>Number float</option>
|
||||
@ -112,40 +167,87 @@ const EditFormFunction = (props) => {
|
||||
<option value='select'>select</option>
|
||||
</select>
|
||||
|
||||
{(input.type === 'select') &&
|
||||
{input.type === 'select' && (
|
||||
<div style={{ marginTop: '50px' }}>
|
||||
|
||||
<label className='form-label'>Options :</label>
|
||||
|
||||
{input.options.map((option, optionIndex) => {
|
||||
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'>
|
||||
<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>
|
||||
|
||||
<label 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" />
|
||||
<label
|
||||
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 />
|
||||
<label 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" />
|
||||
<label
|
||||
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 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>
|
||||
)
|
||||
})}
|
||||
|
||||
</form>
|
||||
|
||||
<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>
|
||||
)
|
||||
|
@ -1,67 +1,67 @@
|
||||
.FunctionCard {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin: 0 0 50px 0;
|
||||
cursor: pointer;
|
||||
transition: all .3s;
|
||||
color: var(--text-color);
|
||||
text-decoration: none !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin: 0 0 50px 0;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
color: var(--text-color);
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.FunctionCard__container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.FunctionCard:hover {
|
||||
transform: translateY(-7px);
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
/* col-md */
|
||||
@media (min-width: 768px) {
|
||||
.FunctionCard {
|
||||
margin: 0 30px 50px 30px;
|
||||
}
|
||||
.FunctionCard {
|
||||
margin: 0 30px 50px 30px;
|
||||
}
|
||||
}
|
||||
/* col-xl */
|
||||
@media (min-width: 1200px) {
|
||||
.FunctionCard {
|
||||
margin: 0 20px 50px 20px;
|
||||
}
|
||||
.FunctionCard {
|
||||
margin: 0 20px 50px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.FunctionCard__top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.FunctionCard__image {
|
||||
width: 150px;
|
||||
width: 150px;
|
||||
}
|
||||
.FunctionCard__title {
|
||||
font-size: 1.4em;
|
||||
margin: 0;
|
||||
color: var(--important);
|
||||
font-weight: 300;
|
||||
font-size: 1.4em;
|
||||
margin: 0;
|
||||
color: var(--important);
|
||||
font-weight: 300;
|
||||
}
|
||||
.FunctionCard__description {
|
||||
margin: 20px 0 10px 0;
|
||||
margin: 20px 0 10px 0;
|
||||
}
|
||||
.FunctionCard__info {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.FunctionCard__category {
|
||||
border-radius: 0.5em;
|
||||
padding: 0.5em;
|
||||
margin-right: 20px;
|
||||
font-size: 16.4px;
|
||||
border-radius: 0.5em;
|
||||
padding: 0.5em;
|
||||
margin-right: 20px;
|
||||
font-size: 16.4px;
|
||||
}
|
@ -5,51 +5,76 @@ import Loader from '../Loader'
|
||||
import { API_URL } from '../../utils/config/config'
|
||||
import './FunctionCard.css'
|
||||
|
||||
const FunctionCard = memo(forwardRef((props, ref) => {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const FunctionCard = memo(
|
||||
forwardRef((props, ref) => {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
|
||||
const handleLoad = () => {
|
||||
setIsLoading(false)
|
||||
}
|
||||
const handleLoad = () => {
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
const handleError = (event) => {
|
||||
event.target.src = API_URL + '/images/functions/default.png'
|
||||
}
|
||||
const handleError = event => {
|
||||
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 (
|
||||
<Link
|
||||
{
|
||||
...(props.isAdmin)
|
||||
return (
|
||||
<Link
|
||||
{...(props.isAdmin
|
||||
? {
|
||||
href: '/admin/[slug]',
|
||||
as: `/admin/${props.slug}`
|
||||
}
|
||||
: {
|
||||
href: (isFormOrArticle) ? '/functions/[slug]' : `/functions/${props.slug}`,
|
||||
href: isFormOrArticle
|
||||
? '/functions/[slug]'
|
||||
: `/functions/${props.slug}`,
|
||||
as: `/functions/${props.slug}`
|
||||
})}
|
||||
>
|
||||
{/* FunctionCard a une hauteur pendant chargement */}
|
||||
<a
|
||||
ref={ref}
|
||||
style={
|
||||
isLoading ? { height: '360px', justifyContent: 'center' } : null
|
||||
}
|
||||
}
|
||||
>
|
||||
{/* 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'>
|
||||
{isLoading && <Loader width='125px' height='125px' />}
|
||||
className='FunctionCard col-sm-24 col-md-10 col-xl-7'
|
||||
>
|
||||
{isLoading && <Loader width='125px' height='125px' />}
|
||||
|
||||
<div className={`FunctionCard__container ${isLoading ? 'd-none' : ''}`}>
|
||||
<div className='FunctionCard__top'>
|
||||
<img onLoad={handleLoad} onError={handleError} className='FunctionCard__image' alt={props.title} src={API_URL + props.image} />
|
||||
<h2 className='FunctionCard__title'>{props.title}</h2>
|
||||
<p className='FunctionCard__description text-center'>{props.description}</p>
|
||||
<div
|
||||
className={`FunctionCard__container ${isLoading ? 'd-none' : ''}`}
|
||||
>
|
||||
<div className='FunctionCard__top'>
|
||||
<img
|
||||
onLoad={handleLoad}
|
||||
onError={handleError}
|
||||
className='FunctionCard__image'
|
||||
alt={props.title}
|
||||
src={API_URL + props.image}
|
||||
/>
|
||||
<h2 className='FunctionCard__title'>{props.title}</h2>
|
||||
<p className='FunctionCard__description text-center'>
|
||||
{props.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className='FunctionCard__info'>
|
||||
<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 className='FunctionCard__info'>
|
||||
<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>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}))
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
export default FunctionCard
|
||||
|
@ -1,33 +1,33 @@
|
||||
.CommentCard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||
border: 1px solid black;
|
||||
border-radius: .7em;
|
||||
margin: 15px 0 15px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
border-radius: 0.7em;
|
||||
margin: 15px 0 15px 0;
|
||||
}
|
||||
.CommentCard__container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
}
|
||||
.CommentCard__user-logo {
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.CommentCard__message-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.CommentCard__message {
|
||||
line-height: 1.8;
|
||||
margin: 15px 0 0 0;
|
||||
line-height: 1.8;
|
||||
margin: 15px 0 0 0;
|
||||
}
|
@ -23,28 +23,42 @@ const CommentCard = forwardRef((props, ref) => {
|
||||
props.manageComment.setLoadingComments(true)
|
||||
if (isAuth && user.token != null) {
|
||||
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 commentIndex = newCommentsData.rows.findIndex((value) => value.id === props.id)
|
||||
const commentIndex = newCommentsData.rows.findIndex(
|
||||
value => value.id === props.id
|
||||
)
|
||||
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 {}
|
||||
}
|
||||
props.manageComment.setLoadingComments(false)
|
||||
}
|
||||
|
||||
const handleChange = (event) => {
|
||||
const handleChange = event => {
|
||||
setEditInput(event.target.value)
|
||||
}
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault()
|
||||
api.put(`/comments/${props.id}`, { message: editInput }, { headers: { Authorization: user.token } })
|
||||
.then((_response) => {
|
||||
api
|
||||
.put(
|
||||
`/comments/${props.id}`,
|
||||
{ message: editInput },
|
||||
{ headers: { Authorization: user.token } }
|
||||
)
|
||||
.then(_response => {
|
||||
setEditing(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`)
|
||||
.catch(error => {
|
||||
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='row'>
|
||||
<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>
|
||||
<span className='CommentCard__message-info'>
|
||||
<Link href='/users/[name]' as={`/users/${props.user.name}`}>
|
||||
<a>{props.user.name}</a>
|
||||
</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>
|
||||
</div>
|
||||
<div className='row'>
|
||||
<div className='col-24'>
|
||||
{
|
||||
(!isEditing)
|
||||
? (
|
||||
<>
|
||||
<div className='CommentCard__message'>
|
||||
<ReactMarkdown source={editInput} renderers={{ code: CodeBlock }} />
|
||||
</div>
|
||||
{(isAuth && user.name === props.user.name) &&
|
||||
<p style={{ fontSize: '15px', margin: '15px 0 0 0', fontStyle: 'italic' }}>
|
||||
<a onClick={deleteCommentById} href='#'>supprimer</a>
|
||||
-
|
||||
<a style={{ cursor: 'pointer' }} onClick={editComment}>modifier</a>
|
||||
</p>}
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='form-group FunctionComments__post-group'>
|
||||
<label className='form-label' htmlFor='commentEdit'>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 className='form-group' style={{ marginTop: '0.7em' }}>
|
||||
<button type='submit' className='btn btn-dark'>Envoyer</button>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
{htmlParser(message)}
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
{!isEditing ? (
|
||||
<>
|
||||
<div className='CommentCard__message'>
|
||||
<ReactMarkdown
|
||||
source={editInput}
|
||||
renderers={{ code: CodeBlock }}
|
||||
/>
|
||||
</div>
|
||||
{isAuth && user.name === props.user.name && (
|
||||
<p
|
||||
style={{
|
||||
fontSize: '15px',
|
||||
margin: '15px 0 0 0',
|
||||
fontStyle: 'italic'
|
||||
}}
|
||||
>
|
||||
<a onClick={deleteCommentById} href='#'>
|
||||
supprimer
|
||||
</a>
|
||||
-
|
||||
<a style={{ cursor: 'pointer' }} onClick={editComment}>
|
||||
modifier
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='form-group FunctionComments__post-group'>
|
||||
<label className='form-label' htmlFor='commentEdit'>
|
||||
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 className='form-group' style={{ marginTop: '0.7em' }}>
|
||||
<button type='submit' className='btn btn-dark'>
|
||||
Envoyer
|
||||
</button>
|
||||
</div>
|
||||
<div className='text-center'>{htmlParser(message)}</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,7 +3,11 @@ import htmlParser from 'html-react-parser'
|
||||
const FunctionArticle = ({ article }) => {
|
||||
return (
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
.FunctionComments__row {
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.FunctionComments__textarea {
|
||||
height: auto;
|
||||
resize: vertical;
|
||||
height: auto;
|
||||
resize: vertical;
|
||||
}
|
@ -18,49 +18,69 @@ const FunctionComments = ({ functionId }) => {
|
||||
|
||||
// Récupère les commentaires si la page change
|
||||
useEffect(() => {
|
||||
getCommentsData().then((data) => setCommentsData({
|
||||
hasMore: data.hasMore,
|
||||
rows: [...commentsData.rows, ...data.rows]
|
||||
}))
|
||||
getCommentsData().then(data =>
|
||||
setCommentsData({
|
||||
hasMore: data.hasMore,
|
||||
rows: [...commentsData.rows, ...data.rows]
|
||||
})
|
||||
)
|
||||
}, [pageComments])
|
||||
|
||||
// Permet la pagination au scroll
|
||||
const observer = useRef()
|
||||
const lastCommentCardRef = useCallback((node) => {
|
||||
if (isLoadingComments) return
|
||||
if (observer.current) observer.current.disconnect()
|
||||
observer.current = new window.IntersectionObserver((entries) => {
|
||||
if (entries[0].isIntersecting && commentsData.hasMore) {
|
||||
setPageComments(pageComments + 1)
|
||||
}
|
||||
}, { threshold: 1 })
|
||||
if (node) observer.current.observe(node)
|
||||
}, [isLoadingComments, commentsData.hasMore])
|
||||
const lastCommentCardRef = useCallback(
|
||||
node => {
|
||||
if (isLoadingComments) return
|
||||
if (observer.current) observer.current.disconnect()
|
||||
observer.current = new window.IntersectionObserver(
|
||||
entries => {
|
||||
if (entries[0].isIntersecting && commentsData.hasMore) {
|
||||
setPageComments(pageComments + 1)
|
||||
}
|
||||
},
|
||||
{ threshold: 1 }
|
||||
)
|
||||
if (node) observer.current.observe(node)
|
||||
},
|
||||
[isLoadingComments, commentsData.hasMore]
|
||||
)
|
||||
|
||||
const getCommentsData = async () => {
|
||||
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)
|
||||
return data
|
||||
}
|
||||
|
||||
const handleChange = (event) => {
|
||||
const handleChange = event => {
|
||||
const inputStateNew = { ...inputState }
|
||||
inputStateNew[event.target.name] = event.target.value
|
||||
setInputState(inputStateNew)
|
||||
}
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
const handleSubmit = async event => {
|
||||
setLoadingComments(true)
|
||||
event.preventDefault()
|
||||
const token = user.token
|
||||
if (isAuth && token != null && inputState.commentPost != null) {
|
||||
try {
|
||||
const response = await api.post(`/comments/${functionId}`, { 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] })
|
||||
const response = await api.post(
|
||||
`/comments/${functionId}`,
|
||||
{ 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: '' })
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
setLoadingComments(false)
|
||||
}
|
||||
@ -70,40 +90,73 @@ const FunctionComments = ({ functionId }) => {
|
||||
<div className='FunctionComments__post container-fluid'>
|
||||
<div className='row FunctionComments__row'>
|
||||
<div className='col-24'>
|
||||
{
|
||||
(isAuth)
|
||||
? (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='form-group FunctionComments__post-group'>
|
||||
<label className='form-label' htmlFor='commentPost'>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 className='form-group' style={{ marginTop: '0.7em' }}>
|
||||
<button type='submit' className='btn btn-dark'>Envoyer</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
: (
|
||||
<p className='text-center'>
|
||||
Vous devez être <Link href='/users/login'><a>connecté</a></Link> pour poster un commentaire.
|
||||
</p>
|
||||
)
|
||||
}
|
||||
{isAuth ? (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='form-group FunctionComments__post-group'>
|
||||
<label className='form-label' htmlFor='commentPost'>
|
||||
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 className='form-group' style={{ marginTop: '0.7em' }}>
|
||||
<button type='submit' className='btn btn-dark'>
|
||||
Envoyer
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
) : (
|
||||
<p className='text-center'>
|
||||
Vous devez être{' '}
|
||||
<Link href='/users/login'>
|
||||
<a>connecté</a>
|
||||
</Link>{' '}
|
||||
pour poster un commentaire.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='container-fluid'>
|
||||
{isLoadingComments &&
|
||||
{isLoadingComments && (
|
||||
<div className='row justify-content-center'>
|
||||
<Loader />
|
||||
</div>}
|
||||
</div>
|
||||
)}
|
||||
<div className='row justify-content-center'>
|
||||
{commentsData.rows.map((comment, index) => {
|
||||
// Si c'est le dernier élément
|
||||
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>
|
||||
|
@ -9,7 +9,7 @@ import api from '../../utils/api'
|
||||
import { API_URL } from '../../utils/config/config'
|
||||
import '../FunctionCard/FunctionCard.css'
|
||||
|
||||
const FunctionComponentTop = (props) => {
|
||||
const FunctionComponentTop = props => {
|
||||
const { isAuth, user } = useContext(UserContext)
|
||||
const [isFavorite, setIsFavorite] = useState(false)
|
||||
|
||||
@ -21,7 +21,9 @@ const FunctionComponentTop = (props) => {
|
||||
|
||||
const fetchFavorite = async () => {
|
||||
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)
|
||||
} catch {}
|
||||
}
|
||||
@ -30,16 +32,22 @@ const FunctionComponentTop = (props) => {
|
||||
if (isAuth && user.token != null) {
|
||||
try {
|
||||
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)
|
||||
}
|
||||
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)
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
const handleError = (event) => {
|
||||
const handleError = event => {
|
||||
event.target.src = API_URL + '/images/functions/default.png'
|
||||
}
|
||||
|
||||
@ -47,18 +55,44 @@ const FunctionComponentTop = (props) => {
|
||||
<div className='container-fluid'>
|
||||
<div className='row justify-content-center text-center'>
|
||||
<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) &&
|
||||
<FontAwesomeIcon onClick={toggleFavorite} {...(isFavorite) ? { icon: faStar } : { icon: farStar }} title={(isFavorite) ? 'Retirer la fonction des favoris' : 'Ajouter la fonction aux favoris'} className='FunctionComponent__star-favorite' />}
|
||||
|
||||
<img onError={handleError} className='FunctionComponent__image' src={API_URL + props.image} alt={props.title} />
|
||||
<h1 className='FunctionComponent__title title-important'>{props.title}</h1>
|
||||
<img
|
||||
onError={handleError}
|
||||
className='FunctionComponent__image'
|
||||
src={API_URL + props.image}
|
||||
alt={props.title}
|
||||
/>
|
||||
<h1 className='FunctionComponent__title title-important'>
|
||||
{props.title}
|
||||
</h1>
|
||||
<p className='FunctionComponent__description'>{props.description}</p>
|
||||
<div className='FunctionCard__info'>
|
||||
<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>
|
||||
<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>
|
||||
|
@ -10,7 +10,7 @@ import 'react-datepicker/dist/react-datepicker.css'
|
||||
|
||||
registerLocale('fr', fr)
|
||||
|
||||
const FunctionForm = (props) => {
|
||||
const FunctionForm = props => {
|
||||
const [inputState, setInputState] = useState({})
|
||||
const [message, setMessage] = useState('')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
@ -18,7 +18,7 @@ const FunctionForm = (props) => {
|
||||
// inputState par défaut
|
||||
useEffect(() => {
|
||||
const inputStateNew = { ...inputState }
|
||||
props.inputsArray.forEach((input) => {
|
||||
props.inputsArray.forEach(input => {
|
||||
if (input.type === 'select' && input.options.length > 0) {
|
||||
inputStateNew[input.name] = input.options[0].value
|
||||
}
|
||||
@ -26,21 +26,22 @@ const FunctionForm = (props) => {
|
||||
setInputState(inputStateNew)
|
||||
}, [])
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const handleSubmit = event => {
|
||||
setIsLoading(true)
|
||||
event.preventDefault()
|
||||
api.post(`/functions/${props.slug}`, inputState)
|
||||
.then((response) => {
|
||||
api
|
||||
.post(`/functions/${props.slug}`, inputState)
|
||||
.then(response => {
|
||||
setMessage(response.data.resultHTML)
|
||||
setIsLoading(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
setMessage(error.response.data.message)
|
||||
setIsLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
const handleChange = (event) => {
|
||||
const handleChange = event => {
|
||||
const inputStateNew = { ...inputState }
|
||||
inputStateNew[event.target.name] = event.target.value
|
||||
setInputState(inputStateNew)
|
||||
@ -61,16 +62,35 @@ const FunctionForm = (props) => {
|
||||
case 'text':
|
||||
return (
|
||||
<div key={index} className='form-group'>
|
||||
<label className='form-label' htmlFor={input.name}>{input.label}</label>
|
||||
<input onChange={handleChange} type='text' name={input.name} id={input.name} placeholder={input.placeholder} className='form-control' />
|
||||
<label className='form-label' htmlFor={input.name}>
|
||||
{input.label}
|
||||
</label>
|
||||
<input
|
||||
onChange={handleChange}
|
||||
type='text'
|
||||
name={input.name}
|
||||
id={input.name}
|
||||
placeholder={input.placeholder}
|
||||
className='form-control'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
case 'integer':
|
||||
case 'float':
|
||||
return (
|
||||
<div key={index} className='form-group'>
|
||||
<label className='form-label' htmlFor={input.name}>{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' />
|
||||
<label className='form-label' htmlFor={input.name}>
|
||||
{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>
|
||||
)
|
||||
case 'calendar':
|
||||
@ -81,13 +101,14 @@ const FunctionForm = (props) => {
|
||||
document.body.appendChild(newScript)
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
const DatePicker = dynamic(
|
||||
() => import('react-datepicker'),
|
||||
{ ssr: false }
|
||||
)
|
||||
const DatePicker = dynamic(() => import('react-datepicker'), {
|
||||
ssr: false
|
||||
})
|
||||
return (
|
||||
<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 />
|
||||
<DatePicker
|
||||
selected={(() => {
|
||||
@ -97,7 +118,11 @@ const FunctionForm = (props) => {
|
||||
const year = dateArray[2]
|
||||
const month = dateArray[1]
|
||||
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')
|
||||
} catch {
|
||||
@ -108,9 +133,13 @@ const FunctionForm = (props) => {
|
||||
dateFormat='dd/MM/yyyy'
|
||||
fixedHeight
|
||||
placeholderText={input.placeholder}
|
||||
onChange={(dateObject) => {
|
||||
onChange={dateObject => {
|
||||
try {
|
||||
const formattedDate = date.format(dateObject, 'DD/MM/YYYY', true)
|
||||
const formattedDate = date.format(
|
||||
dateObject,
|
||||
'DD/MM/YYYY',
|
||||
true
|
||||
)
|
||||
handleChange({
|
||||
target: {
|
||||
name: input.name,
|
||||
@ -125,33 +154,39 @@ const FunctionForm = (props) => {
|
||||
case 'select':
|
||||
return (
|
||||
<div key={index} className='form-group'>
|
||||
<label className='form-label' htmlFor={input.name}>{input.label}</label>
|
||||
<select onChange={handleChange} name={input.name} id={input.name} value={inputState[input.name] || input.options[0]} className='form-control'>
|
||||
<label className='form-label' htmlFor={input.name}>
|
||||
{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) => {
|
||||
return (
|
||||
<option key={optionIndex} value={option.value}>{option.name}</option>
|
||||
<option key={optionIndex} value={option.value}>
|
||||
{option.name}
|
||||
</option>
|
||||
)
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<p>Erreur, l'input n'est pas valide...</p>
|
||||
)
|
||||
return <p>Erreur, l'input n'est pas valide...</p>
|
||||
}
|
||||
})}
|
||||
|
||||
<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>
|
||||
</form>
|
||||
<div className='form-result text-center'>
|
||||
{
|
||||
(isLoading)
|
||||
? <Loader />
|
||||
: htmlParser(message)
|
||||
}
|
||||
{isLoading ? <Loader /> : htmlParser(message)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
@ -4,17 +4,29 @@ import HeadTag from '../HeadTag'
|
||||
import FunctionTabsTop from './FunctionTabsTop'
|
||||
import FunctionComponentTop from './FunctionComponentTop'
|
||||
|
||||
const FunctionPage = (props) => {
|
||||
const FunctionPage = props => {
|
||||
const [slideIndex, setSlideIndex] = useState(0)
|
||||
|
||||
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'>
|
||||
<FunctionTabsTop slideIndex={slideIndex} setSlideIndex={setSlideIndex} tabNames={props.tabNames} />
|
||||
<FunctionTabsTop
|
||||
slideIndex={slideIndex}
|
||||
setSlideIndex={setSlideIndex}
|
||||
tabNames={props.tabNames}
|
||||
/>
|
||||
<FunctionComponentTop {...props} />
|
||||
<props.FunctionTabManager {...props} slideIndex={slideIndex} setSlideIndex={setSlideIndex} />
|
||||
<props.FunctionTabManager
|
||||
{...props}
|
||||
slideIndex={slideIndex}
|
||||
setSlideIndex={setSlideIndex}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
@ -1,41 +1,41 @@
|
||||
.FunctionTabs__nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
border-bottom: 1px solid #d9e2ef;
|
||||
margin-bottom: -1px;
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
border-bottom: 1px solid #d9e2ef;
|
||||
margin-bottom: -1px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.FunctionTabs__nav-item {
|
||||
margin-bottom: -1px;
|
||||
cursor: pointer;
|
||||
margin-bottom: -1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.FunctionTabs__nav-link {
|
||||
color: var(--text-color);
|
||||
border: 1px solid #0c0b0b38;
|
||||
border-bottom: 0px;
|
||||
border-top-left-radius: .375rem;
|
||||
border-top-right-radius: .375rem;
|
||||
display: block;
|
||||
padding: .5rem 1rem;
|
||||
transition: .2s;
|
||||
color: var(--text-color);
|
||||
border: 1px solid #0c0b0b38;
|
||||
border-bottom: 0px;
|
||||
border-top-left-radius: 0.375rem;
|
||||
border-top-right-radius: 0.375rem;
|
||||
display: block;
|
||||
padding: 0.5rem 1rem;
|
||||
transition: 0.2s;
|
||||
}
|
||||
.FunctionTabs__nav-link-active {
|
||||
border-color: #d9e2ef #d9e2ef #fff;
|
||||
color: var(--important);
|
||||
border-color: #d9e2ef #d9e2ef #fff;
|
||||
color: var(--important);
|
||||
}
|
||||
.FunctionTabs__nav-link:hover {
|
||||
border-color: #f1f4f8 #f1f4f8 #d9e2ef;
|
||||
text-decoration: none;
|
||||
border-color: #f1f4f8 #f1f4f8 #d9e2ef;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (max-width: 490px) {
|
||||
.FunctionTabs__nav {
|
||||
flex-direction: column;
|
||||
}
|
||||
.FunctionTabs__nav-link {
|
||||
border-color: #f1f4f8 #f1f4f8 #d9e2ef;
|
||||
}
|
||||
.FunctionTabs__nav {
|
||||
flex-direction: column;
|
||||
}
|
||||
.FunctionTabs__nav-link {
|
||||
border-color: #f1f4f8 #f1f4f8 #d9e2ef;
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
import SwipeableViews from 'react-swipeable-views'
|
||||
import './FunctionTabs.css'
|
||||
|
||||
const FunctionTabs = (props) => {
|
||||
const FunctionTabs = props => {
|
||||
return (
|
||||
<div className='container-fluid'>
|
||||
<SwipeableViews onChangeIndex={(index) => props.setSlideIndex(index)} index={props.slideIndex}>
|
||||
<SwipeableViews
|
||||
onChangeIndex={index => props.setSlideIndex(index)}
|
||||
index={props.slideIndex}
|
||||
>
|
||||
{props.children}
|
||||
</SwipeableViews>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
const FunctionTabsTop = (props) => {
|
||||
const FunctionTabsTop = props => {
|
||||
return (
|
||||
<div className='container'>
|
||||
<div className='row justify-content-center'>
|
||||
@ -7,7 +7,11 @@ const FunctionTabsTop = (props) => {
|
||||
return (
|
||||
<li key={index} className='FunctionTabs__nav-item'>
|
||||
<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)}
|
||||
>
|
||||
{tabName}
|
||||
|
@ -1,40 +1,40 @@
|
||||
.Functions__title {
|
||||
padding: 20px 0 20px 0;
|
||||
margin-bottom: 0;
|
||||
padding: 20px 0 20px 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.Functions__form-control {
|
||||
display: block;
|
||||
height: calc(1.5em + .75rem + 2px);
|
||||
padding: .375rem .75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .5em;
|
||||
display: block;
|
||||
height: calc(1.5em + 0.75rem + 2px);
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
.Functions__search-container {
|
||||
margin-bottom: 50px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.Functions__select-option {
|
||||
color: rgb(221, 220, 220);
|
||||
color: rgb(221, 220, 220);
|
||||
}
|
||||
.Functions__search-input {
|
||||
width: 40%;
|
||||
width: 40%;
|
||||
}
|
||||
/* col-sm */
|
||||
@media (max-width: 576px) {
|
||||
.Functions__search-container {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.Functions__select {
|
||||
width: 90%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.Functions__search-input {
|
||||
width: 90%;
|
||||
}
|
||||
.Functions__search-container {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.Functions__select {
|
||||
width: 90%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.Functions__search-input {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
@ -7,15 +7,21 @@ import useAPI from '../../hooks/useAPI'
|
||||
import './FunctionsList.css'
|
||||
|
||||
let pageFunctions = 1
|
||||
const FunctionsList = (props) => {
|
||||
const FunctionsList = props => {
|
||||
const { categoryId } = useRouter().query
|
||||
|
||||
// State de recherche et de catégories
|
||||
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
|
||||
const [functionsData, setFunctionsData] = useState({ hasMore: true, rows: [] })
|
||||
const [functionsData, setFunctionsData] = useState({
|
||||
hasMore: true,
|
||||
rows: []
|
||||
})
|
||||
const [isLoadingFunctions, setLoadingFunctions] = useState(true)
|
||||
|
||||
// Récupère la catégorie avec la query categoryId
|
||||
@ -28,43 +34,54 @@ const FunctionsList = (props) => {
|
||||
// Récupère les fonctions si la catégorie/recherche change
|
||||
useEffect(() => {
|
||||
pageFunctions = 1
|
||||
getFunctionsData().then((data) => setFunctionsData(data))
|
||||
getFunctionsData().then(data => setFunctionsData(data))
|
||||
}, [inputSearch])
|
||||
|
||||
// Permet la pagination au scroll
|
||||
const observer = useRef()
|
||||
const lastFunctionCardRef = useCallback((node) => {
|
||||
if (isLoadingFunctions) return
|
||||
if (observer.current) observer.current.disconnect()
|
||||
observer.current = new window.IntersectionObserver((entries) => {
|
||||
if (entries[0].isIntersecting && functionsData.hasMore) {
|
||||
pageFunctions += 1
|
||||
getFunctionsData().then((data) => {
|
||||
setFunctionsData((oldData) => {
|
||||
return {
|
||||
hasMore: data.hasMore,
|
||||
rows: [...oldData.rows, ...data.rows]
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}, { threshold: 1 })
|
||||
if (node) observer.current.observe(node)
|
||||
}, [isLoadingFunctions, functionsData.hasMore])
|
||||
const lastFunctionCardRef = useCallback(
|
||||
node => {
|
||||
if (isLoadingFunctions) return
|
||||
if (observer.current) observer.current.disconnect()
|
||||
observer.current = new window.IntersectionObserver(
|
||||
entries => {
|
||||
if (entries[0].isIntersecting && functionsData.hasMore) {
|
||||
pageFunctions += 1
|
||||
getFunctionsData().then(data => {
|
||||
setFunctionsData(oldData => {
|
||||
return {
|
||||
hasMore: data.hasMore,
|
||||
rows: [...oldData.rows, ...data.rows]
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
{ threshold: 1 }
|
||||
)
|
||||
if (node) observer.current.observe(node)
|
||||
},
|
||||
[isLoadingFunctions, functionsData.hasMore]
|
||||
)
|
||||
|
||||
const getFunctionsData = async () => {
|
||||
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, {
|
||||
headers: {
|
||||
...(props.isAdmin && props.token != null) && { Authorization: props.token }
|
||||
...(props.isAdmin &&
|
||||
props.token != null && { Authorization: props.token })
|
||||
}
|
||||
})
|
||||
setLoadingFunctions(false)
|
||||
return data
|
||||
}
|
||||
|
||||
const handleChange = (event) => {
|
||||
const handleChange = event => {
|
||||
const inputSearchNew = { ...inputSearch }
|
||||
inputSearchNew[event.target.name] = event.target.value
|
||||
setInputSearch(inputSearchNew)
|
||||
@ -72,27 +89,58 @@ const FunctionsList = (props) => {
|
||||
|
||||
return (
|
||||
<div className='container text-center'>
|
||||
<div className='row justify-content-center'>
|
||||
{props.children}
|
||||
</div>
|
||||
<div className='row justify-content-center'>{props.children}</div>
|
||||
|
||||
<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>
|
||||
{categories.map((category) => (
|
||||
<option key={category.id} value={category.id} className='Functions__select-option' style={{ backgroundColor: category.color }}>{category.name}</option>
|
||||
{categories.map(category => (
|
||||
<option
|
||||
key={category.id}
|
||||
value={category.id}
|
||||
className='Functions__select-option'
|
||||
style={{ backgroundColor: category.color }}
|
||||
>
|
||||
{category.name}
|
||||
</option>
|
||||
))}
|
||||
</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 className='row justify-content-center'>
|
||||
{functionsData.rows.map((currentFunction, index) => {
|
||||
// Si c'est le dernier élément
|
||||
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>
|
||||
{isLoadingFunctions && <Loader />}
|
||||
|
@ -36,7 +36,8 @@ const HeadTag = ({ title, image, description }) => (
|
||||
|
||||
HeadTag.defaultProps = {
|
||||
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'
|
||||
}
|
||||
|
||||
|
@ -1,161 +1,167 @@
|
||||
/* HEADER */
|
||||
.Header {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: .5rem 1rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 1rem;
|
||||
|
||||
border-bottom: var(--border-header-footer);
|
||||
background-color: var(--background-color);
|
||||
border-bottom: var(--border-header-footer);
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.Header {
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.Header {
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
.Header > .container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.Header > .container {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.Header > .container {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
}
|
||||
/* Brand */
|
||||
.Header__brand-link {
|
||||
display: inline-block;
|
||||
padding-top: .3125rem;
|
||||
padding-bottom: .3125rem;
|
||||
margin-right: 1rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: inherit;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
padding-top: 0.3125rem;
|
||||
padding-bottom: 0.3125rem;
|
||||
margin-right: 1rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: inherit;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#brand-link__logo-small-screen {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 496px) {
|
||||
#brand-link__logo {
|
||||
display: none;
|
||||
}
|
||||
.Header__brand-link {
|
||||
width: 30%;
|
||||
}
|
||||
#brand-link__logo-small-screen {
|
||||
display: inline-block;
|
||||
}
|
||||
#brand-link__logo {
|
||||
display: none;
|
||||
}
|
||||
.Header__brand-link {
|
||||
width: 30%;
|
||||
}
|
||||
#brand-link__logo-small-screen {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.Header .Header__navbar {
|
||||
display: flex;
|
||||
flex-basis: auto;
|
||||
}
|
||||
.Header .Header__navbar {
|
||||
display: flex;
|
||||
flex-basis: auto;
|
||||
}
|
||||
}
|
||||
.Header__navbar {
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
}
|
||||
.navbar__list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-left: auto;
|
||||
}
|
||||
.navbar__list.navbar__list-active {
|
||||
margin: 0 !important;
|
||||
display: flex;
|
||||
margin: 0 !important;
|
||||
display: flex;
|
||||
}
|
||||
@media (max-width: 992px) {
|
||||
.navbar__list {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.navbar__list {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
.navbar-link {
|
||||
display: block;
|
||||
padding: .5rem 1rem;
|
||||
display: block;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
/* Details Styling */
|
||||
.navbar-link:hover {
|
||||
text-decoration: none;
|
||||
color: rgba(255, 255, 255, .75);
|
||||
text-decoration: none;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
.navbar-link, .navbar-link-active {
|
||||
color: rgba(255, 255, 255, .5);
|
||||
.navbar-link,
|
||||
.navbar-link-active {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.navbar-link-active, .navbar-link-active:hover, .Header__brand-link {
|
||||
color: var(--text-color);
|
||||
.navbar-link-active,
|
||||
.navbar-link-active:hover,
|
||||
.Header__brand-link {
|
||||
color: var(--text-color);
|
||||
}
|
||||
.navbar-item {
|
||||
list-style: none;
|
||||
list-style: none;
|
||||
}
|
||||
.navbar-link {
|
||||
font-size: 16px;
|
||||
padding: .5rem;
|
||||
font-size: 16px;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
/* Hamburger Icon */
|
||||
.Header__hamburger {
|
||||
display: none;
|
||||
width: 56px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, .1);
|
||||
border-radius: .25rem;
|
||||
position: relative;
|
||||
display: none;
|
||||
width: 56px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 0.25rem;
|
||||
position: relative;
|
||||
}
|
||||
.Header__hamburger > span, .Header__hamburger > span::before, .Header__hamburger > span::after {
|
||||
position: absolute;
|
||||
width: 22px;
|
||||
height: 1.3px;
|
||||
background-color: rgba(255, 255, 255);
|
||||
.Header__hamburger > span,
|
||||
.Header__hamburger > span::before,
|
||||
.Header__hamburger > span::after {
|
||||
position: absolute;
|
||||
width: 22px;
|
||||
height: 1.3px;
|
||||
background-color: rgba(255, 255, 255);
|
||||
}
|
||||
.Header__hamburger > span {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: background-color .3s ease-in-out;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
}
|
||||
.Header__hamburger > span::before, .Header__hamburger > span::after {
|
||||
content: '';
|
||||
transition: transform .3s ease-in-out;
|
||||
.Header__hamburger > span::before,
|
||||
.Header__hamburger > span::after {
|
||||
content: '';
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.Header__hamburger > span::before {
|
||||
transform: translateY(-8px);
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
.Header__hamburger > span::after {
|
||||
transform: translateY(8px);
|
||||
transform: translateY(8px);
|
||||
}
|
||||
.Header__hamburger-active span {
|
||||
background-color: transparent;
|
||||
background-color: transparent;
|
||||
}
|
||||
.Header__hamburger-active > span::before {
|
||||
transform: translateY(0px) rotateZ(45deg);
|
||||
transform: translateY(0px) rotateZ(45deg);
|
||||
}
|
||||
.Header__hamburger-active > span::after {
|
||||
transform: translateY(0px) rotateZ(-45deg);
|
||||
transform: translateY(0px) rotateZ(-45deg);
|
||||
}
|
||||
/* Apparition du hamburger */
|
||||
@media (max-width: 992px) {
|
||||
.Header__hamburger {
|
||||
display: flex;
|
||||
}
|
||||
.Header__hamburger {
|
||||
display: flex;
|
||||
}
|
||||
}
|
@ -17,56 +17,74 @@ export default function Header () {
|
||||
return (
|
||||
<header className='Header'>
|
||||
<div className='container'>
|
||||
|
||||
{/* Brand */}
|
||||
<Link href='/'>
|
||||
<a className='Header__brand-link'>
|
||||
<img 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' />
|
||||
<img
|
||||
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>
|
||||
</Link>
|
||||
|
||||
{/* 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 />
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<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='Fonctions' path='/functions' />
|
||||
<NavigationLink name='Utilisateurs' path='/users' />
|
||||
{
|
||||
(!isAuth)
|
||||
? (
|
||||
<>
|
||||
<NavigationLink name="S'inscrire" path='/users/register' />
|
||||
<NavigationLink name='Connexion' path='/users/login' />
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<li className='navbar-item'>
|
||||
<Link href='/users/[name]' as={`/users/${user.name}`}>
|
||||
<a className={`navbar-link ${pathname === '/users/[name]' ? 'navbar-link-active' : null}`}>Mon Profil</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li className='navbar-item'>
|
||||
<Link href='/'>
|
||||
<a onClick={logoutUser} className='navbar-link'>Se déconnecter</a>
|
||||
</Link>
|
||||
</li>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
(isAuth && user.isAdmin) &&
|
||||
<NavigationLink name='Admin' path='/admin' />
|
||||
}
|
||||
{!isAuth ? (
|
||||
<>
|
||||
<NavigationLink name="S'inscrire" path='/users/register' />
|
||||
<NavigationLink name='Connexion' path='/users/login' />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<li className='navbar-item'>
|
||||
<Link href='/users/[name]' as={`/users/${user.name}`}>
|
||||
<a
|
||||
className={`navbar-link ${
|
||||
pathname === '/users/[name]'
|
||||
? 'navbar-link-active'
|
||||
: null
|
||||
}`}
|
||||
>
|
||||
Mon Profil
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li className='navbar-item'>
|
||||
<Link href='/'>
|
||||
<a onClick={logoutUser} className='navbar-link'>
|
||||
Se déconnecter
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
{isAuth && user.isAdmin && (
|
||||
<NavigationLink name='Admin' path='/admin' />
|
||||
)}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
|
@ -8,7 +8,11 @@ export default function NavigationLink (props) {
|
||||
return (
|
||||
<li className='navbar-item'>
|
||||
<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}
|
||||
</a>
|
||||
</Link>
|
||||
|
@ -1,7 +1,17 @@
|
||||
const Loader = ({ width, height, speed }) => (
|
||||
<svg width={width} height={height} viewBox='0 0 100 100'>
|
||||
<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>
|
||||
</svg>
|
||||
)
|
||||
|
@ -1,8 +1,6 @@
|
||||
const Modal = (props) => (
|
||||
const Modal = props => (
|
||||
<div className='Modal container-fluid'>
|
||||
<div className='Modal__content'>
|
||||
{props.children}
|
||||
</div>
|
||||
<div className='Modal__content'>{props.children}</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
@ -22,7 +22,9 @@ function UserContextProvider (props) {
|
||||
|
||||
useEffect(() => {
|
||||
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 {
|
||||
setMessageLogin('')
|
||||
}
|
||||
@ -43,10 +45,14 @@ function UserContextProvider (props) {
|
||||
cookies.set('user', newUser, { path: '/', maxAge: newUser.expiresIn })
|
||||
setUser(newUser)
|
||||
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)
|
||||
} 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)
|
||||
setIsAuth(false)
|
||||
setUser(null)
|
||||
@ -54,7 +60,17 @@ function UserContextProvider (props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<UserContext.Provider value={{ user, loginUser, logoutUser, loginLoading, messageLogin, isAuth, setMessageLogin }}>
|
||||
<UserContext.Provider
|
||||
value={{
|
||||
user,
|
||||
loginUser,
|
||||
logoutUser,
|
||||
loginLoading,
|
||||
messageLogin,
|
||||
isAuth,
|
||||
setMessageLogin
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</UserContext.Provider>
|
||||
)
|
||||
|
@ -2,8 +2,8 @@ import { useContext } from 'react'
|
||||
import { UserContext } from '../contexts/UserContext'
|
||||
import redirect from '../utils/redirect'
|
||||
|
||||
const withoutAuth = (WrappedComponent) => {
|
||||
const Component = (props) => {
|
||||
const withoutAuth = WrappedComponent => {
|
||||
const Component = props => {
|
||||
const { isAuth, user } = useContext(UserContext)
|
||||
|
||||
if (isAuth) return redirect({}, `/users/${user.name}`)
|
||||
|
@ -14,11 +14,11 @@ function useAPI (url, defaultData = [], method = 'get', options = {}) {
|
||||
|
||||
useEffect(() => {
|
||||
api[method](url, options)
|
||||
.then((result) => {
|
||||
.then(result => {
|
||||
setData(result.data)
|
||||
setIsLoading(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
setHasError(true)
|
||||
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,236 +1,244 @@
|
||||
/* GENERAL */
|
||||
:root {
|
||||
--border-header-footer: 3px rgba(255, 255, 255, .7) solid;
|
||||
--background-color: #181818;
|
||||
--text-color: rgb(222, 222, 222);
|
||||
--important: #ffd800;
|
||||
--border-header-footer: 3px rgba(255, 255, 255, 0.7) solid;
|
||||
--background-color: #181818;
|
||||
--text-color: rgb(222, 222, 222);
|
||||
--important: #ffd800;
|
||||
}
|
||||
html {
|
||||
line-height: initial;
|
||||
line-height: initial;
|
||||
}
|
||||
body {
|
||||
background-color: var(--background-color);
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
#__next {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
padding-top: 99px; /* Height of the Header */
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
padding-top: 99px; /* Height of the Header */
|
||||
}
|
||||
p {
|
||||
font-size: 18px;
|
||||
line-height: 1.9;
|
||||
font-size: 18px;
|
||||
line-height: 1.9;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* CONTENT */
|
||||
.content {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
}
|
||||
.centeredContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
div[aria-hidden="true"] > * {
|
||||
display: none;
|
||||
div[aria-hidden='true'] > * {
|
||||
display: none;
|
||||
}
|
||||
.notyf__message {
|
||||
font-family: sans-serif;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
/* LOADING */
|
||||
.isLoading {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
#preloader {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
@keyframes Loader__spin {
|
||||
0% {
|
||||
animation-timing-function: cubic-bezier(0.5856, 0.0703, 0.4143, 0.9297);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
0% {
|
||||
animation-timing-function: cubic-bezier(0.5856, 0.0703, 0.4143, 0.9297);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* UTILITIES */
|
||||
.text-center {
|
||||
text-align: center;
|
||||
word-break: break-word;
|
||||
text-align: center;
|
||||
word-break: break-word;
|
||||
}
|
||||
.justify-content-center {
|
||||
justify-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.align-items-center {
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
.title-important {
|
||||
color: var(--important);
|
||||
font-weight: 500;
|
||||
color: var(--important);
|
||||
font-weight: 500;
|
||||
}
|
||||
a, .important {
|
||||
color: var(--important);
|
||||
text-decoration: none;
|
||||
a,
|
||||
.important {
|
||||
color: var(--important);
|
||||
text-decoration: none;
|
||||
}
|
||||
.d-none {
|
||||
display: none !important;
|
||||
display: none !important;
|
||||
}
|
||||
.form-group {
|
||||
margin-top: 1.4rem;
|
||||
margin-top: 1.4rem;
|
||||
}
|
||||
.form-control {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: calc(1.5em + .75rem + 2px);
|
||||
padding: .375rem .75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: calc(1.5em + 0.75rem + 2px);
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.form-label {
|
||||
display: inline-block;
|
||||
margin-bottom: .5em;
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.form-result {
|
||||
margin: 30px;
|
||||
margin: 30px;
|
||||
}
|
||||
.form-success {
|
||||
color: #90EE90;
|
||||
color: #90ee90;
|
||||
}
|
||||
.form-error {
|
||||
color: #ff7f7f;
|
||||
color: #ff7f7f;
|
||||
}
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
padding: .375rem .75rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
border-radius: .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;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
border-radius: 0.25rem;
|
||||
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 {
|
||||
padding: .5rem 1rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.5;
|
||||
border-radius: .3rem;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.5;
|
||||
border-radius: 0.3rem;
|
||||
}
|
||||
.btn-dark:hover {
|
||||
color: #fff;
|
||||
background-color: #23272b;
|
||||
border-color: #1d2124;
|
||||
color: #fff;
|
||||
background-color: #23272b;
|
||||
border-color: #1d2124;
|
||||
}
|
||||
.btn-dark {
|
||||
color: #fff;
|
||||
background-color: #343a40;
|
||||
border-color: #343a40;
|
||||
color: #fff;
|
||||
background-color: #343a40;
|
||||
border-color: #343a40;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
color: #fff;
|
||||
background-color: #0069d9;
|
||||
border-color: #0062cc;
|
||||
color: #fff;
|
||||
background-color: #0069d9;
|
||||
border-color: #0062cc;
|
||||
}
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
}
|
||||
.custom-control {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.custom-control-input {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
}
|
||||
.custom-control-label {
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
.custom-control-input:checked~.custom-control-label::before {
|
||||
color: #fff;
|
||||
border-color: #007bff;
|
||||
background-color: #007bff;
|
||||
.custom-control-input:checked ~ .custom-control-label::before {
|
||||
color: #fff;
|
||||
border-color: #007bff;
|
||||
background-color: #007bff;
|
||||
}
|
||||
.custom-switch .custom-control-label::before {
|
||||
left: -2.25rem;
|
||||
width: 1.75rem;
|
||||
pointer-events: all;
|
||||
border-radius: .5rem;
|
||||
left: -2.25rem;
|
||||
width: 1.75rem;
|
||||
pointer-events: all;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
.custom-control-label::before {
|
||||
position: absolute;
|
||||
top: .25rem;
|
||||
left: -1.5rem;
|
||||
display: block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
background-color: #fff;
|
||||
border: #adb5bd solid 1px;
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
left: -1.5rem;
|
||||
display: block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
background-color: #fff;
|
||||
border: #adb5bd solid 1px;
|
||||
}
|
||||
.custom-switch .custom-control-input:checked~.custom-control-label::after {
|
||||
background-color: #fff;
|
||||
-webkit-transform: translateX(.75rem);
|
||||
transform: translateX(.75rem);
|
||||
.custom-switch .custom-control-input:checked ~ .custom-control-label::after {
|
||||
background-color: #fff;
|
||||
-webkit-transform: translateX(0.75rem);
|
||||
transform: translateX(0.75rem);
|
||||
}
|
||||
.custom-switch .custom-control-label::after {
|
||||
top: calc(.25rem + 2px);
|
||||
left: calc(-2.25rem + 2px);
|
||||
width: calc(1rem - 4px);
|
||||
height: calc(1rem - 4px);
|
||||
background-color: #adb5bd;
|
||||
border-radius: .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: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .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;
|
||||
top: calc(0.25rem + 2px);
|
||||
left: calc(-2.25rem + 2px);
|
||||
width: calc(1rem - 4px);
|
||||
height: calc(1rem - 4px);
|
||||
background-color: #adb5bd;
|
||||
border-radius: 0.5rem;
|
||||
transition: 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;
|
||||
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 {
|
||||
position: absolute;
|
||||
top: .25rem;
|
||||
left: -1.5rem;
|
||||
display: block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
content: "";
|
||||
background: no-repeat 50%/50% 50%;
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
left: -1.5rem;
|
||||
display: block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
content: '';
|
||||
background: no-repeat 50%/50% 50%;
|
||||
}
|
||||
.table-column {
|
||||
display: grid;
|
||||
display: grid;
|
||||
}
|
||||
.table, th, td {
|
||||
border: 1px solid var(--text-color);
|
||||
border-collapse: collapse;
|
||||
.table,
|
||||
th,
|
||||
td {
|
||||
border: 1px solid var(--text-color);
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.table-row {
|
||||
padding: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
.vscode-editor {
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
overflow: auto;
|
||||
white-space: normal !important;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
overflow: auto;
|
||||
white-space: normal !important;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
36
website/public/css/normalize.css
vendored
36
website/public/css/normalize.css
vendored
@ -174,7 +174,8 @@ textarea {
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
input {
|
||||
/* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
@ -184,7 +185,8 @@ input { /* 1 */
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
select {
|
||||
/* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
@ -193,9 +195,9 @@ select { /* 1 */
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
[type='button'],
|
||||
[type='reset'],
|
||||
[type='submit'] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
@ -204,9 +206,9 @@ button,
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
[type='button']::-moz-focus-inner,
|
||||
[type='reset']::-moz-focus-inner,
|
||||
[type='submit']::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
@ -216,9 +218,9 @@ button::-moz-focus-inner,
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
[type='button']:-moz-focusring,
|
||||
[type='reset']:-moz-focusring,
|
||||
[type='submit']:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
@ -267,8 +269,8 @@ textarea {
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
[type='checkbox'],
|
||||
[type='radio'] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
@ -277,8 +279,8 @@ textarea {
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
[type='number']::-webkit-inner-spin-button,
|
||||
[type='number']::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@ -287,7 +289,7 @@ textarea {
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
[type='search'] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
@ -296,7 +298,7 @@ textarea {
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
[type='search']::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
|
@ -1,81 +1,81 @@
|
||||
/* Make clicks pass-through */
|
||||
#nprogress {
|
||||
pointer-events: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#nprogress .bar {
|
||||
background: var(--important);
|
||||
background: var(--important);
|
||||
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
/* Fancy blur effect */
|
||||
#nprogress .peg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
box-shadow: 0 0 10px var(--important), 0 0 5px var(--important);
|
||||
opacity: 1;
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
box-shadow: 0 0 10px var(--important), 0 0 5px var(--important);
|
||||
opacity: 1;
|
||||
|
||||
-webkit-transform: rotate(3deg) translate(0px, -4px);
|
||||
-ms-transform: rotate(3deg) translate(0px, -4px);
|
||||
transform: rotate(3deg) translate(0px, -4px);
|
||||
-webkit-transform: rotate(3deg) translate(0px, -4px);
|
||||
-ms-transform: rotate(3deg) translate(0px, -4px);
|
||||
transform: rotate(3deg) translate(0px, -4px);
|
||||
}
|
||||
|
||||
/* Remove these to get rid of the spinner */
|
||||
#nprogress .spinner {
|
||||
display: block;
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
display: block;
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
#nprogress .spinner-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
box-sizing: border-box;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
box-sizing: border-box;
|
||||
|
||||
border: solid 2px transparent;
|
||||
border-top-color: var(--important);
|
||||
border-left-color: var(--important);
|
||||
border-radius: 50%;
|
||||
border: solid 2px transparent;
|
||||
border-top-color: var(--important);
|
||||
border-left-color: var(--important);
|
||||
border-radius: 50%;
|
||||
|
||||
-webkit-animation: nprogress-spinner 400ms linear infinite;
|
||||
animation: nprogress-spinner 400ms linear infinite;
|
||||
-webkit-animation: nprogress-spinner 400ms linear infinite;
|
||||
animation: nprogress-spinner 400ms linear infinite;
|
||||
}
|
||||
|
||||
.nprogress-custom-parent {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nprogress-custom-parent #nprogress .spinner,
|
||||
.nprogress-custom-parent #nprogress .bar {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@-webkit-keyframes nprogress-spinner {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
}
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes nprogress-spinner {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
.Error404__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 100%;
|
||||
}
|
@ -1,34 +1,34 @@
|
||||
.FunctionComponent__top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin-top: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin-top: 40px;
|
||||
}
|
||||
.FunctionComponent__title {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.FunctionComponent__description {
|
||||
word-break: break-all;
|
||||
margin-bottom: 0;
|
||||
word-break: break-all;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.FunctionComponent__slide {
|
||||
margin-top: 30px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.FunctionComponent__image {
|
||||
width: 150px;
|
||||
width: 150px;
|
||||
}
|
||||
.FunctionComponent__star-favorite {
|
||||
color: var(--important);
|
||||
width: 2em !important;
|
||||
height: 2em !important;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 15px;
|
||||
margin-right: 15px;
|
||||
cursor: pointer;
|
||||
color: var(--important);
|
||||
width: 2em !important;
|
||||
height: 2em !important;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 15px;
|
||||
margin-right: 15px;
|
||||
cursor: pointer;
|
||||
}
|
@ -1,35 +1,35 @@
|
||||
.Admin__Modal__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin: 30px 0 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
.Admin__Modal__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin-bottom: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.Admin__Modal-top-container {
|
||||
margin: 20px 0;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.Admin__Modal-select-option {
|
||||
color: rgb(221, 220, 220);
|
||||
color: rgb(221, 220, 220);
|
||||
}
|
||||
.Admin__Function-slide {
|
||||
margin-top: 40px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
.Admin__Input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin-top: 40px;
|
||||
padding: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin-top: 40px;
|
||||
padding: 40px;
|
||||
}
|
@ -1,38 +1,38 @@
|
||||
.Chronometer__container {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
padding: 20px;
|
||||
margin-bottom: 40px;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
padding: 20px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.Chronometer__item {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.Chronomter__row {
|
||||
margin: 8px 0 16px 0;
|
||||
text-align: center;
|
||||
margin: 8px 0 16px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.Chronometer__time-left {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.Chronometer__buttons {
|
||||
border-top: solid 3px var(--important);
|
||||
padding-top: 12px;
|
||||
border-top: solid 3px var(--important);
|
||||
padding-top: 12px;
|
||||
}
|
||||
.Chronometer__row-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.Chronometer-btn {
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-left: 14px;
|
||||
margin-right: 14px;
|
||||
width: 2em;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-left: 14px;
|
||||
margin-right: 14px;
|
||||
width: 2em;
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
.Product__image {
|
||||
max-width: 150px;
|
||||
max-width: 150px;
|
||||
}
|
||||
.Price__result {
|
||||
height: 50px;
|
||||
margin: 5px 0 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
color: #fff;
|
||||
height: 50px;
|
||||
margin: 5px 0 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
color: #fff;
|
||||
}
|
||||
.Price__result-success {
|
||||
background-color: #28a745;
|
||||
background-color: #28a745;
|
||||
}
|
||||
.Price__result-plus {
|
||||
background-color: #dc3545;
|
||||
background-color: #dc3545;
|
||||
}
|
||||
.Price__result-moins {
|
||||
background-color: #343a40;
|
||||
background-color: #343a40;
|
||||
}
|
@ -1,34 +1,34 @@
|
||||
.ManageToDo__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin: 40px 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin: 40px 40px;
|
||||
}
|
||||
.ManageToDo__list {
|
||||
overflow: hidden;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ManageToDo__list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 25px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 25px 0;
|
||||
}
|
||||
.ManageToDo__list-item.isCompleted {
|
||||
text-decoration: line-through;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.ManageToDo__task-btn {
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
width: 1.75em;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
width: 1.75em;
|
||||
}
|
||||
.ManageToDo__list-item-span {
|
||||
width: calc(100% - 120px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: calc(100% - 120px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
.Home__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.Home__logo-spin {
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
.Home__image-width {
|
||||
width: 13em;
|
||||
width: 13em;
|
||||
}
|
||||
div[aria-hidden="false"] {
|
||||
overflow: hidden !important;
|
||||
div[aria-hidden='false'] {
|
||||
overflow: hidden !important;
|
||||
}
|
@ -1,28 +1,28 @@
|
||||
.Profile__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin: 30px 0 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
.Profile__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin-bottom: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.Profile__logo {
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
.Profile__comment {
|
||||
margin: 0 0 50px 0;
|
||||
margin: 0 0 50px 0;
|
||||
}
|
||||
.Profile__Modal-top-container {
|
||||
margin: 20px 0;
|
||||
margin: 20px 0;
|
||||
}
|
@ -1,22 +1,22 @@
|
||||
.Register-Login__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.Register-Login__row {
|
||||
padding: 30px;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
padding: 30px;
|
||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid black;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
.Register-Login__title {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
.Register-Login__Forgot-password {
|
||||
color: var(--text-color);
|
||||
font-size: 16px;
|
||||
color: var(--text-color);
|
||||
font-size: 16px;
|
||||
}
|
||||
.Register-Login__Forgot-password:hover {
|
||||
color: var(--important);
|
||||
text-decoration: none;
|
||||
color: var(--important);
|
||||
text-decoration: none;
|
||||
}
|
@ -21,7 +21,7 @@ app.prepare().then(() => {
|
||||
})
|
||||
|
||||
/* Server */
|
||||
server.listen(PORT, (error) => {
|
||||
server.listen(PORT, error => {
|
||||
if (error) throw error
|
||||
console.log(`> Ready on http://localhost:${PORT}`)
|
||||
})
|
||||
|
Reference in New Issue
Block a user