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

Release/v2.2
This commit is contained in:
Divlo authored and GitHub committed 2020-10-30 17:28:58 +01:00
commit cbe82f74a9
38 files changed
+5051 -4509

No files matched your search

+1 -1
View File
@@ -29,4 +29,4 @@ INSERT INTO `functions` (`id`, `title`, `slug`, `description`, `image`, `type`,
(18, 'Quel est le mot le plus long ?', 'findLongestWord', 'Trouve le mot le plus long dans une chaîne de caractères.', '/images/functions/findLongestWord.png', 'form', '<p></p><div class=\"vscode-editor\" style=\"color: #d4d4d4;background-color: #1e1e1e;font-family: Consolas, \'Courier New\', monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-space: pre;\"><div><span style=\"color: #569cd6;\">function</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #dcdcaa;\">findLongestWord</span><span style=\"color: #d4d4d4;\">(</span><span style=\"color: #9cdcfe;\">string</span><span style=\"color: #d4d4d4;\">)&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #569cd6;\">const</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #51b6c4;\">arrayString</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #9cdcfe;\">string</span><span style=\"color: #d4d4d4;\">.</span><span style=\"color: #dcdcaa;\">split</span><span style=\"color: #d4d4d4;\">(</span><span style=\"color: #ce9178;\">\"&nbsp;\"</span><span style=\"color: #d4d4d4;\">);</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #569cd6;\">let</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">wordLength</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #b5cea8;\">0</span><span style=\"color: #d4d4d4;\">;</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #569cd6;\">let</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">wordResult</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #ce9178;\">\"\"</span><span style=\"color: #d4d4d4;\">;</span></div><br><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #9cdcfe;\">arrayString</span><span style=\"color: #d4d4d4;\">.</span><span style=\"color: #dcdcaa;\">forEach</span><span style=\"color: #d4d4d4;\">((</span><span style=\"color: #9cdcfe;\">word</span><span style=\"color: #d4d4d4;\">)&nbsp;</span><span style=\"color: #569cd6;\">=&gt;</span><span style=\"color: #d4d4d4;\">&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">if</span><span style=\"color: #d4d4d4;\">&nbsp;(</span><span style=\"color: #9cdcfe;\">word</span><span style=\"color: #d4d4d4;\">.</span><span style=\"color: #9cdcfe;\">length</span><span style=\"color: #d4d4d4;\">&nbsp;&gt;&nbsp;</span><span style=\"color: #9cdcfe;\">wordLength</span><span style=\"color: #d4d4d4;\">)&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #9cdcfe;\">wordResult</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #9cdcfe;\">word</span><span style=\"color: #d4d4d4;\">;</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #9cdcfe;\">wordLength</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #9cdcfe;\">word</span><span style=\"color: #d4d4d4;\">.</span><span style=\"color: #9cdcfe;\">length</span><span style=\"color: #d4d4d4;\">;</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;});</span></div><br><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">return</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">wordResult</span><span style=\"color: #d4d4d4;\">;</span></div><div><span style=\"color: #d4d4d4;\">}</span></div></div><p>On part du principe que chaque mot est séparé par des espaces. Au début, le mot le plus long a 0 caractère. Puis on fait une boucle qui vérifie si le mot qu\'on est en train de vérifier est plus long que le mot qui était jusqu\'à cette itération le plus long, si c\'est le cas, on met à jour les variables du mot le plus long, pour vérifier si c\'est encore le cas à la prochaine itération.</p>', '[{\"name\": \"string\", \"type\": \"text\", \"label\": \"Entrez la chaîne de caractères (phrase) à vérifier :\", \"placeholder\": \"(e.g : Une phrase quelconque.)\"}]', 1, '2020-05-06 20:29:05', '2020-05-08 07:23:23', 2),
(19, 'Suite de Fibonacci', 'fibonacci', 'Calcule la suite de Fibonacci.', '/images/functions/fibonacci.png', 'form', '<p>La suite de Fibonacci est une suite d\"entiers dans laquelle chaque terme est la somme des deux termes qui le précèdent, voici les 6 premiers chiffres de la suite :<br>\n\n\n\n\n\n0, 1, 1, 2, 3, 5</p><p>Nous allons coder une fonction récursive qui va nous calculer les \"x\" premiers nombres de la suite.</p><p>Pour commencer qu\"est ce qu\"une fonction récursive ? Une fonction récursive est une fonction qui s\"auto appelle dans le corps de la fonction, exemple :</p><div class=\"vscode-editor\" style=\"color: #d4d4d4;background-color: #1e1e1e;font-family: Consolas, \'Courier New\', monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-space: pre;\"><div><span style=\"color: #569cd6;\">function</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #dcdcaa;\">doSomeCode</span><span style=\"color: #d4d4d4;\">()&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #dcdcaa;\">doSomeCode</span><span style=\"color: #d4d4d4;\">();</span></div><div><span style=\"color: #d4d4d4;\">}</span></div><div><span style=\"color: #dcdcaa;\">doSomeCode</span><span style=\"color: #d4d4d4;\">();</span></div></div><p>Bon ici, ça n\"a pas trop de sens, car ça causerait \"une boucle infinie\" on obtiendra une \"InternalError: too much recursion\", par contre si on exécute la fonction ou on retourne une valeur d\"après des conditions, ça peut-être très intéressant.</p><div class=\"vscode-editor\" style=\"color: #d4d4d4;background-color: #1e1e1e;font-family: Consolas, \'Courier New\', monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-space: pre;\"><div><span style=\"color: #569cd6;\">function</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #dcdcaa;\">fibonacci</span><span style=\"color: #d4d4d4;\">(</span><span style=\"color: #9cdcfe;\">counter</span><span style=\"color: #d4d4d4;\">,&nbsp;</span><span style=\"color: #9cdcfe;\">result</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;[],&nbsp;</span><span style=\"color: #9cdcfe;\">number1</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #b5cea8;\">0</span><span style=\"color: #d4d4d4;\">,&nbsp;</span><span style=\"color: #9cdcfe;\">number2</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #b5cea8;\">1</span><span style=\"color: #d4d4d4;\">)&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">if</span><span style=\"color: #d4d4d4;\">&nbsp;(</span><span style=\"color: #9cdcfe;\">counter</span><span style=\"color: #d4d4d4;\">&nbsp;===&nbsp;</span><span style=\"color: #b5cea8;\">0</span><span style=\"color: #d4d4d4;\">)&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">return</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">result</span><span style=\"color: #d4d4d4;\">;&nbsp;</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #9cdcfe;\">counter</span><span style=\"color: #d4d4d4;\">--;</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #9cdcfe;\">result</span><span style=\"color: #d4d4d4;\">.</span><span style=\"color: #dcdcaa;\">push</span><span style=\"color: #d4d4d4;\">(</span><span style=\"color: #9cdcfe;\">number1</span><span style=\"color: #d4d4d4;\">);</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">return</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #dcdcaa;\">fibonacci</span><span style=\"color: #d4d4d4;\">(</span><span style=\"color: #9cdcfe;\">counter</span><span style=\"color: #d4d4d4;\">,&nbsp;</span><span style=\"color: #9cdcfe;\">result</span><span style=\"color: #d4d4d4;\">,&nbsp;</span><span style=\"color: #9cdcfe;\">number2</span><span style=\"color: #d4d4d4;\">,&nbsp;</span><span style=\"color: #9cdcfe;\">number1</span><span style=\"color: #d4d4d4;\">&nbsp;+&nbsp;</span><span style=\"color: #9cdcfe;\">number2</span><span style=\"color: #d4d4d4;\">);</span></div><div><span style=\"color: #d4d4d4;\">}</span></div></div><p>Cette fonction a en réalité un seul argument qui doit être passé lors de la première exécution, le \"counter\", le reste c\'est des arguments qu\"on passera dans le corps de la fonction quand on la réecutera.</p><p>On décrémente counter de -1 à chaque itération donc si counter est égale à 0 c\"est qu\"il faut retourner la suite de nombres sinon on peut continuer à push des nombres dans la liste.<br>\nPour rajouter des nombres dans la liste, il nous faut 2 variables (les Line truncated
INSERT INTO `functions` (`id`, `title`, `slug`, `description`, `image`, `type`, `article`, `utilizationForm`, `isOnline`, `createdAt`, `updatedAt`, `categorieId`) VALUES
(20, 'Trie les éléments d\'un tableau', 'sortArray', 'Trie les éléments d\'un tableau dans l\'ordre croissant.', '/images/functions/sortArray.png', 'form', '<p>Le but ici c\"est de recevoir un tableau de nombres et de pouvoir remettre dans l\"ordre croissant le tableau (exemple : [4, 12, 1, 8] devient [1, 4, 8, 12]).</p><p>Comme je le dis souvent, il faut penser un problème en petits autres problèmes plus facilement atteignable. Pour commencer, je décide de faire une fonction qui me retourne le nombre le plus petit d\"un tableau.<br>\n</p><div class=\"vscode-editor\" style=\"color: #d4d4d4;background-color: #1e1e1e;font-family: Consolas, \'Courier New\', monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-space: pre;\"><div><span style=\"color: #569cd6;\">function</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #dcdcaa;\">minNumber</span><span style=\"color: #d4d4d4;\">(</span><span style=\"color: #9cdcfe;\">array</span><span style=\"color: #d4d4d4;\">)&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #569cd6;\">let</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">minNumber</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;{&nbsp;</span><span style=\"color: #9cdcfe;\">index:</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #b5cea8;\">0</span><span style=\"color: #d4d4d4;\">,&nbsp;</span><span style=\"color: #9cdcfe;\">value:</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">array</span><span style=\"color: #d4d4d4;\">[</span><span style=\"color: #b5cea8;\">0</span><span style=\"color: #d4d4d4;\">]&nbsp;};</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">for</span><span style=\"color: #d4d4d4;\">&nbsp;(</span><span style=\"color: #569cd6;\">let</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #b5cea8;\">1</span><span style=\"color: #d4d4d4;\">;&nbsp;</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">&nbsp;&lt;&nbsp;</span><span style=\"color: #9cdcfe;\">array</span><span style=\"color: #d4d4d4;\">.</span><span style=\"color: #9cdcfe;\">length</span><span style=\"color: #d4d4d4;\">;&nbsp;</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">++)&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #569cd6;\">const</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #51b6c4;\">number</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #9cdcfe;\">array</span><span style=\"color: #d4d4d4;\">[</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">];</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">if</span><span style=\"color: #d4d4d4;\">&nbsp;(</span><span style=\"color: #9cdcfe;\">number</span><span style=\"color: #d4d4d4;\">&nbsp;&lt;&nbsp;</span><span style=\"color: #9cdcfe;\">minNumber</span><span style=\"color: #d4d4d4;\">.</span><span style=\"color: #9cdcfe;\">value</span><span style=\"color: #d4d4d4;\">)&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #9cdcfe;\">minNumber</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;{&nbsp;</span><span style=\"color: #9cdcfe;\">index:</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">,&nbsp;</span><span style=\"color: #9cdcfe;\">value:</span><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;</span><span style=\"color: #9cdcfe;\">array</span><span style=\"color: #d4d4d4;\">[</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">]&nbsp;};</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">return</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">minNumber</span><span style=\"color: #d4d4d4;\">;</span></div><div><span style=\"color: #d4d4d4;\">}</span></div></div><p>Cette fonction retourne un objet contenant la valeur ainsi que son index du nombre le plus petit du tableau passé en argument.</p><p>Ensuite on peut passer à la fonction sortArray :</p><div class=\"vscode-editor\" style=\"color: #d4d4d4;background-color: #1e1e1e;font-family: Consolas, \'Courier New\', monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-Line truncated
(20, 'Trie les éléments d\'un tableau', 'sortArray', 'Trie les éléments d\'un tableau dans l\'ordre croissant.', '/images/functions/sortArray.png', 'form', '<p>Le but ici c\"est de recevoir un tableau de nombres et de pouvoir remettre dans l\"ordre croissant le tableau (exemple : [4, 12, 1, 8] devient [1, 4, 8, 12]).</p><p>Comme je le dis souvent, il faut penser un problème en petits autres problèmes plus facilement atteignable. Pour commencer, je décide de faire une fonction qui me retourne le nombre le plus petit d\"un tableau.<br>\n</p><div class=\"vscode-editor\" style=\"color: #d4d4d4;background-color: #1e1e1e;font-family: Consolas, \'Courier New\', monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-space: pre;\"><div><span style=\"color: #569cd6;\">function</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #dcdcaa;\">minNumber</span><span style=\"color: #d4d4d4;\">(</span><span style=\"color: #9cdcfe;\">array</span><span style=\"color: #d4d4d4;\">)&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #569cd6;\">let</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">minNumber</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;{&nbsp;</span><span style=\"color: #9cdcfe;\">index:</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #b5cea8;\">0</span><span style=\"color: #d4d4d4;\">,&nbsp;</span><span style=\"color: #9cdcfe;\">value:</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">array</span><span style=\"color: #d4d4d4;\">[</span><span style=\"color: #b5cea8;\">0</span><span style=\"color: #d4d4d4;\">]&nbsp;};</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">for</span><span style=\"color: #d4d4d4;\">&nbsp;(</span><span style=\"color: #569cd6;\">let</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #b5cea8;\">1</span><span style=\"color: #d4d4d4;\">;&nbsp;</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">&nbsp;&lt;&nbsp;</span><span style=\"color: #9cdcfe;\">array</span><span style=\"color: #d4d4d4;\">.</span><span style=\"color: #9cdcfe;\">length</span><span style=\"color: #d4d4d4;\">;&nbsp;</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">++)&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #569cd6;\">const</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #51b6c4;\">number</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;</span><span style=\"color: #9cdcfe;\">array</span><span style=\"color: #d4d4d4;\">[</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">];</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">if</span><span style=\"color: #d4d4d4;\">&nbsp;(</span><span style=\"color: #9cdcfe;\">number</span><span style=\"color: #d4d4d4;\">&nbsp;&lt;&nbsp;</span><span style=\"color: #9cdcfe;\">minNumber</span><span style=\"color: #d4d4d4;\">.</span><span style=\"color: #9cdcfe;\">value</span><span style=\"color: #d4d4d4;\">)&nbsp;{</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #9cdcfe;\">minNumber</span><span style=\"color: #d4d4d4;\">&nbsp;=&nbsp;{&nbsp;</span><span style=\"color: #9cdcfe;\">index:</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">,&nbsp;</span><span style=\"color: #9cdcfe;\">value:</span><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;</span><span style=\"color: #9cdcfe;\">array</span><span style=\"color: #d4d4d4;\">[</span><span style=\"color: #9cdcfe;\">index</span><span style=\"color: #d4d4d4;\">]&nbsp;};</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><div><span style=\"color: #d4d4d4;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"color: #c586c0;\">return</span><span style=\"color: #d4d4d4;\">&nbsp;</span><span style=\"color: #9cdcfe;\">minNumber</span><span style=\"color: #d4d4d4;\">;</span></div><div><span style=\"color: #d4d4d4;\">}</span></div></div><p>Cette fonction retourne un objet contenant la valeur ainsi que son index du nombre le plus petit du tableau passé en argument.</p><p>Ensuite on peut passer à la fonction sortArray :</p><div class=\"vscode-editor\" style=\"color: #d4d4d4;background-color: #1e1e1e;font-family: Consolas, \'Courier New\', monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-Line truncated
+1 -1
View File
@@ -1 +1 @@
.github/backup
.github/backup
+1 -1
View File
@@ -5,4 +5,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+23 -5
View File
@@ -5,10 +5,9 @@
</p>
<p align="center">
<a href="https://gitmoji.carloscuesta.me/"><img src="https://camo.githubusercontent.com/2a4924a23bd9ef18afe793f4999b1b9ec474e48f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6769746d6f6a692d253230f09f989c253230f09f988d2d4646444436372e7376673f7374796c653d666c61742d737175617265" alt="Gitmoji"/></a>
<a href="https://standardjs.com"><img alt="JavaScript Style Guide" src="https://img.shields.io/badge/code_style-standard-brightgreen.svg"/></a>
<a href="https://conventionalcommits.org"><img src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg" alt="Conventional Commits" /></a>
<a href="./LICENSE"><img src="https://img.shields.io/badge/licence-MIT-blue.svg" alt="Licence MIT"/></a>
<img src="https://img.shields.io/github/stars/Divlo/FunctionProject?style=social" alt="Stars">
<br/> <br/>
<a href="https://function.divlo.fr/"><img src="https://raw.githubusercontent.com/Divlo/FunctionProject/master/.github/FunctionProject.png" alt="FunctionProject" /></a>
</p>
@@ -23,7 +22,7 @@ Si vous aimez le projet, vous pouvez aider à **le faire connaître** en utilisa
Les dernières versions publiées : [https://github.com/Divlo/FunctionProject/releases](https://github.com/Divlo/FunctionProject/releases)
Le projet est disponible sur [function.divlo.fr](https://function.divlo.fr/) (actuellement en version 2.1).
Le projet est disponible sur [function.divlo.fr](https://function.divlo.fr/) (actuellement en version 2.2).
## 🚀 Open Source
@@ -39,8 +38,9 @@ Si vous voulez avoir les données des catégories et des fonctions, vous pouvez
### Prérequis :
- NodeJS (et npm) → version récente
- Base de donnée MySQL → J'utilise Wamp ce qui me permet d'avoir phpmyadmin.
- [Node.js](https://nodejs.org/) >= 14
- [npm](https://www.npmjs.com/) >= 6
- [MySQL](https://www.mysql.com/) >= 5.7
### Commandes (à suivre dans l'ordre) :
@@ -62,6 +62,24 @@ Vous devrez ensuite configurer l'API en créant un fichier `.env` à la racine d
### Lancer l'environnement de développement :
#### Avec [docker](https://www.docker.com/) :
```sh
# Setup and run all the services for you
docker-compose up --build
```
**Services started :**
- api : `http://localhost:8080`
- s.divlo.fr : `http://localhost:7000`
- website : `http://localhost:3000`
- [phpmyadmin](https://www.phpmyadmin.net/) : `http://localhost:8000`
- [MailDev](https://maildev.github.io/maildev/) : `http://localhost:1080`
- [MySQL database](https://www.mysql.com/) (with PORT 3006)
#### Sans docker :
Dans deux terminals séparés :
- Lancer le front-end en allant dans `/website`
+2
View File
@@ -0,0 +1,2 @@
node_modules
build
+15 -12
View File
@@ -1,12 +1,15 @@
HOST = "http://localhost:8080"
FRONT_END_HOST = "http://localhost:3000"
OpenWeatherMap_API_KEY = ""
Scraper_API_KEY = ""
DB_HOST = ""
DB_NAME = ""
DB_USER = ""
DB_PASS = ""
JWT_SECRET = ""
EMAIL_HOST = ""
EMAIL_USER = ""
EMAIL_PASSWORD = ""
HOST="http://localhost:8080"
FRONT_END_HOST="http://localhost:3000"
OpenWeatherMap_API_KEY=""
Scraper_API_KEY=""
DATABASE_HOST="functionproject-database"
DATABASE_NAME="functionproject"
DATABASE_USER="root"
DATABASE_PASSWORD="password"
DATABASE_PORT=3306
JWT_SECRET=""
EMAIL_HOST="functionproject-maildev"
EMAIL_USER="no-reply@functionproject.fr"
EMAIL_PASSWORD="password"
EMAIL_PORT=25
COMPOSE_PROJECT_NAME="function.divlo.fr-api"
+4 -2
View File
@@ -11,14 +11,16 @@
# production
/build
# misc
.DS_Store
# envs
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.env.production
# misc
.DS_Store
/temp
/assets/images/
+13
View File
@@ -0,0 +1,13 @@
FROM node:14.15.0-alpine3.12
WORKDIR /app
COPY ./package*.json ./
RUN npm install
COPY ./ ./
# docker-compose-wait
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.7.3/wait /wait
RUN chmod +x /wait
CMD /wait && npm run dev
+20 -4
View File
@@ -6,6 +6,7 @@ const helmet = require('helmet')
const cors = require('cors')
const morgan = require('morgan')
const { redirectToHTTPS } = require('express-http-to-https')
const rateLimit = require('express-rate-limit')
/* Files Imports & Variables */
const sequelize = require('./assets/utils/database')
@@ -16,11 +17,27 @@ const isAdmin = require('./middlewares/isAdmin')
const app = express()
/* Middlewares */
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'))
} else if (process.env.NODE_ENV === 'production') {
app.use(redirectToHTTPS())
const requestPerSecond = 2
const seconds = 60
const windowMs = seconds * 1000
app.enable('trust proxy')
app.use(
rateLimit({
windowMs,
max: seconds * requestPerSecond,
handler: (_req, res) => {
return res.status(429).json({ message: 'Too many requests' })
}
})
)
}
app.use(helmet())
app.use(cors())
app.use(morgan('dev'))
app.use(express.json())
app.use(redirectToHTTPS([/localhost:(\d{4})/]))
/* Routes */
app.use('/images', express.static(path.join(__dirname, 'assets', 'images')))
@@ -37,7 +54,7 @@ app.use('/links', require('./routes/links_shortener'))
/* Errors Handling */
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
@@ -83,7 +100,6 @@ Users.hasMany(ShortLinks)
ShortLinks.belongsTo(Users, { constraints: false })
/* Server */
// sequelize.sync({ force: true })
sequelize
.sync()
.then(() => {
+14 -7
View File
@@ -1,3 +1,8 @@
const dotenv = require('dotenv')
dotenv.config()
const EMAIL_PORT = parseInt(process.env.EMAIL_PORT ?? '465', 10)
const config = {
PORT: process.env.PORT || 8080,
HOST: process.env.HOST,
@@ -5,20 +10,22 @@ const config = {
WEATHER_API_KEY: process.env.OpenWeatherMap_API_KEY,
SCRAPER_API_KEY: process.env.Scraper_API_KEY,
DATABASE: {
host: process.env.DB_HOST,
name: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASS
host: process.env.DATABASE_HOST,
name: process.env.DATABASE_NAME,
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
port: parseInt(process.env.DATABASE_PORT ?? '3306', 10)
},
JWT_SECRET: process.env.JWT_SECRET,
EMAIL_INFO: {
host: process.env.EMAIL_HOST,
port: 465,
secure: true, // true for 465, false for other ports
port: EMAIL_PORT,
secure: EMAIL_PORT === 465,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASSWORD
}
},
ignoreTLS: process.env.NODE_ENV !== 'production'
},
TOKEN_LIFE: '1 week'
}
+2 -1
View File
@@ -7,7 +7,8 @@ const sequelize = new Sequelize(
DATABASE.password,
{
dialect: 'mysql',
host: DATABASE.host
host: DATABASE.host,
port: DATABASE.port
}
)
+690 -966
View File
File diff suppressed because it is too large. Load diff
+23 -22
View File
@@ -1,37 +1,38 @@
{
"name": "api",
"version": "2.1.0",
"version": "2.2.0",
"description": "Backend REST API for FunctionProject",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"format": "standard \"./**/*.js\" --fix | snazzy || exit 0"
"format": "standard \"./**/*.js\" --fix | snazzy"
},
"dependencies": {
"axios": "^0.19.2",
"axios": "^0.21.0",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-fileupload": "^1.1.6",
"express-http-to-https": "^1.1.4",
"express-validator": "^6.4.0",
"helmet": "^3.21.3",
"jsdom": "^16.2.2",
"jsonwebtoken": "^8.5.1",
"moment": "^2.24.0",
"ms": "^2.1.2",
"mysql2": "^2.1.0",
"nodemailer": "^6.4.6",
"sequelize": "^5.21.5",
"smart-request-balancer": "^2.1.1",
"uuid": "^7.0.2",
"validator": "^13.0.0",
"dotenv": "^8.2.0",
"morgan": "^1.9.1"
"express": "^4.17.1",
"express-fileupload": "^1.2.0",
"express-http-to-https": "^1.1.4",
"express-rate-limit": "^5.1.3",
"express-validator": "^6.6.1",
"helmet": "^4.1.1",
"jsdom": "^16.4.0",
"jsonwebtoken": "^8.5.1",
"moment": "^2.29.1",
"morgan": "^1.10.0",
"ms": "^2.1.2",
"mysql2": "^2.2.5",
"nodemailer": "^6.4.14",
"sequelize": "^6.3.5",
"smart-request-balancer": "^2.1.1",
"uuid": "^8.3.1",
"validator": "^13.1.17"
},
"devDependencies": {
"nodemon": "^2.0.4",
"snazzy": "^8.0.0",
"standard": "^14.3.4"
"nodemon": "^2.0.6",
"snazzy": "^9.0.0",
"standard": "^16.0.0"
}
}
+73
View File
@@ -0,0 +1,73 @@
version: '3.0'
services:
functionproject-api:
build:
context: './api'
ports:
- '8080:8080'
depends_on:
- 'functionproject-database'
- 'functionproject-maildev'
volumes:
- './api:/app'
- '/app/node_modules'
environment:
WAIT_HOSTS: 'functionproject-database:3306'
container_name: 'functionproject-api'
s.divlo.fr-website:
build:
context: './s.divlo.fr'
ports:
- '7000:7000'
depends_on:
- 'functionproject-database'
volumes:
- './s.divlo.fr:/app'
- '/app/node_modules'
environment:
WAIT_HOSTS: 'functionproject-database:3306'
container_name: 's.divlo.fr-website'
functionproject-website:
build:
context: './website'
ports:
- '3000:3000'
volumes:
- './website:/app'
- '/app/node_modules'
container_name: 'functionproject-website'
functionproject-phpmyadmin:
image: 'phpmyadmin/phpmyadmin:5.0.2'
environment:
PMA_HOST: 'functionproject-database'
PMA_USER: 'root'
PMA_PASSWORD: 'password'
ports:
- '8000:80'
depends_on:
- 'functionproject-database'
container_name: 'functionproject-phpmyadmin'
functionproject-database:
image: 'mysql:5.7'
command: '--default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci'
environment:
MYSQL_ROOT_PASSWORD: 'password'
MYSQL_DATABASE: 'functionproject'
ports:
- '3306:3306'
volumes:
- 'database-volume:/var/lib/mysql'
container_name: 'functionproject-database'
functionproject-maildev:
image: 'maildev/maildev:1.1.0'
ports:
- '1080:80'
container_name: 'functionproject-maildev'
volumes:
database-volume:
+2
View File
@@ -0,0 +1,2 @@
node_modules
build
+6 -4
View File
@@ -1,4 +1,6 @@
DB_HOST = ""
DB_NAME = ""
DB_USER = ""
DB_PASS = ""
DATABASE_HOST="functionproject-database"
DATABASE_NAME="functionproject"
DATABASE_USER="root"
DATABASE_PASSWORD="password"
DATABASE_PORT=3306
COMPOSE_PROJECT_NAME="s.divlo.fr-website"
+1 -1
View File
@@ -1,2 +1,2 @@
node_modules
.env
.env
+13
View File
@@ -0,0 +1,13 @@
FROM node:14.15.0-alpine3.12
WORKDIR /app
COPY ./package*.json ./
RUN npm install
COPY ./ ./
# docker-compose-wait
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.7.3/wait /wait
RUN chmod +x /wait
CMD /wait && npm run dev
+11 -8
View File
@@ -10,18 +10,21 @@ const mysql = require('mysql')
/* Files Imports & Variables */
const app = express()
const database = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
port: process.env.DB_PORT
host: process.env.DATABASE_HOST,
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASS,
database: process.env.DATABASE_NAME,
port: process.env.DATABASE_PORT
})
/* Middlewares */
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'))
} else if (process.env.NODE_ENV === 'production') {
app.use(redirectToHTTPS())
}
app.use(helmet())
app.use(morgan('dev'))
app.use(express.json())
app.use(redirectToHTTPS([/localhost:(\d{4})/]))
/* EJS Template Engines */
app.set('view engine', 'ejs')
@@ -77,7 +80,7 @@ app.use((error, _req, res) => {
})
/* Server */
const PORT = process.env.PORT || 8000
const PORT = process.env.PORT || 7000
app.listen(PORT, () => {
console.log('\x1b[36m%s\x1b[0m', `Started on port ${PORT}.`)
})
+794 -913
View File
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -9,16 +9,16 @@
},
"dependencies": {
"dotenv": "^8.2.0",
"ejs": "^3.1.3",
"ejs": "^3.1.5",
"express": "^4.17.1",
"express-http-to-https": "^1.1.4",
"helmet": "^4.0.0",
"helmet": "^4.1.1",
"morgan": "^1.10.0",
"mysql": "^2.18.1"
},
"devDependencies": {
"nodemon": "^2.0.4",
"snazzy": "^8.0.0",
"standard": "^14.3.4"
"nodemon": "^2.0.6",
"snazzy": "^9.0.0",
"standard": "^16.0.0"
}
}
+2
View File
@@ -0,0 +1,2 @@
node_modules
build
+3 -1
View File
@@ -1 +1,3 @@
NEXT_PUBLIC_API_URL = "http://localhost:8080"
NEXT_PUBLIC_API_URL = "http://localhost:8080"
CONTAINER_API_URL="http://functionproject-api:8080"
COMPOSE_PROJECT_NAME="function.divlo.fr-website"
+9
View File
@@ -0,0 +1,9 @@
FROM node:14.15.0-alpine3.12
WORKDIR /app
COPY ./package*.json ./
RUN npm install
COPY ./ ./
CMD ["npm", "run", "dev"]
+1 -1
View File
@@ -8,7 +8,7 @@ export default function Footer () {
<Link href='/about'>
<a>FunctionProject</a>
</Link>
&nbsp;- Version 2.1 <br />
&nbsp;- Version 2.2 <br />
<a href='https://divlo.fr/' target='_blank' rel='noopener noreferrer'>
Divlo
</a>{' '}
@@ -23,15 +23,15 @@ const FunctionCard = memo(
<Link
{...(props.isAdmin
? {
href: '/admin/[slug]',
as: `/admin/${props.slug}`
}
href: '/admin/[slug]',
as: `/admin/${props.slug}`
}
: {
href: isFormOrArticle
? '/functions/[slug]'
: `/functions/${props.slug}`,
as: `/functions/${props.slug}`
})}
href: isFormOrArticle
? '/functions/[slug]'
: `/functions/${props.slug}`,
as: `/functions/${props.slug}`
})}
>
{/* FunctionCard a une hauteur pendant chargement */}
<a
@@ -82,62 +82,68 @@ const CommentCard = forwardRef((props, ref) => {
<a>{props.user.name}</a>
</Link>
&nbsp;-{' '}
{date.format(new Date(props.createdAt), 'DD/MM/YYYY à HH:mm', false)}
{date.format(
new Date(props.createdAt),
'DD/MM/YYYY à HH:mm',
false
)}
</span>
</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>
{!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>
&nbsp;-&nbsp;
<a style={{ cursor: 'pointer' }} onClick={editComment}>
modifier
</a>
</p>
<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>
)}
</>
) : (
<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,11 +3,13 @@ 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>
)
}
@@ -90,36 +90,38 @@ 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>
+27 -25
View File
@@ -51,35 +51,37 @@ export default function Header () {
<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 ${
{!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>
</>
)}
>
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' />
)}
+2494 -1758
View File
File diff suppressed because it is too large. Load diff
+27 -25
View File
@@ -1,47 +1,49 @@
{
"name": "website",
"version": "2.1.0",
"version": "2.2.0",
"description": "Website frontend for FunctionProject",
"main": "server.js",
"scripts": {
"dev": "cross-env NODE_ENV=development node server",
"dev:custom": "cross-env NODE_ENV=development node server",
"start:custom": "cross-env NODE_ENV=production node server",
"dev": "next",
"start": "next start",
"build": "next build",
"export": "next export",
"start": "cross-env NODE_ENV=production node server",
"format": "standard \"./**/*.{js,jsx}\" --fix | snazzy || exit 0"
"format": "standard \"./**/*.{js,jsx}\" --fix | snazzy"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.28",
"@fortawesome/free-brands-svg-icons": "^5.13.0",
"@fortawesome/free-regular-svg-icons": "^5.13.0",
"@fortawesome/free-solid-svg-icons": "^5.13.0",
"@fortawesome/react-fontawesome": "^0.1.9",
"@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-brands-svg-icons": "^5.15.1",
"@fortawesome/free-regular-svg-icons": "^5.15.1",
"@fortawesome/free-solid-svg-icons": "^5.15.1",
"@fortawesome/react-fontawesome": "^0.1.12",
"@zeit/next-css": "^1.0.1",
"axios": "^0.19.2",
"date-and-time": "^0.13.1",
"date-fns": "^2.12.0",
"axios": "^0.21.0",
"date-and-time": "^0.14.1",
"date-fns": "^2.16.1",
"express": "^4.17.1",
"express-http-to-https": "^1.1.4",
"html-react-parser": "^0.10.2",
"next": "^9.5.4",
"next-fonts": "^1.0.3",
"notyf": "^3.6.0",
"html-react-parser": "^0.14.0",
"next": "^10.0.0",
"next-fonts": "^1.4.0",
"notyf": "^3.9.0",
"nprogress": "^0.2.0",
"react": "16.13.0",
"react": "17.0.1",
"react-codepen-embed": "^1.0.1",
"react-color": "^2.18.0",
"react-datepicker": "^2.14.1",
"react-dom": "16.13.0",
"react-markdown": "^4.3.1",
"react-color": "^2.19.3",
"react-datepicker": "^3.3.0",
"react-dom": "17.0.1",
"react-markdown": "^5.0.2",
"react-swipeable-views": "^0.13.9",
"react-swipeable-views-utils": "^0.13.9",
"react-syntax-highlighter": "^12.2.1",
"react-syntax-highlighter": "^15.3.0",
"suneditor-react": "^2.8.0",
"universal-cookie": "^4.0.3"
"universal-cookie": "^4.0.4"
},
"devDependencies": {
"cross-env": "^7.0.2",
"snazzy": "^8.0.0",
"standard": "^14.3.4"
"snazzy": "^9.0.0",
"standard": "^16.0.0"
}
}
+54 -52
View File
@@ -23,64 +23,66 @@ const Admin = props => {
/>
{/* Création d'une fonction */}
{isOpen ? (
<Modal toggleModal={toggleModal}>
<div className='Admin__Modal__container container-fluid'>
<div className='Admin__Modal__row row'>
<div className='col-24'>
<div className='Admin__Modal-top-container row'>
<div className='col-24'>
<span
onClick={toggleModal}
style={{
cursor: 'pointer',
position: 'absolute',
left: 0
}}
>
<FontAwesomeIcon
icon={faTimes}
style={{ width: '1.5rem', color: 'red' }}
/>
</span>
<h2 className='text-center'>Crée une nouvelle fonction</h2>
{isOpen
? (
<Modal toggleModal={toggleModal}>
<div className='Admin__Modal__container container-fluid'>
<div className='Admin__Modal__row row'>
<div className='col-24'>
<div className='Admin__Modal-top-container row'>
<div className='col-24'>
<span
onClick={toggleModal}
style={{
cursor: 'pointer',
position: 'absolute',
left: 0
}}
>
<FontAwesomeIcon
icon={faTimes}
style={{ width: '1.5rem', color: 'red' }}
/>
</span>
<h2 className='text-center'>Crée une nouvelle fonction</h2>
</div>
</div>
</div>
</div>
<div className='col-24'>
<AddEditFunction
defaultInputState={{ type: 'form' }}
{...props}
/>
<div className='col-24'>
<AddEditFunction
defaultInputState={{ type: 'form' }}
{...props}
/>
</div>
</div>
</div>
</div>
</Modal>
) : (
<FunctionsList isAdmin token={props.user.token}>
<div className='col-24'>
<h1 className='Functions__title'>Administration</h1>
<button
onClick={toggleModal}
style={{ margin: '0 0 40px 0' }}
className='btn btn-dark'
>
Crée une nouvelle fonction
</button>
<Link href='/admin/manageCategories'>
<button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'>
Gérer les catégories
</Modal>
)
: (
<FunctionsList isAdmin token={props.user.token}>
<div className='col-24'>
<h1 className='Functions__title'>Administration</h1>
<button
onClick={toggleModal}
style={{ margin: '0 0 40px 0' }}
className='btn btn-dark'
>
Crée une nouvelle fonction
</button>
</Link>
<Link href='/admin/manageQuotes'>
<button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'>
Gérer les citations
</button>
</Link>
</div>
</FunctionsList>
)}
<Link href='/admin/manageCategories'>
<button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'>
Gérer les catégories
</button>
</Link>
<Link href='/admin/manageQuotes'>
<button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'>
Gérer les citations
</button>
</Link>
</div>
</FunctionsList>
)}
</>
)
}
+110 -108
View File
@@ -159,119 +159,121 @@ const manageCategories = props => {
description="Page d'administration de FunctionProject. Gérer les catégories."
/>
{isOpen ? (
<Modal>
<AddEditCategory
handleToggleModal={toggleModal}
defaultInputState={defaultInputState}
{...props}
isEditing={isEditing}
/>
</Modal>
) : (
<div className='container-fluid text-center'>
<div className='row justify-content-center'>
<div className='col-24'>
<h1>Gérer les catégories</h1>
<button
onClick={() => {
setDefaultInputState(defaultCategoryState)
toggleModal()
setIsEditing(false)
}}
style={{ margin: '0 0 40px 0' }}
className='btn btn-dark'
>
Ajouter une catégorie
</button>
{isOpen
? (
<Modal>
<AddEditCategory
handleToggleModal={toggleModal}
defaultInputState={defaultInputState}
{...props}
isEditing={isEditing}
/>
</Modal>
)
: (
<div className='container-fluid text-center'>
<div className='row justify-content-center'>
<div className='col-24'>
<h1>Gérer les catégories</h1>
<button
onClick={() => {
setDefaultInputState(defaultCategoryState)
toggleModal()
setIsEditing(false)
}}
style={{ margin: '0 0 40px 0' }}
className='btn btn-dark'
>
Ajouter une catégorie
</button>
</div>
</div>
</div>
<div className='row justify-content-center'>
<div className='container-fluid'>
<div className='col-24 table-column'>
<table className='table'>
<thead>
<tr>
<th className='table-row' scope='col'>
id
</th>
<th className='table-row' scope='col'>
name
</th>
<th className='table-row' scope='col'>
color
</th>
<th className='table-row' scope='col'>
createdAt
</th>
<th className='table-row' scope='col'>
updatedAt
</th>
<th className='table-row' scope='col'>
Modifier
</th>
<th className='table-row' scope='col'>
Supprimer
</th>
</tr>
</thead>
<tbody>
{categories.map(category => {
return (
<tr
key={category.id}
style={{ backgroundColor: category.color }}
>
<td className='table-row'>{category.id}</td>
<td className='table-row'>{category.name}</td>
<td className='table-row'>{category.color}</td>
<td className='table-row'>
{date.format(
new Date(category.createdAt),
'DD/MM/YYYY à HH:mm',
false
)}
</td>
<td className='table-row'>
{date.format(
new Date(category.updatedAt),
'DD/MM/YYYY à HH:mm',
false
)}
</td>
<td
style={{ cursor: 'pointer' }}
onClick={() =>
handleEditCategory({
name: category.name,
color: category.color,
id: category.id
})}
<div className='row justify-content-center'>
<div className='container-fluid'>
<div className='col-24 table-column'>
<table className='table'>
<thead>
<tr>
<th className='table-row' scope='col'>
id
</th>
<th className='table-row' scope='col'>
name
</th>
<th className='table-row' scope='col'>
color
</th>
<th className='table-row' scope='col'>
createdAt
</th>
<th className='table-row' scope='col'>
updatedAt
</th>
<th className='table-row' scope='col'>
Modifier
</th>
<th className='table-row' scope='col'>
Supprimer
</th>
</tr>
</thead>
<tbody>
{categories.map(category => {
return (
<tr
key={category.id}
style={{ backgroundColor: category.color }}
>
<FontAwesomeIcon
icon={faPen}
style={{ width: '1.5rem' }}
/>
</td>
<td
style={{ cursor: 'pointer' }}
onClick={() => handleRemoveCategory(category.id)}
>
<FontAwesomeIcon
icon={faTrash}
style={{ width: '1.5rem' }}
/>
</td>
</tr>
)
})}
</tbody>
</table>
<td className='table-row'>{category.id}</td>
<td className='table-row'>{category.name}</td>
<td className='table-row'>{category.color}</td>
<td className='table-row'>
{date.format(
new Date(category.createdAt),
'DD/MM/YYYY à HH:mm',
false
)}
</td>
<td className='table-row'>
{date.format(
new Date(category.updatedAt),
'DD/MM/YYYY à HH:mm',
false
)}
</td>
<td
style={{ cursor: 'pointer' }}
onClick={() =>
handleEditCategory({
name: category.name,
color: category.color,
id: category.id
})}
>
<FontAwesomeIcon
icon={faPen}
style={{ width: '1.5rem' }}
/>
</td>
<td
style={{ cursor: 'pointer' }}
onClick={() => handleRemoveCategory(category.id)}
>
<FontAwesomeIcon
icon={faTrash}
style={{ width: '1.5rem' }}
/>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
)}
)}
</>
)
}
+130 -128
View File
@@ -266,144 +266,146 @@ const LinksList = ({
</div>
<div className='row justify-content-center'>
<div className='container-fluid'>
{!isEditing ? (
<div className='col-24 table-column'>
<table className='table' style={{ marginBottom: '40px' }}>
<thead>
<tr>
<th className='table-row' scope='col'>
Liens
</th>
<th className='table-row' scope='col'>
Nom
</th>
<th className='table-row' scope='col'>
Compteur de clics
</th>
<th className='table-row' scope='col'>
Modifier
</th>
<th className='table-row' scope='col'>
Supprimer
</th>
</tr>
</thead>
<tbody>
{linksData.rows.map((link, index) => {
const linkJSX = (
<>
<td className='table-row'>
<a href={link.url}>{link.url}</a>
</td>
<td className='table-row'>
<a href={`https://s.divlo.fr/${link.shortcut}`}>
{link.shortcut}
</a>
</td>
<td className='table-row'>{link.count}</td>
<td
style={{ cursor: 'pointer' }}
onClick={() => handleEditLink(link)}
>
<FontAwesomeIcon
icon={faPen}
style={{ width: '1.5rem' }}
/>
</td>
<td
style={{ cursor: 'pointer' }}
onClick={() => handleRemoveLink(link.id)}
>
<FontAwesomeIcon
icon={faTrash}
style={{ width: '1.5rem' }}
/>
</td>
</>
)
// Si c'est le dernier élément
if (linksData.rows.length === index + 1) {
return (
<tr key={index} ref={lastLinkRef}>
{linkJSX}
</tr>
{!isEditing
? (
<div className='col-24 table-column'>
<table className='table' style={{ marginBottom: '40px' }}>
<thead>
<tr>
<th className='table-row' scope='col'>
Liens
</th>
<th className='table-row' scope='col'>
Nom
</th>
<th className='table-row' scope='col'>
Compteur de clics
</th>
<th className='table-row' scope='col'>
Modifier
</th>
<th className='table-row' scope='col'>
Supprimer
</th>
</tr>
</thead>
<tbody>
{linksData.rows.map((link, index) => {
const linkJSX = (
<>
<td className='table-row'>
<a href={link.url}>{link.url}</a>
</td>
<td className='table-row'>
<a href={`https://s.divlo.fr/${link.shortcut}`}>
{link.shortcut}
</a>
</td>
<td className='table-row'>{link.count}</td>
<td
style={{ cursor: 'pointer' }}
onClick={() => handleEditLink(link)}
>
<FontAwesomeIcon
icon={faPen}
style={{ width: '1.5rem' }}
/>
</td>
<td
style={{ cursor: 'pointer' }}
onClick={() => handleRemoveLink(link.id)}
>
<FontAwesomeIcon
icon={faTrash}
style={{ width: '1.5rem' }}
/>
</td>
</>
)
}
return <tr key={index}>{linkJSX}</tr>
})}
</tbody>
</table>
</div>
) : (
<Modal>
<div className='Admin__Modal__container container-fluid'>
<div className='Admin__Modal__row row'>
<div className='col-24'>
<div className='Admin__Modal-top-container row'>
<div className='col-24'>
<span
onClick={toggleModal}
style={{
cursor: 'pointer',
position: 'absolute',
left: 0
}}
>
<FontAwesomeIcon
icon={faTimes}
style={{ width: '1.5rem', color: 'red' }}
/>
</span>
// Si c'est le dernier élément
if (linksData.rows.length === index + 1) {
return (
<tr key={index} ref={lastLinkRef}>
{linkJSX}
</tr>
)
}
return <tr key={index}>{linkJSX}</tr>
})}
</tbody>
</table>
</div>
)
: (
<Modal>
<div className='Admin__Modal__container container-fluid'>
<div className='Admin__Modal__row row'>
<div className='col-24'>
<div className='Admin__Modal-top-container row'>
<div className='col-24'>
<span
onClick={toggleModal}
style={{
cursor: 'pointer',
position: 'absolute',
left: 0
}}
>
<FontAwesomeIcon
icon={faTimes}
style={{ width: '1.5rem', color: 'red' }}
/>
</span>
</div>
</div>
</div>
</div>
<div className='col-24'>
<form onSubmit={handleEditSubmit}>
<div className='form-group'>
<label className='form-label' htmlFor='url'>
Entrez le lien à raccourcir :
</label>
<input
value={defaultInputState.url}
onChange={handleChange}
type='text'
name='url'
id='url'
placeholder='(e.g : https://divlo.fr)'
className='form-control'
/>
</div>
<div className='col-24'>
<form onSubmit={handleEditSubmit}>
<div className='form-group'>
<label className='form-label' htmlFor='url'>
Entrez le lien à raccourcir :
</label>
<input
value={defaultInputState.url}
onChange={handleChange}
type='text'
name='url'
id='url'
placeholder='(e.g : https://divlo.fr)'
className='form-control'
/>
</div>
<div className='form-group'>
<label className='form-label' htmlFor='shortcutName'>
Entrez le nom du raccourci :
</label>
<input
value={defaultInputState.shortcutName}
onChange={handleChange}
type='text'
name='shortcutName'
id='shortcutName'
placeholder='(e.g : divlo)'
className='form-control'
/>
</div>
<div className='form-group'>
<label className='form-label' htmlFor='shortcutName'>
Entrez le nom du raccourci :
</label>
<input
value={defaultInputState.shortcutName}
onChange={handleChange}
type='text'
name='shortcutName'
id='shortcutName'
placeholder='(e.g : divlo)'
className='form-control'
/>
</div>
<div className='form-group text-center'>
<button type='submit' className='btn btn-dark'>
Envoyer
</button>
<div className='form-group text-center'>
<button type='submit' className='btn btn-dark'>
Envoyer
</button>
</div>
</form>
<div className='form-result text-center'>
{isLoading ? <Loader /> : htmlParser(message)}
</div>
</form>
<div className='form-result text-center'>
{isLoading ? <Loader /> : htmlParser(message)}
</div>
</div>
</div>
</div>
</Modal>
)}
</Modal>
)}
</div>
</div>
</div>
+97 -91
View File
@@ -54,104 +54,110 @@ const PlayRightPrice = () => {
return (
<div className='container-fluid'>
{!isPlaying ? (
<div className='row justify-content-center'>
<div className='form-group text-center'>
<button
onClick={handlePlaying}
type='submit'
className='btn btn-dark'
>
Jouer
</button>
</div>
</div>
) : isLoadingProduct ? (
<div className='row justify-content-center'>
<Loader />
</div>
) : (
<>
{!isPlaying
? (
<div className='row justify-content-center'>
<div
style={{ marginBottom: '20px' }}
className='col-24 text-center'
>
<h4>{productToGuess.name}</h4>
<img
src={productToGuess.image}
alt={productToGuess.name}
className='Product__image'
/>
<div className='form-group text-center'>
<button
onClick={handlePlaying}
type='submit'
className='btn btn-dark'
>
Jouer
</button>
</div>
</div>
<div className='row justify-content-center'>
<div style={{ marginBottom: '25px' }} className='col-24'>
{attemptsArray.length > 0 &&
attemptsArray[0].message ===
'Bravo, vous avez trouvé le juste prix !' ? (
<div className='form-group text-center'>
<button
onClick={handlePlaying}
type='submit'
className='btn btn-dark'
>
Rejouer ?
</button>
</div>
) : (
<form onSubmit={handleSubmit}>
<div className='text-center'>
<input
value={enteredPrice}
onChange={handleChange}
name='enteredPrice'
id='enteredPrice'
type='number'
step='0.01'
className='form-control'
autoComplete='off'
placeholder='Devinez le prix (prix à virgule possible!)'
/>
</div>
<div className='form-group text-center'>
<button type='submit' className='btn btn-dark'>
Deviner
</button>
</div>
</form>
)}
)
: isLoadingProduct
? (
<div className='row justify-content-center'>
<Loader />
</div>
</div>
<div
style={{ marginBottom: '30px' }}
className='row justify-content-center'
>
{attemptsArray.map((attempt, index) => {
const { message } = attempt
let priceResultClass
if (message === "C'est moins !") {
priceResultClass = 'Price__result-moins'
} else if (message === "C'est plus !") {
priceResultClass = 'Price__result-plus'
} else {
priceResultClass = 'Price__result-success'
}
return (
)
: (
<>
<div className='row justify-content-center'>
<div
key={index}
className={`col-24 Price__result ${priceResultClass}`}
style={{ marginBottom: '20px' }}
className='col-24 text-center'
>
# {attempt.numberTry} ({attempt.guessedPrice}) {message}
<h4>{productToGuess.name}</h4>
<img
src={productToGuess.image}
alt={productToGuess.name}
className='Product__image'
/>
</div>
)
})}
</div>
</>
)}
</div>
<div className='row justify-content-center'>
<div style={{ marginBottom: '25px' }} className='col-24'>
{attemptsArray.length > 0 &&
attemptsArray[0].message ===
'Bravo, vous avez trouvé le juste prix !'
? (
<div className='form-group text-center'>
<button
onClick={handlePlaying}
type='submit'
className='btn btn-dark'
>
Rejouer ?
</button>
</div>
)
: (
<form onSubmit={handleSubmit}>
<div className='text-center'>
<input
value={enteredPrice}
onChange={handleChange}
name='enteredPrice'
id='enteredPrice'
type='number'
step='0.01'
className='form-control'
autoComplete='off'
placeholder='Devinez le prix (prix à virgule possible!)'
/>
</div>
<div className='form-group text-center'>
<button type='submit' className='btn btn-dark'>
Deviner
</button>
</div>
</form>
)}
</div>
</div>
<div
style={{ marginBottom: '30px' }}
className='row justify-content-center'
>
{attemptsArray.map((attempt, index) => {
const { message } = attempt
let priceResultClass
if (message === "C'est moins !") {
priceResultClass = 'Price__result-moins'
} else if (message === "C'est plus !") {
priceResultClass = 'Price__result-plus'
} else {
priceResultClass = 'Price__result-success'
}
return (
<div
key={index}
className={`col-24 Price__result ${priceResultClass}`}
>
# {attempt.numberTry} ({attempt.guessedPrice}) {message}
</div>
)
})}
</div>
</>
)}
</div>
)
}
+270 -268
View File
@@ -84,299 +84,301 @@ const Profile = props => {
/>
{/* Édition du profil */}
{isOpen ? (
<Modal toggleModal={toggleModal}>
<div className='Profile__container container-fluid'>
<div className='Profile__row row'>
<div className='col-24'>
<div className='Profile__Modal-top-container row'>
<div className='col-24'>
<span
{isOpen
? (
<Modal toggleModal={toggleModal}>
<div className='Profile__container container-fluid'>
<div className='Profile__row row'>
<div className='col-24'>
<div className='Profile__Modal-top-container row'>
<div className='col-24'>
<span
onClick={toggleModal}
style={{
cursor: 'pointer',
position: 'absolute',
left: 0
}}
>
<FontAwesomeIcon
icon={faTimes}
style={{ width: '1.5rem', color: 'red' }}
/>
</span>
<h2 className='text-center'>Éditer le profil</h2>
<p className='text-center'>
<em>
(Vous devrez vous reconnecter après la sauvegarde){' '}
<br /> Si vous changez votre adresse email, vous devrez
la confirmer comme à l'inscription (vérifier vos
emails).
</em>
</p>
</div>
</div>
</div>
<div className='col-24'>
<form onSubmit={handleSubmit}>
<div className='form-group'>
<label className='form-label' htmlFor='name'>
Nom :
</label>
<input
value={inputState.name}
onChange={handleChange}
type='text'
name='name'
id='name'
className='form-control'
placeholder='Divlo'
/>
</div>
<div className='form-group'>
<label className='form-label' htmlFor='email'>
Email :
</label>
<input
value={inputState.email}
onChange={handleChange}
type='email'
name='email'
id='email'
className='form-control'
placeholder='email@gmail.com'
/>
</div>
<div className='form-group custom-control custom-switch'>
<input
onChange={event => handleChange(event, true)}
type='checkbox'
name='isPublicEmail'
checked={inputState.isPublicEmail}
className='custom-control-input'
id='isPublicEmail'
/>
<label
className='custom-control-label'
htmlFor='isPublicEmail'
>
Email Public
</label>
</div>
<div className='form-group'>
<label className='form-label' htmlFor='biography'>
Biographie :
</label>
<textarea
style={{ height: 'auto' }}
value={inputState.biography}
onChange={handleChange}
name='biography'
id='biography'
className='form-control'
rows='5'
/>
</div>
<div className='form-group'>
<label className='form-label' htmlFor='logo'>
Logo <em>(400x400 recommandé)</em> :
</label>
<p style={{ margin: 0 }}>
<em>
Si aucun fichier est choisi, le logo ne sera pas
modifié.
</em>
</p>
<input
onChange={handleChange}
accept='image/jpeg,image/jpg,image/png,image/gif'
type='file'
name='logo'
id='logo'
/>
</div>
<div className='form-group text-center'>
<button type='submit' className='btn btn-dark'>
Sauvegarder
</button>
</div>
</form>
<div className='form-result text-center'>
{isLoading ? <Loader /> : htmlParser(message)}
</div>
</div>
</div>
</div>
</Modal>
)
: (
<div className='container-fluid Profile__container'>
<div className='row Profile__row'>
<div className='col-20'>
<div className='text-center'>
<h1>
Profil de <span className='important'>{props.name}</span>
</h1>
</div>
<div className='row justify-content-center'>
<div className='col-24 text-center'>
<img
className='Profile__logo'
src={API_URL + props.logo}
alt={props.name}
/>
</div>
<div className='col-24 text-center'>
{props.biography != null && <p>{props.biography}</p>}
{props.email != null && (
<p>
<span className='important'>Email :</span> {props.email}
</p>
)}
<p>
<span className='important'>
Date de création du compte :
</span>{' '}
{date.format(
new Date(props.createdAt),
'DD/MM/YYYY à HH:mm',
false
)}
</p>
</div>
{isAuth && user.name === props.name && (
<button
onClick={toggleModal}
style={{
cursor: 'pointer',
position: 'absolute',
left: 0
}}
style={{ marginBottom: '25px' }}
className='btn btn-dark'
>
<FontAwesomeIcon
icon={faTimes}
style={{ width: '1.5rem', color: 'red' }}
icon={faPen}
style={{ cursor: 'pointer', width: '1rem' }}
/>
</span>
<h2 className='text-center'>Éditer le profil</h2>
<p className='text-center'>
<em>
(Vous devrez vous reconnecter après la sauvegarde){' '}
<br /> Si vous changez votre adresse email, vous devrez
la confirmer comme à l'inscription (vérifier vos
emails).
</em>
</p>
</div>
</div>
</div>
<div className='col-24'>
<form onSubmit={handleSubmit}>
<div className='form-group'>
<label className='form-label' htmlFor='name'>
Nom :
</label>
<input
value={inputState.name}
onChange={handleChange}
type='text'
name='name'
id='name'
className='form-control'
placeholder='Divlo'
/>
</div>
<div className='form-group'>
<label className='form-label' htmlFor='email'>
Email :
</label>
<input
value={inputState.email}
onChange={handleChange}
type='email'
name='email'
id='email'
className='form-control'
placeholder='email@gmail.com'
/>
</div>
<div className='form-group custom-control custom-switch'>
<input
onChange={event => handleChange(event, true)}
type='checkbox'
name='isPublicEmail'
checked={inputState.isPublicEmail}
className='custom-control-input'
id='isPublicEmail'
/>
<label
className='custom-control-label'
htmlFor='isPublicEmail'
>
Email Public
</label>
</div>
<div className='form-group'>
<label className='form-label' htmlFor='biography'>
Biographie :
</label>
<textarea
style={{ height: 'auto' }}
value={inputState.biography}
onChange={handleChange}
name='biography'
id='biography'
className='form-control'
rows='5'
/>
</div>
<div className='form-group'>
<label className='form-label' htmlFor='logo'>
Logo <em>(400x400 recommandé)</em> :
</label>
<p style={{ margin: 0 }}>
<em>
Si aucun fichier est choisi, le logo ne sera pas
modifié.
</em>
</p>
<input
onChange={handleChange}
accept='image/jpeg,image/jpg,image/png,image/gif'
type='file'
name='logo'
id='logo'
/>
</div>
<div className='form-group text-center'>
<button type='submit' className='btn btn-dark'>
Sauvegarder
&nbsp; Éditez le profil
</button>
</div>
</form>
<div className='form-result text-center'>
{isLoading ? <Loader /> : htmlParser(message)}
)}
</div>
</div>
</div>
</div>
</Modal>
) : (
<div className='container-fluid Profile__container'>
<div className='row Profile__row'>
<div className='col-20'>
<div className='text-center'>
<h1>
Profil de <span className='important'>{props.name}</span>
</h1>
</div>
{props.favoritesArray.length > 0 && (
<div className='row justify-content-center'>
<div className='col-24 text-center'>
<img
className='Profile__logo'
src={API_URL + props.logo}
alt={props.name}
/>
<h2>
Dernières fonctions ajoutées aux{' '}
<span className='important'>favoris</span> :
</h2>
</div>
<div className='col-24'>
<div className='row justify-content-center'>
{props.favoritesArray.map(favorite => {
return <FunctionCard key={favorite.id} {...favorite} />
})}
</div>
</div>
</div>
)}
{props.commentsArray.length > 0 && (
<div className='row justify-content-center'>
<div className='col-24 text-center'>
{props.biography != null && <p>{props.biography}</p>}
{props.email != null && (
<p>
<span className='important'>Email :</span> {props.email}
</p>
)}
<p>
<span className='important'>
Date de création du compte :
</span>{' '}
{date.format(
new Date(props.createdAt),
'DD/MM/YYYY à HH:mm',
false
)}
</p>
<h2>
Derniers <span className='important'>commentaires</span> :
</h2>
</div>
{isAuth && user.name === props.name && (
<button
onClick={toggleModal}
style={{ marginBottom: '25px' }}
className='btn btn-dark'
>
<FontAwesomeIcon
icon={faPen}
style={{ cursor: 'pointer', width: '1rem' }}
/>
&nbsp; Éditez le profil
</button>
)}
</div>
</div>
</div>
{props.favoritesArray.length > 0 && (
<div className='row justify-content-center'>
<div className='col-24 text-center'>
<h2>
Dernières fonctions ajoutées aux{' '}
<span className='important'>favoris</span> :
</h2>
</div>
<div className='col-24'>
<div className='row justify-content-center'>
{props.favoritesArray.map(favorite => {
return <FunctionCard key={favorite.id} {...favorite} />
})}
</div>
</div>
</div>
)}
{props.commentsArray.length > 0 && (
<div className='row justify-content-center'>
<div className='col-24 text-center'>
<h2>
Derniers <span className='important'>commentaires</span> :
</h2>
</div>
<div className='col-24'>
{props.commentsArray.map(comment => (
<div
key={comment.id}
className='row Profile__row Profile__comment'
>
<div className='col-20'>
<p style={{ textAlign: 'center', marginTop: '30px' }}>
Posté sur la fonction&nbsp;
<Link
href={
<div className='col-24'>
{props.commentsArray.map(comment => (
<div
key={comment.id}
className='row Profile__row Profile__comment'
>
<div className='col-20'>
<p style={{ textAlign: 'center', marginTop: '30px' }}>
Posté sur la fonction&nbsp;
<Link
href={
comment.function.type === 'form' ||
comment.function.type === 'article'
? '/functions/[slug]'
: `/functions/${comment.function.slug}`
}
as={`/functions/${comment.function.slug}`}
>
<a>{comment.function.title}</a>
</Link>
as={`/functions/${comment.function.slug}`}
>
<a>{comment.function.title}</a>
</Link>
&nbsp;le{' '}
{date.format(
new Date(comment.createdAt),
'DD/MM/YYYY à HH:mm',
false
)}
</p>
<ReactMarkdown
source={comment.message}
renderers={{ code: CodeBlock }}
/>
{date.format(
new Date(comment.createdAt),
'DD/MM/YYYY à HH:mm',
false
)}
</p>
<ReactMarkdown
source={comment.message}
renderers={{ code: CodeBlock }}
/>
</div>
</div>
</div>
))}
))}
</div>
</div>
</div>
)}
)}
{props.quotesArray.length > 0 && (
<div className='row justify-content-center'>
<div className='col-24 text-center'>
<h2>
Dernières <span className='important'>citations</span>{' '}
proposées (et validées) :
</h2>
<p>
Citations pour la fonction{' '}
<Link href='/functions/randomQuote'>
<a>Générateur de citations</a>
</Link>
.
</p>
{props.quotesArray.length > 0 && (
<div className='row justify-content-center'>
<div className='col-24 text-center'>
<h2>
Dernières <span className='important'>citations</span>{' '}
proposées (et validées) :
</h2>
<p>
Citations pour la fonction{' '}
<Link href='/functions/randomQuote'>
<a>Générateur de citations</a>
</Link>
.
</p>
</div>
<div className='col-24 table-column'>
<table style={{ marginBottom: '50px' }}>
<thead>
<tr>
<th className='table-row' scope='col'>
Citation/Proverbe
</th>
<th className='table-row' scope='col'>
Auteur
</th>
</tr>
</thead>
<tbody>
{props.quotesArray.map((currentQuote, index) => {
return (
<tr key={index}>
<td className='table-row text-center'>
{currentQuote.quote}
</td>
<td className='table-row text-center'>
{currentQuote.author}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
<div className='col-24 table-column'>
<table style={{ marginBottom: '50px' }}>
<thead>
<tr>
<th className='table-row' scope='col'>
Citation/Proverbe
</th>
<th className='table-row' scope='col'>
Auteur
</th>
</tr>
</thead>
<tbody>
{props.quotesArray.map((currentQuote, index) => {
return (
<tr key={index}>
<td className='table-row text-center'>
{currentQuote.quote}
</td>
<td className='table-row text-center'>
{currentQuote.author}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
)}
</div>
)}
</div>
)}
</>
)
}
+10 -6
View File
@@ -2,11 +2,15 @@ import axios from 'axios'
export const API_URL = process.env.NEXT_PUBLIC_API_URL
const api = axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json'
}
})
const api = (() => {
const baseURL =
typeof window === 'undefined' ? process.env.CONTAINER_API_URL : API_URL
return axios.create({
baseURL,
headers: {
'Content-Type': 'application/json'
}
})
})()
export default api