frontend et backend: Crée une nouvelle fonction
This commit is contained in:
		| @@ -4,6 +4,50 @@ const { validationResult } = require('express-validator'); | ||||
| const errorHandling        = require('../assets/utils/errorHandling'); | ||||
| const { serverError }      = require('../assets/config/errors'); | ||||
| const Functions            = require('../models/functions'); | ||||
| const Categories           = require('../models/categories'); | ||||
| const helperQueryNumber    = require('../assets/utils/helperQueryNumber'); | ||||
| const Sequelize            = require('sequelize'); | ||||
|  | ||||
| exports.getFunctions = (req, res, next) => { | ||||
|     const page       = helperQueryNumber(req.query.page, 1); | ||||
|     const limit      = helperQueryNumber(req.query.limit, 10); | ||||
|     const categoryId = helperQueryNumber(req.query.categoryId, 0); | ||||
|     let   search     = req.query.search; | ||||
|     try { search = search.toLowerCase(); } catch {} | ||||
|     const offset     = (page - 1) * limit; | ||||
|     Functions.findAndCountAll({  | ||||
|         limit,  | ||||
|         offset,  | ||||
|         where: { | ||||
|             // Trie par catégorie | ||||
|             ... (categoryId !== 0) && { categorieId: categoryId }, | ||||
|             // Recherche | ||||
|             ... (search != undefined) && {  | ||||
|                 [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}%`) } | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         include: [ | ||||
|             { model: Categories, attributes: ["name", "color"] } | ||||
|         ], | ||||
|         attributes: { | ||||
|             exclude: ["updatedAt", "utilizationForm", "article", "isOnline"] | ||||
|         }, | ||||
|         order: [['createdAt', 'DESC']] | ||||
|     }) | ||||
|         .then((result) => { | ||||
|             const { count, rows } = result; | ||||
|             const hasMore = (page * limit) < count; | ||||
|             return res.status(200).json({ totalItems: count, hasMore, rows }); | ||||
|         }) | ||||
|         .catch((error) => { | ||||
|             console.log(error); | ||||
|             return errorHandling(next, serverError); | ||||
|         }); | ||||
| } | ||||
|  | ||||
| exports.postFunction = (req, res, next) => { | ||||
|     const { title, slug, description, type, categorieId } = req.body; | ||||
| @@ -19,7 +63,9 @@ exports.postFunction = (req, res, next) => { | ||||
|     )) { | ||||
|         return errorHandling(next, { message:"La fonction doit avoir une image valide.", statusCode: 400 }); | ||||
|     } | ||||
|     const imageName = slug + image.name; | ||||
|     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 { | ||||
|   | ||||
| @@ -18,6 +18,6 @@ module.exports = (req, _res, next) => { | ||||
|         }) | ||||
|         .catch((error) => { | ||||
|             console.log(error); | ||||
|             errorHandling(next, serverError); | ||||
|             return errorHandling(next, serverError); | ||||
|         }); | ||||
| } | ||||
| @@ -9,70 +9,95 @@ const Categories      = require('../models/categories'); | ||||
|  | ||||
| const AdminRouter = Router(); | ||||
|  | ||||
| // Permet de créé une fonction | ||||
| AdminRouter.post('/functions', isAuth, isAdmin,  | ||||
| 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."), | ||||
|     body('slug') | ||||
|         .not() | ||||
|         .isEmpty() | ||||
|         .withMessage("La fonction doit avoir un slug.") | ||||
|         .isLength({ max: 100 }) | ||||
|         .withMessage("Le slug est trop long.") | ||||
|         .custom((async (slug) => { | ||||
|             try { | ||||
|                 const FunctionSlug = await Functions.findOne({ where: { slug } }); | ||||
|                 if (FunctionSlug) { | ||||
|                     return Promise.reject("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 }) | ||||
|         .withMessage("La description est trop longue."), | ||||
|     body('categorieId') | ||||
|         .not() | ||||
|         .isEmpty() | ||||
|         .withMessage("La fonction doit avoir une catégorie.") | ||||
|         .custom(async (categorieId) => { | ||||
|             try { | ||||
|                 const categorieFound = await Categories.findOne({ where: { id: categorieId } }); | ||||
|                 if (!categorieFound) { | ||||
|                     return Promise.reject("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('Le type de la fonction peut être : article, form ou page.'); | ||||
|             } | ||||
|             return true; | ||||
|         }) | ||||
| ], adminController.postFunction); | ||||
| AdminRouter.route('/functions') | ||||
|  | ||||
| // Supprime une fonction avec son id | ||||
| AdminRouter.delete('/functions/:id', isAuth, isAdmin, adminController.deleteFunction); | ||||
|     // Récupère les fonctions | ||||
|     .get(isAuth, isAdmin, adminController.getFunctions) | ||||
|  | ||||
|     // Permet de créé une fonction | ||||
|     .post(isAuth, isAdmin,  | ||||
|     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("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("La fonction doit avoir un slug."); | ||||
|                 } | ||||
|                 return true; | ||||
|             })) | ||||
|             .custom((async (slug) => { | ||||
|                 try { | ||||
|                     const FunctionSlug = await Functions.findOne({ where: { slug } }); | ||||
|                     if (FunctionSlug) { | ||||
|                         return Promise.reject("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("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("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('Le type de la fonction peut être : article, form ou page.'); | ||||
|                 } | ||||
|                 return true; | ||||
|             }) | ||||
|     ], adminController.postFunction); | ||||
|  | ||||
| AdminRouter.route('/functions/:id') | ||||
|  | ||||
|     // Supprime une fonction avec son id | ||||
|     .delete(isAuth, isAdmin, adminController.deleteFunction); | ||||
|  | ||||
| module.exports = AdminRouter; | ||||
| @@ -55,7 +55,12 @@ const FunctionsList = (props) => { | ||||
|     const getFunctionsData = () => { | ||||
|         setLoadingFunctions(true); | ||||
|         return new Promise(async (next) => { | ||||
|             const result = await api.get(`/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 result = await api.get(URL, { | ||||
|                 headers: { | ||||
|                     ...(props.isAdmin && props.token != undefined) && { 'Authorization': props.token } | ||||
|                 } | ||||
|             }); | ||||
|             setLoadingFunctions(false); | ||||
|             next(result.data); | ||||
|         }); | ||||
|   | ||||
| @@ -1,18 +0,0 @@ | ||||
| import Cookies from "universal-cookie"; | ||||
|  | ||||
| const addFunction = (props) => { | ||||
|     return ( | ||||
|         <p>Crée une nouvelle fonction</p> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| export async function getServerSideProps({ req }) { | ||||
|     const cookies = new Cookies(req.headers.cookie); | ||||
|     return { | ||||
|         props: {  | ||||
|             user: { ...cookies.get('user') } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| export default addFunction; | ||||
| @@ -1,12 +1,66 @@ | ||||
| import { Fragment } from 'react'; | ||||
| import Link from 'next/link'; | ||||
| import { Fragment, useState, useEffect } from 'react'; | ||||
| import Cookies from "universal-cookie"; | ||||
| import HeadTag from '../../components/HeadTag'; | ||||
| import FunctionsList from '../../components/FunctionsList/FunctionsList'; | ||||
| import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; | ||||
| import { faTimes } from '@fortawesome/free-solid-svg-icons'; | ||||
| import Modal from '../../components/Modal'; | ||||
| import redirect from '../../utils/redirect'; | ||||
| import htmlParser from 'html-react-parser'; | ||||
| import Loader from '../../components/Loader'; | ||||
| import useAPI from '../../hooks/useAPI'; | ||||
| import '../../public/css/pages/admin.css'; | ||||
| import api from '../../utils/api'; | ||||
|  | ||||
| const Admin = (props) => { | ||||
|  | ||||
|     const [, categories]              = useAPI('/categories'); | ||||
|     const [isOpen, setIsOpen]         = useState(false); | ||||
|     const [inputState, setInputState] = useState({ type: 'form' }); | ||||
|     const [message, setMessage]       = useState(""); | ||||
|     const [isLoading, setIsLoading]   = useState(false); | ||||
|  | ||||
|     useEffect(() => { | ||||
|         if (categories.length > 0) { | ||||
|             handleChange({ | ||||
|                 target: { | ||||
|                     name: "categorieId", | ||||
|                     value: categories[0].id | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     }, [categories]); | ||||
|  | ||||
|     const toggleModal = () => setIsOpen(!isOpen); | ||||
|  | ||||
|     const handleChange = (event, isTypeCheck = false) => { | ||||
|         const inputStateNew = { ...inputState }; | ||||
|         inputStateNew[event.target.name] = (event.target.files != undefined) ? event.target.files[0] : (isTypeCheck) ? event.target.checked : event.target.value; | ||||
|         setInputState(inputStateNew); | ||||
|     } | ||||
|  | ||||
|     const handleSubmit = (event) => { | ||||
|         event.preventDefault(); | ||||
|         setIsLoading(true); | ||||
|         const formData = new FormData(); | ||||
|         formData.append('type', inputState.type); | ||||
|         formData.append('categorieId', inputState.categorieId); | ||||
|         formData.append('title', inputState.title); | ||||
|         formData.append('slug', inputState.slug); | ||||
|         formData.append('description', inputState.description); | ||||
|         formData.append('image', inputState.image); | ||||
|  | ||||
|         api.post('/admin/functions', formData, { headers: { 'Authorization': props.user.token } }) | ||||
|             .then((response) => { | ||||
|                 setMessage(`<p class="form-success"><b>Succès:</b> ${response.data.message}</p>`); | ||||
|                 setIsLoading(false); | ||||
|             }) | ||||
|             .catch((error) => { | ||||
|                 setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`); | ||||
|                 setIsLoading(false); | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     if (!props.user.isAdmin && typeof window != 'undefined') { | ||||
|         return redirect({}, '/404'); | ||||
|     } | ||||
| @@ -15,14 +69,89 @@ const Admin = (props) => { | ||||
|         <Fragment> | ||||
|             <HeadTag title="Admin - FunctionProject" description="Page d'administration de FunctionProject." /> | ||||
|  | ||||
|             <FunctionsList isAdmin> | ||||
|                 <div className="col-24"> | ||||
|                     <h1 className="Functions__title">Administration</h1> | ||||
|                     <Link href={"/admin/addFunction"}> | ||||
|                         <button style={{ margin: '0 0 40px 0' }} className="btn btn-dark">Crée une nouvelle fonction</button> | ||||
|                     </Link> | ||||
|                 </div> | ||||
|             </FunctionsList> | ||||
|             {/* 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> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|  | ||||
|                                 <div className="col-24"> | ||||
|                                     <form onSubmit={handleSubmit}> | ||||
|                                         <div className="form-group"> | ||||
|                                             <label className="form-label" htmlFor="title">Titre :</label> | ||||
|                                             <input value={inputState.name} 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.name} 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.biography} onChange={handleChange} name="description" id="description" className="form-control" rows="5"></textarea> | ||||
|                                         </div> | ||||
|  | ||||
|                                         <div className="form-group"> | ||||
|                                             <label className="form-label" htmlFor="type">Type :</label> | ||||
|                                             <select onChange={handleChange} name="type" id="type" className="form-control"> | ||||
|                                                 <option value="form">Formulaire</option> | ||||
|                                                 <option value="article">Article</option> | ||||
|                                                 <option value="page">Page</option> | ||||
|                                             </select> | ||||
|                                         </div> | ||||
|  | ||||
|                                         <div className="form-group"> | ||||
|                                             <label className="form-label" htmlFor="categorieId">Catégorie :</label> | ||||
|                                             <select onChange={handleChange} name="categorieId" id="categorieId" className="form-control"> | ||||
|                                                 {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> | ||||
|                                             <br/> | ||||
|                                             <input onChange={handleChange} accept="image/jpeg,image/jpg,image/png" type="file" name="image" id="image" /> | ||||
|                                         </div> | ||||
|  | ||||
|                                         <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> | ||||
|                                 </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> | ||||
|                         </div> | ||||
|                     </FunctionsList> | ||||
|             } | ||||
|         </Fragment> | ||||
|     ); | ||||
| } | ||||
|   | ||||
| @@ -70,137 +70,140 @@ const Profile = (props) => { | ||||
|             <HeadTag title={`${props.name} - FunctionProject`} description={`Profil utilisateur de ${props.name}. ${(props.biography != undefined) ? props.biography : ""}`} /> | ||||
|  | ||||
|             {/* Édition du profil */} | ||||
|             {(isOpen) && | ||||
|                 <Modal toggleModal={toggleModal}> | ||||
|                     <div className="container-fluid Profile__container"> | ||||
|                         <div className="row Profile__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> | ||||
|             {(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"></textarea> | ||||
|                                         </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="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 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="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 className="col-24 text-center"> | ||||
|                                         {(props.biography != undefined) && <p>{props.biography}</p>} | ||||
|                                         {(props.email != undefined) && <p><span className="important">Email :</span> {props.email}</p>} | ||||
|                                         <p><span className="important">Date de création :</span> {publicationDate}</p> | ||||
|                                     </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"></textarea> | ||||
|                                     </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) | ||||
|                                     {(isAuth && user.name === props.name) &&                             | ||||
|                                         <button onClick={toggleModal} style={{ marginBottom: '25px' }} className="btn btn-dark"> | ||||
|                                             <FontAwesomeIcon icon={faPen} style={{cursor: 'pointer', width: '1rem'}} />  | ||||
|                                               Éditez le profil | ||||
|                                         </button> | ||||
|                                     } | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </Modal> | ||||
|             } | ||||
|  | ||||
|             <div className={`container-fluid Profile__container ${(isOpen) ? "d-none" : ""}`}> | ||||
|                 <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 != undefined) && <p>{props.biography}</p>} | ||||
|                                 {(props.email != undefined) && <p><span className="important">Email :</span> {props.email}</p>} | ||||
|                                 <p><span className="important">Date de création :</span> {publicationDate}</p> | ||||
|                             </div> | ||||
|  | ||||
|                             {(isAuth && user.name === props.name) &&                             | ||||
|                                 <button onClick={toggleModal} style={{ marginBottom: '25px' }} className="btn btn-dark"> | ||||
|                                     <FontAwesomeIcon icon={faPen} style={{cursor: 'pointer', width: '1rem'}} />  | ||||
|                                       Éditez le profil | ||||
|                                 </button> | ||||
|                             } | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                  | ||||
|                 {(props.favoritesArray.length > 0) && | ||||
|                     <div className="row justify-content-center"> | ||||
|                         <div className="col-24 text-center"> | ||||
|                             <h2>Fonctions en <span className="important">favoris :</span></h2> | ||||
|                         </div> | ||||
|                         <div className="col-24"> | ||||
|                          | ||||
|                         {(props.favoritesArray.length > 0) && | ||||
|                             <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 text-center"> | ||||
|                             {props.commentsArray.map((comment) => ( | ||||
|                                 <div key={comment.id} className="row Profile__row Profile__comment"> | ||||
|                                     <div className="col-20"> | ||||
|                                         <p> | ||||
|                                             Posté sur la fonction   | ||||
|                                             <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>  | ||||
|                                              le {date.format(new Date(comment.createdAt), 'DD/MM/YYYY à HH:mm', true)} | ||||
|                                         </p> | ||||
|                                         <p>"{comment.message}"</p> | ||||
|                                 <div className="col-24 text-center"> | ||||
|                                     <h2>Fonctions en <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> | ||||
|                             </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 text-center"> | ||||
|                                     {props.commentsArray.map((comment) => ( | ||||
|                                         <div key={comment.id} className="row Profile__row Profile__comment"> | ||||
|                                             <div className="col-20"> | ||||
|                                                 <p> | ||||
|                                                     Posté sur la fonction   | ||||
|                                                     <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>  | ||||
|                                                      le {date.format(new Date(comment.createdAt), 'DD/MM/YYYY à HH:mm', true)} | ||||
|                                                 </p> | ||||
|                                                 <p>"{comment.message}"</p> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     ))} | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         } | ||||
|                     </div> | ||||
|                 } | ||||
|             </div> | ||||
|             } | ||||
|  | ||||
|         </Fragment> | ||||
|     ); | ||||
| } | ||||
|   | ||||
							
								
								
									
										22
									
								
								website/public/css/pages/admin.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								website/public/css/pages/admin.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| .Admin__Modal__container { | ||||
|     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; | ||||
| } | ||||
| .Admin__Modal-top-container { | ||||
|     margin: 20px 0; | ||||
| } | ||||
| .Admin__Modal-select-option { | ||||
|     color: rgb(221, 220, 220); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user