📦 NEW: Ajout de la fonction toDoList
This commit is contained in:
		
							
								
								
									
										4
									
								
								.github/backup.sql
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/backup.sql
									
									
									
									
										vendored
									
									
								
							| @@ -17,4 +17,6 @@ INSERT INTO `functions` (`id`, `title`, `slug`, `description`, `image`, `type`, | ||||
| (8, 'Conversion des Encodages de caractères', 'convertEncoding', 'Convertis des nombres de différentes bases et convertis en UTF-8.', '/images/functions/convertEncoding.png', 'form', NULL, '[{\"name\": \"value\", \"type\": \"text\", \"label\": \"Entrez votre valeur :\", \"placeholder\": \"Votre valeur...\"}, {\"name\": \"functionName\", \"type\": \"select\", \"label\": \"Choisissez une option :\", \"options\": [{\"name\": \"Décimal en Binaire\", \"value\": \"decimalToBinary\"}, {\"name\": \"Binaire en Décimal\", \"value\": \"binaryToDecimal\"}, {\"name\": \"Décimal en Hexadecimal\", \"value\": \"decimalToHexadecimal\"}, {\"name\": \"Hexadecimal en Décimal\", \"value\": \"hexadecimalToDecimal\"}, {\"name\": \"Binaire en Hexadécimal\", \"value\": \"binaryToHexadecimal\"}, {\"name\": \"Hexadécimal en Binaire\", \"value\": \"hexadecimalToBinary\"}, {\"name\": \"Chaque caractère a un nombre Unicode\", \"value\": \"textToNumberUnicode\"}, {\"name\": \"Chaque nombre Unicode a un caractère\", \"value\": \"numberUnicodeToText\"}, {\"name\": \"Texte en Binaire (UTF-8)\", \"value\": \"textToBinary\"}, {\"name\": \"Binaire (UTF-8) en Texte\", \"value\": \"binaryToText\"}, {\"name\": \"Texte en Hexadécimal (UTF-8)\", \"value\": \"textToHexadecimal\"}, {\"name\": \"Hexadécimal (UTF-8) en Texte\", \"value\": \"hexadecimalToText\"}], \"placeholder\": \"\"}]', 1, '2019-09-14 00:00:00', '2020-04-22 22:34:48', 2), | ||||
| (9, 'Conversion d\'un nombre arabe en nombre romain', 'convertRomanArabicNumbers', 'Convertis un nombre arabe en nombre romain (et l\'inverse aussi).', '/images/functions/convertRomanArabicNumbers.png', 'form', NULL, '[{\"name\": \"value\", \"type\": \"text\", \"label\": \"Entrez votre nombre :\", \"placeholder\": \"(e.g : 50 ou L)\"}, {\"name\": \"functionName\", \"type\": \"select\", \"label\": \"Convertir en :\", \"options\": [{\"name\": \"Nombre Romain\", \"value\": \"convertArabicToRomanOutput\"}, {\"name\": \"Nombre Arabe\", \"value\": \"convertRomanToArabicOutput\"}], \"placeholder\": \"\"}]', 1, '2019-09-21 00:00:00', '2020-04-22 22:43:50', 2), | ||||
| (10, 'Nombre d\'Armstrong', 'armstrongNumber', 'Permet de savoir si un nombre fait partie des nombres d\'Armstrong.', '/images/functions/armstrongNumber.png', 'form', NULL, '[{\"name\": \"number\", \"type\": \"integer\", \"label\": \"Entrez votre nombre :\", \"placeholder\": \"(e.g : 153)\"}]', 1, '2019-09-21 00:00:00', '2020-04-22 22:49:21', 2), | ||||
| (11, 'Heap\'s algorithm', 'heapAlgorithm', 'Génère toutes les permutations uniques possibles d\'une chaîne de caractère.', '/images/functions/heapAlgorithm.png', 'form', NULL, '[{\"name\": \"string\", \"type\": \"text\", \"label\": \"Entrez un mot :\", \"placeholder\": \"(e.g : Mot)\"}]', 1, '2019-10-11 00:01:00', '2020-04-22 23:06:15', 2); | ||||
| (11, 'Heap\'s algorithm', 'heapAlgorithm', 'Génère toutes les permutations uniques possibles d\'une chaîne de caractère.', '/images/functions/heapAlgorithm.png', 'form', NULL, '[{\"name\": \"string\", \"type\": \"text\", \"label\": \"Entrez un mot :\", \"placeholder\": \"(e.g : Mot)\"}]', 1, '2019-10-11 00:01:00', '2020-04-22 23:06:15', 2), | ||||
| (12, 'Raccourcisseurs de liens', 'linkShortener', 'Une URL trop longue ? Raccourcissez-là !', '/images/functions/linkShortener.png', 'form', NULL, '[{\"name\": \"url\", \"type\": \"text\", \"label\": \"Entrez le lien à raccourcir :\", \"placeholder\": \"(e.g : https://divlo.fr)\"}, {\"name\": \"shortcutName\", \"type\": \"text\", \"label\": \"Entrez le nom du raccourci :\", \"placeholder\": \"(e.g : divlo)\"}]', 1, '2019-12-11 00:00:00', '2020-04-23 09:45:02', 1), | ||||
| (13, 'Liste de choses à faire', 'toDoList', 'Prévoyez la liste de choses que vous devez faire.', '/images/functions/toDoList.png', 'page', NULL, NULL, 1, '2019-12-26 00:00:00', '2020-04-23 13:48:33', 1); | ||||
| @@ -1,5 +1,5 @@ | ||||
| import Link from 'next/link'; | ||||
| import { forwardRef, useContext, Fragment } from 'react'; | ||||
| import { forwardRef, useContext } from 'react'; | ||||
| import date from 'date-and-time'; | ||||
| import { UserContext } from '../../../contexts/UserContext'; | ||||
| import { API_URL } from '../../../utils/config'; | ||||
| @@ -35,14 +35,20 @@ const CommentCard = forwardRef((props, ref) => { | ||||
|                         <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)} | ||||
|                         {(isAuth && user.name === props.user.name) && <Fragment> - <a onClick={deleteCommentById} href="#">supprimer</a></Fragment>}  | ||||
|                          - {date.format(new Date(props.createdAt), 'DD/MM/YYYY à HH:mm', true)}  | ||||
|                     </span> | ||||
|                 </div> | ||||
|                 <div className="row"> | ||||
|                     <p className="CommentCard__message"> | ||||
|                         {props.message} | ||||
|                     </p> | ||||
|                     <div className="col-24"> | ||||
|                         <p className="CommentCard__message"> | ||||
|                             {props.message} | ||||
|                         </p> | ||||
|                         {(isAuth && user.name === props.user.name) &&  | ||||
|                             <p style={{ fontSize: '15px', margin: '15px 0 0 0', fontStyle: 'italic' }}> | ||||
|                                 <a onClick={deleteCommentById} href="#">supprimer le commentaire</a> | ||||
|                             </p> | ||||
|                         } | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|   | ||||
| @@ -86,7 +86,7 @@ const FunctionComments = ({ functionId }) => { | ||||
|                                 </form> | ||||
|                             : | ||||
|                                 <p className="text-center"> | ||||
|                                     Vous devez être <Link href={'/login'}><a>connecté</a></Link> pour poster un commentaire. | ||||
|                                     Vous devez être <Link href={'/users/login'}><a>connecté</a></Link> pour poster un commentaire. | ||||
|                                 </p> | ||||
|                         } | ||||
|                     </div> | ||||
|   | ||||
| @@ -169,7 +169,7 @@ const SuggestQuote = () => { | ||||
|         const token = user.token; | ||||
|         if (isAuth && token != undefined) { | ||||
|             api.post('/quotes', inputState, { headers: { 'Authorization': token } }) | ||||
|             .then(({ data }) => { | ||||
|                 .then(({ data }) => { | ||||
|                     setInputState({ quote: "", author: "" }); | ||||
|                     setMessage(`<p class="form-success"><b>Succès:</b> ${data.message}</p>`); | ||||
|                     setIsLoading(false); | ||||
| @@ -181,50 +181,49 @@ const SuggestQuote = () => { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return ( | ||||
|         <div className="container-fluid"> | ||||
|             { | ||||
|                 (isAuth) ? | ||||
|                     <Fragment>                         | ||||
|                         <div className="row justify-content-center"> | ||||
|                             <div className="col-24 text-center"> | ||||
|                                 <h2 style={{ margin: 0 }}>Proposer une citation : </h2> | ||||
|                                 <p style={{ marginTop: '5px' }}>Vous pouvez proposer des citations, et une fois validé elles seront rajoutés à la liste des citations.</p> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <div style={{ marginBottom: '40px' }} className="row"> | ||||
|                             <div className="col-24"> | ||||
|                                 <form onSubmit={handleSubmit}> | ||||
|                                     <div className="form-group"> | ||||
|                                         <label htmlFor="quote" className="form-label">Citation :</label> | ||||
|                                         <textarea value={inputState.quote} onChange={handleChange} style={{ height: 'auto' }} id="quote" name="quote" type="text" className="form-control" rows="4" placeholder="La citation..." /> | ||||
|                                     </div> | ||||
|                                      | ||||
|                                     <div className="form-group"> | ||||
|                                         <label htmlFor="author" className="form-label">Auteur :</label> | ||||
|                                         <input value={inputState.author} onChange={handleChange} name="author" id="author" type="text" className="form-control" placeholder="L'auteur de la citation..." /> | ||||
|                                     </div> | ||||
|     if (!isAuth) { | ||||
|         return ( | ||||
|             <p className="text-center"> | ||||
|                 Vous devez être <Link href={'/users/login'}><a>connecté</a></Link> pour proposer une citation. | ||||
|             </p> | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|                                     <div className="form-group text-center"> | ||||
|                                         <button type="submit" className="btn btn-dark">Envoyer</button> | ||||
|                                     </div> | ||||
|                                 </form> | ||||
|                             </div> | ||||
|     return ( | ||||
|         <div className="container-fluid">                      | ||||
|             <div className="row justify-content-center"> | ||||
|                 <div className="col-24 text-center"> | ||||
|                     <h2 style={{ margin: 0 }}>Proposer une citation : </h2> | ||||
|                     <p style={{ marginTop: '5px' }}>Vous pouvez proposer des citations, et une fois validé elles seront rajoutés à la liste des citations.</p> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div style={{ marginBottom: '40px' }} className="row"> | ||||
|                 <div className="col-24"> | ||||
|                     <form onSubmit={handleSubmit}> | ||||
|                         <div className="form-group"> | ||||
|                             <label htmlFor="quote" className="form-label">Citation :</label> | ||||
|                             <textarea value={inputState.quote} onChange={handleChange} style={{ height: 'auto' }} id="quote" name="quote" type="text" className="form-control" rows="4" placeholder="La citation..." /> | ||||
|                         </div> | ||||
|                         <div className="form-result text-center"> | ||||
|                             { | ||||
|                                 (isLoading) ?  | ||||
|                                     <Loader /> | ||||
|                                 : | ||||
|                                     htmlParser(message) | ||||
|                             } | ||||
|                          | ||||
|                         <div className="form-group"> | ||||
|                             <label htmlFor="author" className="form-label">Auteur :</label> | ||||
|                             <input value={inputState.author} onChange={handleChange} name="author" id="author" type="text" className="form-control" placeholder="L'auteur de la citation..." /> | ||||
|                         </div> | ||||
|                     </Fragment> | ||||
|                 : | ||||
|                 <p className="text-center"> | ||||
|                     Vous devez être <Link href={'/login'}><a>connecté</a></Link> pour proposer une citation. | ||||
|                 </p> | ||||
|             } | ||||
|  | ||||
|                         <div className="form-group text-center"> | ||||
|                             <button type="submit" className="btn btn-dark">Envoyer</button> | ||||
|                         </div> | ||||
|                     </form> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div className="form-result text-center"> | ||||
|                 { | ||||
|                     (isLoading) ?  | ||||
|                         <Loader /> | ||||
|                     : | ||||
|                         htmlParser(message) | ||||
|                 } | ||||
|             </div> | ||||
|         </div> | ||||
|     ); | ||||
| } | ||||
|   | ||||
							
								
								
									
										150
									
								
								website/pages/functions/toDoList.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								website/pages/functions/toDoList.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| import { useState, useEffect, useContext } from 'react'; | ||||
| import Link from 'next/link'; | ||||
| import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; | ||||
| import { faTrash, faCheck, faTimes } from '@fortawesome/free-solid-svg-icons'; | ||||
| import { UserContext } from '../../contexts/UserContext'; | ||||
| import redirect from '../../utils/redirect'; | ||||
| import FunctionPage from '../../components/FunctionPage/FunctionPage'; | ||||
| import FunctionTabs from '../../components/FunctionPage/FunctionTabs'; | ||||
| import FunctionArticle from '../../components/FunctionPage/FunctionArticle'; | ||||
| import FunctionComments from '../../components/FunctionPage/FunctionComments/FunctionComments'; | ||||
| import api from '../../utils/api'; | ||||
| import '../../public/css/pages/FunctionComponent.css'; | ||||
| import '../../public/css/pages/functions/toDoList.css'; | ||||
| import '../../components/FunctionCard/FunctionCard.css'; | ||||
|  | ||||
| const ManageToDo = () => { | ||||
|  | ||||
|     const { isAuth, user }            = useContext(UserContext); | ||||
|     const [inputState, setInputState] = useState({ task: "" }); | ||||
|     const [tasks, setTasks]           = useState([]); | ||||
|  | ||||
|     useEffect(() => { | ||||
|         const getTasks = async () => { | ||||
|             const { data } = await api.get('/tasks', { headers: { 'Authorization': user.token } }); | ||||
|             setTasks(data); | ||||
|         } | ||||
|         if (isAuth && user.token != undefined) { | ||||
|             getTasks(); | ||||
|         } | ||||
|     }, [isAuth]); | ||||
|  | ||||
|     const handleChange = (event) => { | ||||
|         const inputStateNew = { ...inputState }; | ||||
|         inputStateNew[event.target.name] = event.target.value; | ||||
|         setInputState(inputStateNew); | ||||
|     } | ||||
|  | ||||
|     const handleSubmit = async (event) => { | ||||
|         event.preventDefault(); | ||||
|         try { | ||||
|             const { data } = await api.post('/tasks', inputState, { headers: { 'Authorization': user.token } }); | ||||
|             const newTasks = [...tasks]; | ||||
|             newTasks.push(data); | ||||
|             setTasks(newTasks); | ||||
|             setInputState({ task: "" }); | ||||
|         } catch {} | ||||
|     } | ||||
|  | ||||
|     const handleRemoveTask = async (id, index) => { | ||||
|         const newTasks = [...tasks]; | ||||
|         try { | ||||
|             await api.delete(`/tasks/${id}`, { headers: { 'Authorization': user.token } }); | ||||
|             newTasks.splice(index, 1); | ||||
|             setTasks(newTasks); | ||||
|         } catch {} | ||||
|     } | ||||
|  | ||||
|     const handleEditTask = async (id, index, isCompleted) => { | ||||
|         try { | ||||
|             await api.put(`/tasks/${id}`, { isCompleted: !isCompleted }, { headers: { 'Authorization': user.token } }); | ||||
|             const newTasks   = [...tasks]; | ||||
|             const taskObject = newTasks[index]; | ||||
|             taskObject.isCompleted = !isCompleted; | ||||
|             setTasks(newTasks); | ||||
|         } catch {} | ||||
|     } | ||||
|  | ||||
|     if (!isAuth) { | ||||
|         return ( | ||||
|             <p className="text-center"> | ||||
|                 Vous devez être <Link href={'/users/login'}><a>connecté</a></Link> pour gérer des "tâches à faire". | ||||
|             </p> | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     return ( | ||||
|         <div className="container-fluid"> | ||||
|             <div className="row justify-content-center"> | ||||
|                 <div className="col-24"> | ||||
|                     <form onSubmit={handleSubmit}> | ||||
|                         <div className="text-center"> | ||||
|                             <label htmlFor="task" className="form-label">Ajouter une tâche à faire :</label> | ||||
|                             <input value={inputState.task} onChange={handleChange} name="task" id="task" type="text" className="form-control" placeholder="(e.g : Apprendre à coder)" /> | ||||
|                         </div> | ||||
|  | ||||
|                         <div className="form-group text-center"> | ||||
|                             <button type="submit" className="btn btn-dark">Envoyer</button> | ||||
|                         </div> | ||||
|                     </form> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             {(tasks.length > 0) && | ||||
|                 <div className="row justify-content-center"> | ||||
|                     <div className="col-24 ManageToDo__container"> | ||||
|                         <ul className="ManageToDo__list"> | ||||
|                             {tasks.map((task, index) => { | ||||
|                                return ( | ||||
|                                     <li key={task.id} className={`ManageToDo__list-item ${(task.isCompleted) ? "isCompleted" : ""}`}> | ||||
|                                         <span className="ManageToDo__list-item-span">{task.task}</span> | ||||
|                                         <div> | ||||
|                                             <button className="ManageToDo__task-btn" title="Supprimer de la liste" onClick={() => handleRemoveTask(task.id, index)}> | ||||
|                                                 <FontAwesomeIcon icon={faTrash} /> | ||||
|                                             </button> | ||||
|                                             <button className="ManageToDo__task-btn" onClick={() => handleEditTask(task.id, index, task.isCompleted)}> | ||||
|                                                 <FontAwesomeIcon { ...(task.isCompleted) ? { icon: faTimes } : { icon: faCheck } } /> | ||||
|                                             </button> | ||||
|                                         </div> | ||||
|                                     </li> | ||||
|                                ); | ||||
|                             })} | ||||
|                         </ul> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             } | ||||
|         </div> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| const FunctionTabManager = (props) => { | ||||
|     return ( | ||||
|         <FunctionTabs setSlideIndex={props.setSlideIndex} slideIndex={props.slideIndex}> | ||||
|             <div className="FunctionComponent__slide"> | ||||
|                 <ManageToDo /> | ||||
|             </div> | ||||
|             <div className="FunctionComponent__slide"> | ||||
|                 <FunctionArticle article={props.article} /> | ||||
|             </div> | ||||
|             <div className="FunctionComponent__slide"> | ||||
|                 <FunctionComments functionId={props.id} /> | ||||
|             </div> | ||||
|         </FunctionTabs> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| const toDoList = (props) => ( | ||||
|     <FunctionPage  | ||||
|         FunctionTabManager={FunctionTabManager} | ||||
|         { ...props } | ||||
|         tabNames={["⚙️ Utilisation", "📝 Article", "📬 Commentaires"]}  | ||||
|     /> | ||||
| ); | ||||
|  | ||||
| export async function getServerSideProps(context) { | ||||
|     return api.get(`/functions/toDoList`) | ||||
|         .then((response) => ({ props: response.data })) | ||||
|         .catch(() => redirect(context, '/404')); | ||||
| } | ||||
|  | ||||
| export default toDoList; | ||||
							
								
								
									
										34
									
								
								website/public/css/pages/functions/toDoList.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								website/public/css/pages/functions/toDoList.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +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; | ||||
| } | ||||
| .ManageToDo__list { | ||||
|     overflow: hidden; | ||||
| } | ||||
| .ManageToDo__list-item { | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     margin: 25px 0; | ||||
| } | ||||
| .ManageToDo__list-item.isCompleted { | ||||
|     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; | ||||
| } | ||||
| .ManageToDo__list-item-span { | ||||
|     width: calc(100% - 120px); | ||||
|     overflow: hidden; | ||||
|     text-overflow: ellipsis; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user