🎨 website/utils API_URL + standardJS
This commit is contained in:
		| @@ -2,7 +2,7 @@ import Link from 'next/link' | |||||||
| import { useState, forwardRef, memo } from 'react' | import { useState, forwardRef, memo } from 'react' | ||||||
| import date from 'date-and-time' | import date from 'date-and-time' | ||||||
| import Loader from '../Loader' | import Loader from '../Loader' | ||||||
| import { API_URL } from '../../utils/config/config' | import { API_URL } from '../../utils/api' | ||||||
| import './FunctionCard.css' | import './FunctionCard.css' | ||||||
|  |  | ||||||
| const FunctionCard = memo( | const FunctionCard = memo( | ||||||
|   | |||||||
| @@ -3,10 +3,9 @@ import { useEffect, useState, forwardRef, useContext } from 'react' | |||||||
| import date from 'date-and-time' | import date from 'date-and-time' | ||||||
| import htmlParser from 'html-react-parser' | import htmlParser from 'html-react-parser' | ||||||
| import { UserContext } from '../../../contexts/UserContext' | import { UserContext } from '../../../contexts/UserContext' | ||||||
| import { API_URL } from '../../../utils/config/config' |  | ||||||
| import ReactMarkdown from 'react-markdown' | import ReactMarkdown from 'react-markdown' | ||||||
| import CodeBlock from '../../CodeBlock' | import CodeBlock from '../../CodeBlock' | ||||||
| import api from '../../../utils/api' | import api, { API_URL } from '../../../utils/api' | ||||||
| import './CommentCard.css' | import './CommentCard.css' | ||||||
|  |  | ||||||
| const CommentCard = forwardRef((props, ref) => { | const CommentCard = forwardRef((props, ref) => { | ||||||
|   | |||||||
| @@ -5,8 +5,7 @@ import { faStar } from '@fortawesome/free-solid-svg-icons' | |||||||
| import { faStar as farStar } from '@fortawesome/free-regular-svg-icons' | import { faStar as farStar } from '@fortawesome/free-regular-svg-icons' | ||||||
| import date from 'date-and-time' | import date from 'date-and-time' | ||||||
| import { UserContext } from '../../contexts/UserContext' | import { UserContext } from '../../contexts/UserContext' | ||||||
| import api from '../../utils/api' | import api, { API_URL } from '../../utils/api' | ||||||
| import { API_URL } from '../../utils/config/config' |  | ||||||
| import '../FunctionCard/FunctionCard.css' | import '../FunctionCard/FunctionCard.css' | ||||||
|  |  | ||||||
| const FunctionComponentTop = props => { | const FunctionComponentTop = props => { | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { useState } from 'react' | import { useState } from 'react' | ||||||
| import { API_URL } from '../../utils/config/config' | import { API_URL } from '../../utils/api' | ||||||
| import HeadTag from '../HeadTag' | import HeadTag from '../HeadTag' | ||||||
| import FunctionTabsTop from './FunctionTabsTop' | import FunctionTabsTop from './FunctionTabsTop' | ||||||
| import FunctionComponentTop from './FunctionComponentTop' | import FunctionComponentTop from './FunctionComponentTop' | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import Link from 'next/link' | import Link from 'next/link' | ||||||
| import { forwardRef, memo } from 'react' | import { forwardRef, memo } from 'react' | ||||||
| import { API_URL } from '../../utils/config/config' | import { API_URL } from '../../utils/api' | ||||||
| import './UserCard.css' | import './UserCard.css' | ||||||
|  |  | ||||||
| const UserCard = memo( | const UserCard = memo( | ||||||
|   | |||||||
| @@ -10,9 +10,14 @@ const Error404 = () => ( | |||||||
|       image='/images/error404.png' |       image='/images/error404.png' | ||||||
|     /> |     /> | ||||||
|     <div className='Error404__container'> |     <div className='Error404__container'> | ||||||
|       <h1>Erreur <span className='important'>404</span></h1> |       <h1> | ||||||
|  |         Erreur <span className='important'>404</span> | ||||||
|  |       </h1> | ||||||
|       <p className='text-center'> |       <p className='text-center'> | ||||||
|                 Cette page n'existe pas! <Link href='/'><a>Revenir à la page d'accueil ?</a></Link> |         Cette page n'existe pas!{' '} | ||||||
|  |         <Link href='/'> | ||||||
|  |           <a>Revenir à la page d'accueil ?</a> | ||||||
|  |         </Link> | ||||||
|       </p> |       </p> | ||||||
|     </div> |     </div> | ||||||
|   </> |   </> | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import axios from 'axios' | |||||||
| import ReactMarkdown from 'react-markdown/with-html' | import ReactMarkdown from 'react-markdown/with-html' | ||||||
| import HeadTag from '../components/HeadTag' | import HeadTag from '../components/HeadTag' | ||||||
|  |  | ||||||
| const About = (props) => { | const About = props => { | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <HeadTag |       <HeadTag | ||||||
| @@ -14,7 +14,23 @@ const About = (props) => { | |||||||
|         <div className='row justify-content-center'> |         <div className='row justify-content-center'> | ||||||
|           <div className='col-24 text-center'> |           <div className='col-24 text-center'> | ||||||
|             <h1 style={{ marginBottom: 0, paddingTop: '20px' }}>À-propos</h1> |             <h1 style={{ marginBottom: 0, paddingTop: '20px' }}>À-propos</h1> | ||||||
|             <p style={{ marginTop: '5px', borderBottom: '1px solid var(--important)', paddingBottom: '30px' }}>(README.md du <a target='_blank' rel='noopener noreferrer' href='https://github.com/Divlo/FunctionProject'>GitHub</a>)</p> |             <p | ||||||
|  |               style={{ | ||||||
|  |                 marginTop: '5px', | ||||||
|  |                 borderBottom: '1px solid var(--important)', | ||||||
|  |                 paddingBottom: '30px' | ||||||
|  |               }} | ||||||
|  |             > | ||||||
|  |               (README.md du{' '} | ||||||
|  |               <a | ||||||
|  |                 target='_blank' | ||||||
|  |                 rel='noopener noreferrer' | ||||||
|  |                 href='https://github.com/Divlo/FunctionProject' | ||||||
|  |               > | ||||||
|  |                 GitHub | ||||||
|  |               </a> | ||||||
|  |               ) | ||||||
|  |             </p> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
| @@ -24,9 +40,11 @@ const About = (props) => { | |||||||
|               source={props.data} |               source={props.data} | ||||||
|               escapeHtml={false} |               escapeHtml={false} | ||||||
|               linkTarget='_blank' |               linkTarget='_blank' | ||||||
|               transformLinkUri={(uri) => { |               transformLinkUri={uri => { | ||||||
|                 if (uri.startsWith('./')) { |                 if (uri.startsWith('./')) { | ||||||
|                   return `https://github.com/Divlo/FunctionProject/blob/master/${uri.slice(2)}` |                   return `https://github.com/Divlo/FunctionProject/blob/master/${uri.slice( | ||||||
|  |                     2 | ||||||
|  |                   )}` | ||||||
|                 } |                 } | ||||||
|                 return uri |                 return uri | ||||||
|               }} |               }} | ||||||
| @@ -39,7 +57,9 @@ const About = (props) => { | |||||||
| } | } | ||||||
|  |  | ||||||
| export async function getServerSideProps (_context) { | export async function getServerSideProps (_context) { | ||||||
|   const { data } = await axios.get('https://raw.githubusercontent.com/Divlo/FunctionProject/master/README.md') |   const { data } = await axios.get( | ||||||
|  |     'https://raw.githubusercontent.com/Divlo/FunctionProject/master/README.md' | ||||||
|  |   ) | ||||||
|   return { |   return { | ||||||
|     props: { data } |     props: { data } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -6,42 +6,68 @@ import AddEditFunction from '../../components/FunctionAdmin/AddEditFunction' | |||||||
| import EditArticleFunction from '../../components/FunctionAdmin/EditArticleFunction' | import EditArticleFunction from '../../components/FunctionAdmin/EditArticleFunction' | ||||||
| import EditFormFunction from '../../components/FunctionAdmin/EditFormFunction' | import EditFormFunction from '../../components/FunctionAdmin/EditFormFunction' | ||||||
| import redirect from '../../utils/redirect' | import redirect from '../../utils/redirect' | ||||||
| import api from '../../utils/api' | import api, { API_URL } from '../../utils/api' | ||||||
| import { API_URL } from '../../utils/config/config' |  | ||||||
| import '../../components/FunctionPage/FunctionTabs.css' | import '../../components/FunctionPage/FunctionTabs.css' | ||||||
| import '../../public/css/pages/admin.css' | import '../../public/css/pages/admin.css' | ||||||
|  |  | ||||||
| const AdminFunctionComponent = (props) => { | const AdminFunctionComponent = props => { | ||||||
|   const [slideIndex, setSlideIndex] = useState(0) |   const [slideIndex, setSlideIndex] = useState(0) | ||||||
|  |  | ||||||
|   const handleDeleteFunction = async () => { |   const handleDeleteFunction = async () => { | ||||||
|     await api.delete(`/admin/functions/${props.functionInfo.id}`, { headers: { Authorization: props.user.token } }) |     await api.delete(`/admin/functions/${props.functionInfo.id}`, { | ||||||
|  |       headers: { Authorization: props.user.token } | ||||||
|  |     }) | ||||||
|     redirect({}, '/admin') |     redirect({}, '/admin') | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <HeadTag title={props.functionInfo.title} description={props.functionInfo.description} image={API_URL + props.functionInfo.image} /> |       <HeadTag | ||||||
|  |         title={props.functionInfo.title} | ||||||
|  |         description={props.functionInfo.description} | ||||||
|  |         image={API_URL + props.functionInfo.image} | ||||||
|  |       /> | ||||||
|  |  | ||||||
|       <div className='container-fluid'> |       <div className='container-fluid'> | ||||||
|         <div className='container'> |         <div className='container'> | ||||||
|           <div className='row justify-content-center'> |           <div className='row justify-content-center'> | ||||||
|             <ul className='FunctionTabs__nav'> |             <ul className='FunctionTabs__nav'> | ||||||
|               <li className='FunctionTabs__nav-item'> |               <li className='FunctionTabs__nav-item'> | ||||||
|                 <a onClick={() => setSlideIndex(0)} className={`FunctionTabs__nav-link ${(slideIndex === 0) && 'FunctionTabs__nav-link-active'}`}>✒️ Modifier</a> |                 <a | ||||||
|  |                   onClick={() => setSlideIndex(0)} | ||||||
|  |                   className={`FunctionTabs__nav-link ${slideIndex === 0 && | ||||||
|  |                     'FunctionTabs__nav-link-active'}`} | ||||||
|  |                 > | ||||||
|  |                   ✒️ Modifier | ||||||
|  |                 </a> | ||||||
|               </li> |               </li> | ||||||
|               <li className='FunctionTabs__nav-item'> |               <li className='FunctionTabs__nav-item'> | ||||||
|                 <a onClick={() => setSlideIndex(1)} className={`FunctionTabs__nav-link ${(slideIndex === 1) && 'FunctionTabs__nav-link-active'}`}>📝 Article</a> |                 <a | ||||||
|  |                   onClick={() => setSlideIndex(1)} | ||||||
|  |                   className={`FunctionTabs__nav-link ${slideIndex === 1 && | ||||||
|  |                     'FunctionTabs__nav-link-active'}`} | ||||||
|  |                 > | ||||||
|  |                   📝 Article | ||||||
|  |                 </a> | ||||||
|               </li> |               </li> | ||||||
|               <li className='FunctionTabs__nav-item'> |               <li className='FunctionTabs__nav-item'> | ||||||
|                 <a onClick={() => setSlideIndex(2)} className={`FunctionTabs__nav-link ${(slideIndex === 2) && 'FunctionTabs__nav-link-active'}`}>⚙️ Utilisation</a> |                 <a | ||||||
|  |                   onClick={() => setSlideIndex(2)} | ||||||
|  |                   className={`FunctionTabs__nav-link ${slideIndex === 2 && | ||||||
|  |                     'FunctionTabs__nav-link-active'}`} | ||||||
|  |                 > | ||||||
|  |                   ⚙️ Utilisation | ||||||
|  |                 </a> | ||||||
|               </li> |               </li> | ||||||
|             </ul> |             </ul> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <div className='container-fluid'> |         <div className='container-fluid'> | ||||||
|           <SwipeableViews onChangeIndex={(index) => setSlideIndex(index)} index={slideIndex}> |           <SwipeableViews | ||||||
|  |             onChangeIndex={index => setSlideIndex(index)} | ||||||
|  |             index={slideIndex} | ||||||
|  |           > | ||||||
|             <div className='Admin__Function-slide'> |             <div className='Admin__Function-slide'> | ||||||
|               <AddEditFunction |               <AddEditFunction | ||||||
|                 defaultInputState={{ ...props.functionInfo }} |                 defaultInputState={{ ...props.functionInfo }} | ||||||
| @@ -49,7 +75,9 @@ const AdminFunctionComponent = (props) => { | |||||||
|                 isEditing |                 isEditing | ||||||
|               /> |               /> | ||||||
|               <div style={{ marginBottom: '30px' }} className='text-center'> |               <div style={{ marginBottom: '30px' }} className='text-center'> | ||||||
|                 <button onClick={handleDeleteFunction} className='btn btn-dark'>Supprimer la fonction</button> |                 <button onClick={handleDeleteFunction} className='btn btn-dark'> | ||||||
|  |                   Supprimer la fonction | ||||||
|  |                 </button> | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|             <div className='Admin__Function-slide'> |             <div className='Admin__Function-slide'> | ||||||
| @@ -72,8 +100,9 @@ export async function getServerSideProps (context) { | |||||||
|   if (!user.isAdmin) { |   if (!user.isAdmin) { | ||||||
|     return redirect(context, '/404') |     return redirect(context, '/404') | ||||||
|   } |   } | ||||||
|   return api.get(`/admin/functions/${slug}`, { headers: { Authorization: user.token } }) |   return api | ||||||
|     .then((response) => { |     .get(`/admin/functions/${slug}`, { headers: { Authorization: user.token } }) | ||||||
|  |     .then(response => { | ||||||
|       return { |       return { | ||||||
|         props: { |         props: { | ||||||
|           user, |           user, | ||||||
|   | |||||||
| @@ -10,54 +10,77 @@ import AddEditFunction from '../../components/FunctionAdmin/AddEditFunction' | |||||||
| import redirect from '../../utils/redirect' | import redirect from '../../utils/redirect' | ||||||
| import '../../public/css/pages/admin.css' | import '../../public/css/pages/admin.css' | ||||||
|  |  | ||||||
| const Admin = (props) => { | const Admin = props => { | ||||||
|   const [isOpen, setIsOpen] = useState(false) |   const [isOpen, setIsOpen] = useState(false) | ||||||
|  |  | ||||||
|   const toggleModal = () => setIsOpen(!isOpen) |   const toggleModal = () => setIsOpen(!isOpen) | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <HeadTag title='Admin - FunctionProject' description="Page d'administration de FunctionProject." /> |       <HeadTag | ||||||
|  |         title='Admin - FunctionProject' | ||||||
|  |         description="Page d'administration de FunctionProject." | ||||||
|  |       /> | ||||||
|  |  | ||||||
|       {/* Création d'une fonction */} |       {/* Création d'une fonction */} | ||||||
|       {(isOpen) |       {isOpen ? ( | ||||||
|         ? ( |         <Modal toggleModal={toggleModal}> | ||||||
|           <Modal toggleModal={toggleModal}> |           <div className='Admin__Modal__container container-fluid'> | ||||||
|             <div className='Admin__Modal__container container-fluid'> |             <div className='Admin__Modal__row row'> | ||||||
|               <div className='Admin__Modal__row row'> |               <div className='col-24'> | ||||||
|                 <div className='col-24'> |                 <div className='Admin__Modal-top-container row'> | ||||||
|                   <div className='Admin__Modal-top-container row'> |                   <div className='col-24'> | ||||||
|                     <div className='col-24'> |                     <span | ||||||
|                       <span onClick={toggleModal} style={{ cursor: 'pointer', position: 'absolute', left: 0 }}> |                       onClick={toggleModal} | ||||||
|                         <FontAwesomeIcon icon={faTimes} style={{ width: '1.5rem', color: 'red' }} /> |                       style={{ | ||||||
|                       </span> |                         cursor: 'pointer', | ||||||
|                       <h2 className='text-center'>Crée une nouvelle fonction</h2> |                         position: 'absolute', | ||||||
|                     </div> |                         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> | ||||||
|  |  | ||||||
|                 <div className='col-24'> |               <div className='col-24'> | ||||||
|                   <AddEditFunction defaultInputState={{ type: 'form' }} {...props} /> |                 <AddEditFunction | ||||||
|                 </div> |                   defaultInputState={{ type: 'form' }} | ||||||
|  |                   {...props} | ||||||
|  |                 /> | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|           </Modal> |           </div> | ||||||
|         ) |         </Modal> | ||||||
|  |       ) : ( | ||||||
|         : ( |         <FunctionsList isAdmin token={props.user.token}> | ||||||
|           <FunctionsList isAdmin token={props.user.token}> |           <div className='col-24'> | ||||||
|             <div className='col-24'> |             <h1 className='Functions__title'>Administration</h1> | ||||||
|               <h1 className='Functions__title'>Administration</h1> |             <button | ||||||
|               <button onClick={toggleModal} style={{ margin: '0 0 40px 0' }} className='btn btn-dark'>Crée une nouvelle fonction</button> |               onClick={toggleModal} | ||||||
|               <Link href='/admin/manageCategories'> |               style={{ margin: '0 0 40px 0' }} | ||||||
|                 <button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'>Gérer les catégories</button> |               className='btn btn-dark' | ||||||
|               </Link> |             > | ||||||
|               <Link href='/admin/manageQuotes'> |               Crée une nouvelle fonction | ||||||
|                 <button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'>Gérer les citations</button> |             </button> | ||||||
|               </Link> |             <Link href='/admin/manageCategories'> | ||||||
|             </div> |               <button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'> | ||||||
|           </FunctionsList> |                 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> | ||||||
|  |       )} | ||||||
|     </> |     </> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -15,23 +15,36 @@ import '../../public/css/pages/admin.css' | |||||||
|  |  | ||||||
| const defaultCategoryState = { name: '', color: '#ffffff' } | const defaultCategoryState = { name: '', color: '#ffffff' } | ||||||
|  |  | ||||||
| const AddEditCategory = (props) => { | const AddEditCategory = props => { | ||||||
|   const [inputState, setInputState] = useState(props.defaultInputState) |   const [inputState, setInputState] = useState(props.defaultInputState) | ||||||
|   const [message, setMessage] = useState('') |   const [message, setMessage] = useState('') | ||||||
|   const [isLoading, setIsLoading] = useState(false) |   const [isLoading, setIsLoading] = useState(false) | ||||||
|  |  | ||||||
|   const handleChange = (event, isTypeCheck = false) => { |   const handleChange = (event, isTypeCheck = false) => { | ||||||
|     const inputStateNew = { ...inputState } |     const inputStateNew = { ...inputState } | ||||||
|     inputStateNew[event.target.name] = (event.target.files != null) ? event.target.files[0] : (isTypeCheck) ? event.target.checked : event.target.value |     inputStateNew[event.target.name] = | ||||||
|  |       event.target.files != null | ||||||
|  |         ? event.target.files[0] | ||||||
|  |         : isTypeCheck | ||||||
|  |           ? event.target.checked | ||||||
|  |           : event.target.value | ||||||
|     setInputState(inputStateNew) |     setInputState(inputStateNew) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const apiCallCategory = () => { |   const apiCallCategory = () => { | ||||||
|     if (props.isEditing) return api.put(`/admin/categories/${inputState.id}`, { name: inputState.name, color: inputState.color }, { headers: { Authorization: props.user.token } }) |     if (props.isEditing) { | ||||||
|     return api.post('/admin/categories', inputState, { headers: { Authorization: props.user.token } }) |       return api.put( | ||||||
|  |         `/admin/categories/${inputState.id}`, | ||||||
|  |         { name: inputState.name, color: inputState.color }, | ||||||
|  |         { headers: { Authorization: props.user.token } } | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  |     return api.post('/admin/categories', inputState, { | ||||||
|  |       headers: { Authorization: props.user.token } | ||||||
|  |     }) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const handleSubmit = (event) => { |   const handleSubmit = event => { | ||||||
|     event.preventDefault() |     event.preventDefault() | ||||||
|     setIsLoading(true) |     setIsLoading(true) | ||||||
|     apiCallCategory() |     apiCallCategory() | ||||||
| @@ -39,8 +52,10 @@ const AddEditCategory = (props) => { | |||||||
|         setIsLoading(false) |         setIsLoading(false) | ||||||
|         window.location.reload(true) |         window.location.reload(true) | ||||||
|       }) |       }) | ||||||
|       .catch((error) => { |       .catch(error => { | ||||||
|         setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`) |         setMessage( | ||||||
|  |           `<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>` | ||||||
|  |         ) | ||||||
|         setIsLoading(false) |         setIsLoading(false) | ||||||
|       }) |       }) | ||||||
|   } |   } | ||||||
| @@ -51,10 +66,20 @@ const AddEditCategory = (props) => { | |||||||
|         <div className='col-24'> |         <div className='col-24'> | ||||||
|           <div className='Admin__Modal-top-container row'> |           <div className='Admin__Modal-top-container row'> | ||||||
|             <div className='col-24'> |             <div className='col-24'> | ||||||
|               <span onClick={props.handleToggleModal} style={{ cursor: 'pointer', position: 'absolute', left: 0 }}> |               <span | ||||||
|                 <FontAwesomeIcon icon={faTimes} style={{ width: '1.5rem', color: 'red' }} /> |                 onClick={props.handleToggleModal} | ||||||
|  |                 style={{ cursor: 'pointer', position: 'absolute', left: 0 }} | ||||||
|  |               > | ||||||
|  |                 <FontAwesomeIcon | ||||||
|  |                   icon={faTimes} | ||||||
|  |                   style={{ width: '1.5rem', color: 'red' }} | ||||||
|  |                 /> | ||||||
|               </span> |               </span> | ||||||
|               <h2 className='text-center'>{(props.isEditing) ? 'Modifier la catégorie' : 'Crée une nouvelle catégorie'}</h2> |               <h2 className='text-center'> | ||||||
|  |                 {props.isEditing | ||||||
|  |                   ? 'Modifier la catégorie' | ||||||
|  |                   : 'Crée une nouvelle catégorie'} | ||||||
|  |               </h2> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
| @@ -62,25 +87,39 @@ const AddEditCategory = (props) => { | |||||||
|         <div className='col-24'> |         <div className='col-24'> | ||||||
|           <form onSubmit={handleSubmit}> |           <form onSubmit={handleSubmit}> | ||||||
|             <div className='form-group'> |             <div className='form-group'> | ||||||
|               <label className='form-label' htmlFor='name'>Nom :</label> |               <label className='form-label' htmlFor='name'> | ||||||
|               <input value={inputState.name} onChange={handleChange} type='text' name='name' id='name' className='form-control' placeholder='(e.g : ✨ Utilitaires)' /> |                 Nom : | ||||||
|  |               </label> | ||||||
|  |               <input | ||||||
|  |                 value={inputState.name} | ||||||
|  |                 onChange={handleChange} | ||||||
|  |                 type='text' | ||||||
|  |                 name='name' | ||||||
|  |                 id='name' | ||||||
|  |                 className='form-control' | ||||||
|  |                 placeholder='(e.g : ✨ Utilitaires)' | ||||||
|  |               /> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|             <div className='form-group'> |             <div className='form-group'> | ||||||
|               <label className='form-label' htmlFor='title'>Couleur :</label> |               <label className='form-label' htmlFor='title'> | ||||||
|               <PhotoshopPicker color={inputState.color} onChange={(color) => handleChange({ target: { name: 'color', value: color.hex } })} /> |                 Couleur : | ||||||
|  |               </label> | ||||||
|  |               <PhotoshopPicker | ||||||
|  |                 color={inputState.color} | ||||||
|  |                 onChange={color => | ||||||
|  |                   handleChange({ target: { name: 'color', value: color.hex } })} | ||||||
|  |               /> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|             <div className='form-group text-center'> |             <div className='form-group text-center'> | ||||||
|               <button type='submit' className='btn btn-dark'>Envoyer</button> |               <button type='submit' className='btn btn-dark'> | ||||||
|  |                 Envoyer | ||||||
|  |               </button> | ||||||
|             </div> |             </div> | ||||||
|           </form> |           </form> | ||||||
|           <div className='form-result text-center'> |           <div className='form-result text-center'> | ||||||
|             { |             {isLoading ? <Loader /> : htmlParser(message)} | ||||||
|               (isLoading) |  | ||||||
|                 ? <Loader /> |  | ||||||
|                 : htmlParser(message) |  | ||||||
|             } |  | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
| @@ -88,22 +127,26 @@ const AddEditCategory = (props) => { | |||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| const manageCategories = (props) => { | const manageCategories = props => { | ||||||
|   const [, categories] = useAPI('/categories') |   const [, categories] = useAPI('/categories') | ||||||
|   const [isOpen, setIsOpen] = useState(false) |   const [isOpen, setIsOpen] = useState(false) | ||||||
|   const [isEditing, setIsEditing] = useState(false) |   const [isEditing, setIsEditing] = useState(false) | ||||||
|   const [defaultInputState, setDefaultInputState] = useState(defaultCategoryState) |   const [defaultInputState, setDefaultInputState] = useState( | ||||||
|  |     defaultCategoryState | ||||||
|  |   ) | ||||||
|  |  | ||||||
|   const toggleModal = () => setIsOpen(!isOpen) |   const toggleModal = () => setIsOpen(!isOpen) | ||||||
|  |  | ||||||
|   const handleRemoveCategory = async (categoryId) => { |   const handleRemoveCategory = async categoryId => { | ||||||
|     try { |     try { | ||||||
|       await api.delete(`/admin/categories/${categoryId}`, { headers: { Authorization: props.user.token } }) |       await api.delete(`/admin/categories/${categoryId}`, { | ||||||
|  |         headers: { Authorization: props.user.token } | ||||||
|  |       }) | ||||||
|       window.location.reload(true) |       window.location.reload(true) | ||||||
|     } catch {} |     } catch {} | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const handleEditCategory = (categoryInfo) => { |   const handleEditCategory = categoryInfo => { | ||||||
|     setDefaultInputState(categoryInfo) |     setDefaultInputState(categoryInfo) | ||||||
|     setIsEditing(true) |     setIsEditing(true) | ||||||
|     toggleModal() |     toggleModal() | ||||||
| @@ -111,64 +154,124 @@ const manageCategories = (props) => { | |||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <HeadTag title='Admin - FunctionProject' description="Page d'administration de FunctionProject. Gérer les catégories." /> |       <HeadTag | ||||||
|  |         title='Admin - FunctionProject' | ||||||
|  |         description="Page d'administration de FunctionProject. Gérer les catégories." | ||||||
|  |       /> | ||||||
|  |  | ||||||
|       { |       {isOpen ? ( | ||||||
|         (isOpen) |         <Modal> | ||||||
|           ? ( |           <AddEditCategory | ||||||
|             <Modal> |             handleToggleModal={toggleModal} | ||||||
|               <AddEditCategory handleToggleModal={toggleModal} defaultInputState={defaultInputState} {...props} isEditing={isEditing} /> |             defaultInputState={defaultInputState} | ||||||
|             </Modal> |             {...props} | ||||||
|           ) |             isEditing={isEditing} | ||||||
|           : ( |           /> | ||||||
|             <div className='container-fluid text-center'> |         </Modal> | ||||||
|               <div className='row justify-content-center'> |       ) : ( | ||||||
|                 <div className='col-24'> |         <div className='container-fluid text-center'> | ||||||
|                   <h1>Gérer les catégories</h1> |           <div className='row justify-content-center'> | ||||||
|                   <button onClick={() => { setDefaultInputState(defaultCategoryState); toggleModal(); setIsEditing(false) }} style={{ margin: '0 0 40px 0' }} className='btn btn-dark'>Ajouter une catégorie</button> |             <div className='col-24'> | ||||||
|                 </div> |               <h1>Gérer les catégories</h1> | ||||||
|               </div> |               <button | ||||||
|               <div className='row justify-content-center'> |                 onClick={() => { | ||||||
|                 <div className='container-fluid'> |                   setDefaultInputState(defaultCategoryState) | ||||||
|                   <div className='col-24 table-column'> |                   toggleModal() | ||||||
|                     <table className='table'> |                   setIsEditing(false) | ||||||
|                       <thead> |                 }} | ||||||
|                         <tr> |                 style={{ margin: '0 0 40px 0' }} | ||||||
|                           <th className='table-row' scope='col'>id</th> |                 className='btn btn-dark' | ||||||
|                           <th className='table-row' scope='col'>name</th> |               > | ||||||
|                           <th className='table-row' scope='col'>color</th> |                 Ajouter une catégorie | ||||||
|                           <th className='table-row' scope='col'>createdAt</th> |               </button> | ||||||
|                           <th className='table-row' scope='col'>updatedAt</th> |             </div> | ||||||
|                           <th className='table-row' scope='col'>Modifier</th> |           </div> | ||||||
|                           <th className='table-row' scope='col'>Supprimer</th> |           <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', | ||||||
|  |                               true | ||||||
|  |                             )} | ||||||
|  |                           </td> | ||||||
|  |                           <td className='table-row'> | ||||||
|  |                             {date.format( | ||||||
|  |                               new Date(category.updatedAt), | ||||||
|  |                               'DD/MM/YYYY à HH:mm', | ||||||
|  |                               true | ||||||
|  |                             )} | ||||||
|  |                           </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> |                         </tr> | ||||||
|                       </thead> |                       ) | ||||||
|                       <tbody> |                     })} | ||||||
|                         {categories.map((category) => { |                   </tbody> | ||||||
|                           return ( |                 </table> | ||||||
|                             <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', true)}</td> |  | ||||||
|                               <td className='table-row'>{date.format(new Date(category.updatedAt), 'DD/MM/YYYY à HH:mm', true)}</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> |             </div> | ||||||
|           ) |           </div> | ||||||
|       } |         </div> | ||||||
|  |       )} | ||||||
|     </> |     </> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,8 +8,12 @@ import HeadTag from '../../components/HeadTag' | |||||||
| import api from '../../utils/api' | import api from '../../utils/api' | ||||||
| import '../../public/css/pages/admin.css' | import '../../public/css/pages/admin.css' | ||||||
|  |  | ||||||
| const manageQuotes = (props) => { | const manageQuotes = props => { | ||||||
|   const [quotesData, setQuotesData] = useState({ hasMore: true, rows: [], totalItems: 0 }) |   const [quotesData, setQuotesData] = useState({ | ||||||
|  |     hasMore: true, | ||||||
|  |     rows: [], | ||||||
|  |     totalItems: 0 | ||||||
|  |   }) | ||||||
|   const [isLoadingQuotes, setLoadingQuotes] = useState(true) |   const [isLoadingQuotes, setLoadingQuotes] = useState(true) | ||||||
|   const [pageQuotes, setPageQuotes] = useState(1) |   const [pageQuotes, setPageQuotes] = useState(1) | ||||||
|  |  | ||||||
| @@ -20,7 +24,9 @@ const manageQuotes = (props) => { | |||||||
|  |  | ||||||
|   const getQuotesData = async () => { |   const getQuotesData = async () => { | ||||||
|     setLoadingQuotes(true) |     setLoadingQuotes(true) | ||||||
|     const { data } = await api.get(`/admin/quotes?limit=20page=${pageQuotes}`, { headers: { Authorization: props.user.token } }) |     const { data } = await api.get(`/admin/quotes?limit=20page=${pageQuotes}`, { | ||||||
|  |       headers: { Authorization: props.user.token } | ||||||
|  |     }) | ||||||
|     setQuotesData({ |     setQuotesData({ | ||||||
|       hasMore: data.hasMore, |       hasMore: data.hasMore, | ||||||
|       rows: [...quotesData.rows, ...data.rows], |       rows: [...quotesData.rows, ...data.rows], | ||||||
| @@ -31,33 +37,48 @@ const manageQuotes = (props) => { | |||||||
|  |  | ||||||
|   // Permet la pagination au scroll |   // Permet la pagination au scroll | ||||||
|   const observer = useRef() |   const observer = useRef() | ||||||
|   const lastQuoteRef = useCallback((node) => { |   const lastQuoteRef = useCallback( | ||||||
|     if (isLoadingQuotes) return |     node => { | ||||||
|     if (observer.current) observer.current.disconnect() |       if (isLoadingQuotes) return | ||||||
|     observer.current = new window.IntersectionObserver((entries) => { |       if (observer.current) observer.current.disconnect() | ||||||
|       if (entries[0].isIntersecting && quotesData.hasMore) { |       observer.current = new window.IntersectionObserver( | ||||||
|         setPageQuotes(pageQuotes + 1) |         entries => { | ||||||
|       } |           if (entries[0].isIntersecting && quotesData.hasMore) { | ||||||
|     }, { threshold: 1 }) |             setPageQuotes(pageQuotes + 1) | ||||||
|     if (node) observer.current.observe(node) |           } | ||||||
|   }, [isLoadingQuotes, quotesData.hasMore]) |         }, | ||||||
|  |         { threshold: 1 } | ||||||
|  |       ) | ||||||
|  |       if (node) observer.current.observe(node) | ||||||
|  |     }, | ||||||
|  |     [isLoadingQuotes, quotesData.hasMore] | ||||||
|  |   ) | ||||||
|  |  | ||||||
|   const handleValidationQuote = async (id, isValid) => { |   const handleValidationQuote = async (id, isValid) => { | ||||||
|     try { |     try { | ||||||
|       await api.put(`/admin/quotes/${id}`, { isValid }, { headers: { Authorization: props.user.token } }) |       await api.put( | ||||||
|  |         `/admin/quotes/${id}`, | ||||||
|  |         { isValid }, | ||||||
|  |         { headers: { Authorization: props.user.token } } | ||||||
|  |       ) | ||||||
|       window.location.reload(true) |       window.location.reload(true) | ||||||
|     } catch {} |     } catch {} | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <HeadTag title='Admin - FunctionProject' description="Page d'administration de FunctionProject. Gérer les citations." /> |       <HeadTag | ||||||
|  |         title='Admin - FunctionProject' | ||||||
|  |         description="Page d'administration de FunctionProject. Gérer les citations." | ||||||
|  |       /> | ||||||
|  |  | ||||||
|       <div className='container-fluid'> |       <div className='container-fluid'> | ||||||
|         <div className='row justify-content-center'> |         <div className='row justify-content-center'> | ||||||
|           <div className='col-24 text-center'> |           <div className='col-24 text-center'> | ||||||
|             <h2>Liste des citations (non validées) : </h2> |             <h2>Liste des citations (non validées) : </h2> | ||||||
|             <p style={{ marginTop: '5px' }}>Total de {quotesData.totalItems} citations.</p> |             <p style={{ marginTop: '5px' }}> | ||||||
|  |               Total de {quotesData.totalItems} citations. | ||||||
|  |             </p> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
| @@ -66,35 +87,72 @@ const manageQuotes = (props) => { | |||||||
|             <table className='table'> |             <table className='table'> | ||||||
|               <thead> |               <thead> | ||||||
|                 <tr> |                 <tr> | ||||||
|                   <th className='table-row' scope='col'>Citation/Proverbe</th> |                   <th className='table-row' scope='col'> | ||||||
|                   <th className='table-row' scope='col'>Auteur</th> |                     Citation/Proverbe | ||||||
|                   <th className='table-row' scope='col'>Proposée par</th> |                   </th> | ||||||
|                   <th className='table-row' scope='col'>Valider</th> |                   <th className='table-row' scope='col'> | ||||||
|                   <th className='table-row' scope='col'>Supprimer</th> |                     Auteur | ||||||
|  |                   </th> | ||||||
|  |                   <th className='table-row' scope='col'> | ||||||
|  |                     Proposée par | ||||||
|  |                   </th> | ||||||
|  |                   <th className='table-row' scope='col'> | ||||||
|  |                     Valider | ||||||
|  |                   </th> | ||||||
|  |                   <th className='table-row' scope='col'> | ||||||
|  |                     Supprimer | ||||||
|  |                   </th> | ||||||
|                 </tr> |                 </tr> | ||||||
|               </thead> |               </thead> | ||||||
|               <tbody> |               <tbody> | ||||||
|                 {quotesData.rows.map((currentQuote, index) => { |                 {quotesData.rows.map((currentQuote, index) => { | ||||||
|                   const quoteJSX = ( |                   const quoteJSX = ( | ||||||
|                     <> |                     <> | ||||||
|                       <td className='table-row text-center'>{currentQuote.quote}</td> |  | ||||||
|                       <td className='table-row text-center'>{currentQuote.author}</td> |  | ||||||
|                       <td className='table-row text-center'> |                       <td className='table-row text-center'> | ||||||
|                         <Link href='/users/[name]' as={`/users/${currentQuote.user.name}`}> |                         {currentQuote.quote} | ||||||
|  |                       </td> | ||||||
|  |                       <td className='table-row text-center'> | ||||||
|  |                         {currentQuote.author} | ||||||
|  |                       </td> | ||||||
|  |                       <td className='table-row text-center'> | ||||||
|  |                         <Link | ||||||
|  |                           href='/users/[name]' | ||||||
|  |                           as={`/users/${currentQuote.user.name}`} | ||||||
|  |                         > | ||||||
|                           <a>{currentQuote.user.name}</a> |                           <a>{currentQuote.user.name}</a> | ||||||
|                         </Link> |                         </Link> | ||||||
|                       </td> |                       </td> | ||||||
|                       <td onClick={() => handleValidationQuote(currentQuote.id, true)} className='table-row text-center' style={{ cursor: 'pointer' }}> |                       <td | ||||||
|                         <FontAwesomeIcon icon={faCheck} style={{ width: '1.5rem' }} /> |                         onClick={() => | ||||||
|  |                           handleValidationQuote(currentQuote.id, true)} | ||||||
|  |                         className='table-row text-center' | ||||||
|  |                         style={{ cursor: 'pointer' }} | ||||||
|  |                       > | ||||||
|  |                         <FontAwesomeIcon | ||||||
|  |                           icon={faCheck} | ||||||
|  |                           style={{ width: '1.5rem' }} | ||||||
|  |                         /> | ||||||
|                       </td> |                       </td> | ||||||
|                       <td onClick={() => handleValidationQuote(currentQuote.id, false)} className='table-row text-center' style={{ cursor: 'pointer' }}> |                       <td | ||||||
|                         <FontAwesomeIcon icon={faTrash} style={{ width: '1.5rem' }} /> |                         onClick={() => | ||||||
|  |                           handleValidationQuote(currentQuote.id, false)} | ||||||
|  |                         className='table-row text-center' | ||||||
|  |                         style={{ cursor: 'pointer' }} | ||||||
|  |                       > | ||||||
|  |                         <FontAwesomeIcon | ||||||
|  |                           icon={faTrash} | ||||||
|  |                           style={{ width: '1.5rem' }} | ||||||
|  |                         /> | ||||||
|                       </td> |                       </td> | ||||||
|                     </> |                     </> | ||||||
|                   ) |                   ) | ||||||
|                   // Si c'est le dernier élément |                   // Si c'est le dernier élément | ||||||
|                   if (quotesData.rows.length === index + 1) { |                   if (quotesData.rows.length === index + 1) { | ||||||
|                     return <tr key={index} ref={lastQuoteRef}>{quoteJSX}</tr> |                     return ( | ||||||
|  |                       <tr key={index} ref={lastQuoteRef}> | ||||||
|  |                         {quoteJSX} | ||||||
|  |                       </tr> | ||||||
|  |                     ) | ||||||
|                   } |                   } | ||||||
|                   return <tr key={index}>{quoteJSX}</tr> |                   return <tr key={index}>{quoteJSX}</tr> | ||||||
|                 })} |                 })} | ||||||
|   | |||||||
| @@ -7,12 +7,18 @@ import redirect from '../../utils/redirect' | |||||||
| import api from '../../utils/api' | import api from '../../utils/api' | ||||||
| import '../../public/css/pages/FunctionComponent.css' | import '../../public/css/pages/FunctionComponent.css' | ||||||
|  |  | ||||||
| const FunctionTabManager = (props) => { | const FunctionTabManager = props => { | ||||||
|   if (props.type === 'form') { |   if (props.type === 'form') { | ||||||
|     return ( |     return ( | ||||||
|       <FunctionTabs setSlideIndex={props.setSlideIndex} slideIndex={props.slideIndex}> |       <FunctionTabs | ||||||
|  |         setSlideIndex={props.setSlideIndex} | ||||||
|  |         slideIndex={props.slideIndex} | ||||||
|  |       > | ||||||
|         <div className='FunctionComponent__slide'> |         <div className='FunctionComponent__slide'> | ||||||
|           <FunctionForm inputsArray={[...props.utilizationForm || []]} slug={props.slug} /> |           <FunctionForm | ||||||
|  |             inputsArray={[...(props.utilizationForm || [])]} | ||||||
|  |             slug={props.slug} | ||||||
|  |           /> | ||||||
|         </div> |         </div> | ||||||
|         <div className='FunctionComponent__slide'> |         <div className='FunctionComponent__slide'> | ||||||
|           <FunctionArticle article={props.article} /> |           <FunctionArticle article={props.article} /> | ||||||
| @@ -25,7 +31,10 @@ const FunctionTabManager = (props) => { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <FunctionTabs setSlideIndex={props.setSlideIndex} slideIndex={props.slideIndex}> |     <FunctionTabs | ||||||
|  |       setSlideIndex={props.setSlideIndex} | ||||||
|  |       slideIndex={props.slideIndex} | ||||||
|  |     > | ||||||
|       <div className='FunctionComponent__slide'> |       <div className='FunctionComponent__slide'> | ||||||
|         <FunctionArticle article={props.article} /> |         <FunctionArticle article={props.article} /> | ||||||
|       </div> |       </div> | ||||||
| @@ -36,18 +45,23 @@ const FunctionTabManager = (props) => { | |||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| const FunctionComponent = (props) => ( | const FunctionComponent = props => ( | ||||||
|   <FunctionPage |   <FunctionPage | ||||||
|     FunctionTabManager={FunctionTabManager} |     FunctionTabManager={FunctionTabManager} | ||||||
|     {...props} |     {...props} | ||||||
|     tabNames={(props.type === 'form') ? ['⚙️ Utilisation', '📝 Article', '📬 Commentaires'] : ['📝 Article', '📬 Commentaires']} |     tabNames={ | ||||||
|  |       props.type === 'form' | ||||||
|  |         ? ['⚙️ Utilisation', '📝 Article', '📬 Commentaires'] | ||||||
|  |         : ['📝 Article', '📬 Commentaires'] | ||||||
|  |     } | ||||||
|   /> |   /> | ||||||
| ) | ) | ||||||
|  |  | ||||||
| export async function getServerSideProps (context) { | export async function getServerSideProps (context) { | ||||||
|   const { slug } = context.params |   const { slug } = context.params | ||||||
|   return api.get(`/functions/${slug}`) |   return api | ||||||
|     .then((response) => ({ props: response.data })) |     .get(`/functions/${slug}`) | ||||||
|  |     .then(response => ({ props: response.data })) | ||||||
|     .catch(() => redirect(context, '/404')) |     .catch(() => redirect(context, '/404')) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ const Chronometer = () => { | |||||||
|     } else { |     } else { | ||||||
|       if (interval) clearInterval(interval) |       if (interval) clearInterval(interval) | ||||||
|       interval = setInterval(() => { |       interval = setInterval(() => { | ||||||
|         setTimeLength((time) => time + 1) |         setTimeLength(time => time + 1) | ||||||
|       }, 1000) |       }, 1000) | ||||||
|     } |     } | ||||||
|     setIsPlaying(!isPlaying) |     setIsPlaying(!isPlaying) | ||||||
| @@ -44,7 +44,10 @@ const Chronometer = () => { | |||||||
|  |  | ||||||
|   const getFormattedValue = () => { |   const getFormattedValue = () => { | ||||||
|     const minutesAndSeconds = convertSeconds(timeLength) |     const minutesAndSeconds = convertSeconds(timeLength) | ||||||
|     const minutes = (minutesAndSeconds.minutes < 100) ? (('0' + minutesAndSeconds.minutes).slice(-2)) : minutesAndSeconds.minutes |     const minutes = | ||||||
|  |       minutesAndSeconds.minutes < 100 | ||||||
|  |         ? ('0' + minutesAndSeconds.minutes).slice(-2) | ||||||
|  |         : minutesAndSeconds.minutes | ||||||
|     const seconds = ('0' + minutesAndSeconds.seconds).slice(-2) |     const seconds = ('0' + minutesAndSeconds.seconds).slice(-2) | ||||||
|     return `${minutes}:${seconds}` |     return `${minutes}:${seconds}` | ||||||
|   } |   } | ||||||
| @@ -65,9 +68,15 @@ const Chronometer = () => { | |||||||
|             <div className='Chronometer__item Chronometer__buttons'> |             <div className='Chronometer__item Chronometer__buttons'> | ||||||
|               <div className='Chronomter__row Chronometer__row-button'> |               <div className='Chronomter__row Chronometer__row-button'> | ||||||
|                 <button onClick={handlePlayPause} className='Chronometer-btn'> |                 <button onClick={handlePlayPause} className='Chronometer-btn'> | ||||||
|                   <FontAwesomeIcon {...(isPlaying) ? { icon: faPause } : { icon: faPlay }} /> |                   <FontAwesomeIcon | ||||||
|  |                     {...(isPlaying ? { icon: faPause } : { icon: faPlay })} | ||||||
|  |                   /> | ||||||
|                 </button> |                 </button> | ||||||
|                 <button onClick={handleReset} className='Chronometer-btn' title='Remettre à zéro ?'> |                 <button | ||||||
|  |                   onClick={handleReset} | ||||||
|  |                   className='Chronometer-btn' | ||||||
|  |                   title='Remettre à zéro ?' | ||||||
|  |                 > | ||||||
|                   <FontAwesomeIcon icon={faSync} /> |                   <FontAwesomeIcon icon={faSync} /> | ||||||
|                 </button> |                 </button> | ||||||
|               </div> |               </div> | ||||||
| @@ -82,14 +91,24 @@ const Chronometer = () => { | |||||||
| const Pomodoro = () => { | const Pomodoro = () => { | ||||||
|   return ( |   return ( | ||||||
|     <div style={{ marginBottom: '50px' }} className='container-fluid'> |     <div style={{ marginBottom: '50px' }} className='container-fluid'> | ||||||
|       <Codepen hash='vYEbPoB' user='Divlo' height={800} defaultTab='result' preview={false} loader={() => <Loader />} /> |       <Codepen | ||||||
|  |         hash='vYEbPoB' | ||||||
|  |         user='Divlo' | ||||||
|  |         height={800} | ||||||
|  |         defaultTab='result' | ||||||
|  |         preview={false} | ||||||
|  |         loader={() => <Loader />} | ||||||
|  |       /> | ||||||
|     </div> |     </div> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| const FunctionTabManager = (props) => { | const FunctionTabManager = props => { | ||||||
|   return ( |   return ( | ||||||
|     <FunctionTabs setSlideIndex={props.setSlideIndex} slideIndex={props.slideIndex}> |     <FunctionTabs | ||||||
|  |       setSlideIndex={props.setSlideIndex} | ||||||
|  |       slideIndex={props.slideIndex} | ||||||
|  |     > | ||||||
|       <div className='FunctionComponent__slide'> |       <div className='FunctionComponent__slide'> | ||||||
|         <Chronometer /> |         <Chronometer /> | ||||||
|       </div> |       </div> | ||||||
| @@ -106,17 +125,23 @@ const FunctionTabManager = (props) => { | |||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| const chronometerTimer = (props) => ( | const chronometerTimer = props => ( | ||||||
|   <FunctionPage |   <FunctionPage | ||||||
|     FunctionTabManager={FunctionTabManager} |     FunctionTabManager={FunctionTabManager} | ||||||
|     {...props} |     {...props} | ||||||
|     tabNames={['⏰ Chronomètre', '⌛ Pomodoro', '📝 Article', '📬 Commentaires']} |     tabNames={[ | ||||||
|  |       '⏰ Chronomètre', | ||||||
|  |       '⌛ Pomodoro', | ||||||
|  |       '📝 Article', | ||||||
|  |       '📬 Commentaires' | ||||||
|  |     ]} | ||||||
|   /> |   /> | ||||||
| ) | ) | ||||||
|  |  | ||||||
| export async function getServerSideProps (context) { | export async function getServerSideProps (context) { | ||||||
|   return api.get('/functions/chronometerTimer') |   return api | ||||||
|     .then((response) => ({ props: response.data })) |     .get('/functions/chronometerTimer') | ||||||
|  |     .then(response => ({ props: response.data })) | ||||||
|     .catch(() => redirect(context, '/404')) |     .catch(() => redirect(context, '/404')) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -43,8 +43,16 @@ const GenerateQuote = () => { | |||||||
|     <div className='container-fluid'> |     <div className='container-fluid'> | ||||||
|       <div className='row justify-content-center'> |       <div className='row justify-content-center'> | ||||||
|         <div className='col-24 text-center'> |         <div className='col-24 text-center'> | ||||||
|           <button onClick={getRandomQuote} className='btn btn-dark'>Générer une nouvelle citation</button> |           <button onClick={getRandomQuote} className='btn btn-dark'> | ||||||
|           <button style={{ marginLeft: '15px' }} onClick={handleCopyQuote} className='btn btn-dark'>Copier la citation</button> |             Générer une nouvelle citation | ||||||
|  |           </button> | ||||||
|  |           <button | ||||||
|  |             style={{ marginLeft: '15px' }} | ||||||
|  |             onClick={handleCopyQuote} | ||||||
|  |             className='btn btn-dark' | ||||||
|  |           > | ||||||
|  |             Copier la citation | ||||||
|  |           </button> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div style={{ marginTop: '20px' }} className='row justify-content-center'> |       <div style={{ marginTop: '20px' }} className='row justify-content-center'> | ||||||
| @@ -53,7 +61,10 @@ const GenerateQuote = () => { | |||||||
|           <p>- {quote?.author}</p> |           <p>- {quote?.author}</p> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div style={{ marginBottom: '20px' }} className='row justify-content-center'> |       <div | ||||||
|  |         style={{ marginBottom: '20px' }} | ||||||
|  |         className='row justify-content-center' | ||||||
|  |       > | ||||||
|         <a |         <a | ||||||
|           target='_blank' |           target='_blank' | ||||||
|           rel='noopener noreferrer' |           rel='noopener noreferrer' | ||||||
| @@ -69,12 +80,16 @@ const GenerateQuote = () => { | |||||||
|  |  | ||||||
| let pageQuotes = 1 | let pageQuotes = 1 | ||||||
| const QuoteList = () => { | const QuoteList = () => { | ||||||
|   const [quotesData, setQuotesData] = useState({ hasMore: true, rows: [], totalItems: 0 }) |   const [quotesData, setQuotesData] = useState({ | ||||||
|  |     hasMore: true, | ||||||
|  |     rows: [], | ||||||
|  |     totalItems: 0 | ||||||
|  |   }) | ||||||
|   const [isLoadingQuotes, setLoadingQuotes] = useState(true) |   const [isLoadingQuotes, setLoadingQuotes] = useState(true) | ||||||
|  |  | ||||||
|   // Récupère les citations initiales |   // Récupère les citations initiales | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     getQuotesData().then((data) => setQuotesData(data)) |     getQuotesData().then(data => setQuotesData(data)) | ||||||
|   }, []) |   }, []) | ||||||
|  |  | ||||||
|   const getQuotesData = async () => { |   const getQuotesData = async () => { | ||||||
| @@ -86,32 +101,40 @@ const QuoteList = () => { | |||||||
|  |  | ||||||
|   // Permet la pagination au scroll |   // Permet la pagination au scroll | ||||||
|   const observer = useRef() |   const observer = useRef() | ||||||
|   const lastQuoteRef = useCallback((node) => { |   const lastQuoteRef = useCallback( | ||||||
|     if (isLoadingQuotes) return |     node => { | ||||||
|     if (observer.current) observer.current.disconnect() |       if (isLoadingQuotes) return | ||||||
|     observer.current = new window.IntersectionObserver((entries) => { |       if (observer.current) observer.current.disconnect() | ||||||
|       if (entries[0].isIntersecting && quotesData.hasMore) { |       observer.current = new window.IntersectionObserver( | ||||||
|         pageQuotes += 1 |         entries => { | ||||||
|         getQuotesData().then((data) => { |           if (entries[0].isIntersecting && quotesData.hasMore) { | ||||||
|           setQuotesData((oldData) => { |             pageQuotes += 1 | ||||||
|             return { |             getQuotesData().then(data => { | ||||||
|               hasMore: data.hasMore, |               setQuotesData(oldData => { | ||||||
|               rows: [...oldData.rows, ...data.rows], |                 return { | ||||||
|               totalItems: data.totalItems |                   hasMore: data.hasMore, | ||||||
|             } |                   rows: [...oldData.rows, ...data.rows], | ||||||
|           }) |                   totalItems: data.totalItems | ||||||
|         }) |                 } | ||||||
|       } |               }) | ||||||
|     }, { threshold: 1 }) |             }) | ||||||
|     if (node) observer.current.observe(node) |           } | ||||||
|   }, [isLoadingQuotes, quotesData.hasMore]) |         }, | ||||||
|  |         { threshold: 1 } | ||||||
|  |       ) | ||||||
|  |       if (node) observer.current.observe(node) | ||||||
|  |     }, | ||||||
|  |     [isLoadingQuotes, quotesData.hasMore] | ||||||
|  |   ) | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div className='container-fluid'> |     <div className='container-fluid'> | ||||||
|       <div className='row justify-content-center'> |       <div className='row justify-content-center'> | ||||||
|         <div className='col-24 text-center'> |         <div className='col-24 text-center'> | ||||||
|           <h2 style={{ margin: 0 }}>Liste des citations : </h2> |           <h2 style={{ margin: 0 }}>Liste des citations : </h2> | ||||||
|           <p style={{ marginTop: '5px' }}>Total de {quotesData.totalItems} citations.</p> |           <p style={{ marginTop: '5px' }}> | ||||||
|  |             Total de {quotesData.totalItems} citations. | ||||||
|  |           </p> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
| @@ -120,19 +143,32 @@ const QuoteList = () => { | |||||||
|           <table> |           <table> | ||||||
|             <thead> |             <thead> | ||||||
|               <tr> |               <tr> | ||||||
|                 <th className='table-row' scope='col'>Citation/Proverbe</th> |                 <th className='table-row' scope='col'> | ||||||
|                 <th className='table-row' scope='col'>Auteur</th> |                   Citation/Proverbe | ||||||
|                 <th className='table-row' scope='col'>Proposée par</th> |                 </th> | ||||||
|  |                 <th className='table-row' scope='col'> | ||||||
|  |                   Auteur | ||||||
|  |                 </th> | ||||||
|  |                 <th className='table-row' scope='col'> | ||||||
|  |                   Proposée par | ||||||
|  |                 </th> | ||||||
|               </tr> |               </tr> | ||||||
|             </thead> |             </thead> | ||||||
|             <tbody> |             <tbody> | ||||||
|               {quotesData.rows.map((currentQuote, index) => { |               {quotesData.rows.map((currentQuote, index) => { | ||||||
|                 const quoteJSX = ( |                 const quoteJSX = ( | ||||||
|                   <> |                   <> | ||||||
|                     <td className='table-row text-center'>{currentQuote.quote}</td> |  | ||||||
|                     <td className='table-row text-center'>{currentQuote.author}</td> |  | ||||||
|                     <td className='table-row text-center'> |                     <td className='table-row text-center'> | ||||||
|                       <Link href='/users/[name]' as={`/users/${currentQuote.user.name}`}> |                       {currentQuote.quote} | ||||||
|  |                     </td> | ||||||
|  |                     <td className='table-row text-center'> | ||||||
|  |                       {currentQuote.author} | ||||||
|  |                     </td> | ||||||
|  |                     <td className='table-row text-center'> | ||||||
|  |                       <Link | ||||||
|  |                         href='/users/[name]' | ||||||
|  |                         as={`/users/${currentQuote.user.name}`} | ||||||
|  |                       > | ||||||
|                         <a>{currentQuote.user.name}</a> |                         <a>{currentQuote.user.name}</a> | ||||||
|                       </Link> |                       </Link> | ||||||
|                     </td> |                     </td> | ||||||
| @@ -140,7 +176,11 @@ const QuoteList = () => { | |||||||
|                 ) |                 ) | ||||||
|                 // Si c'est le dernier élément |                 // Si c'est le dernier élément | ||||||
|                 if (quotesData.rows.length === index + 1) { |                 if (quotesData.rows.length === index + 1) { | ||||||
|                   return <tr key={index} ref={lastQuoteRef}>{quoteJSX}</tr> |                   return ( | ||||||
|  |                     <tr key={index} ref={lastQuoteRef}> | ||||||
|  |                       {quoteJSX} | ||||||
|  |                     </tr> | ||||||
|  |                   ) | ||||||
|                 } |                 } | ||||||
|                 return <tr key={index}>{quoteJSX}</tr> |                 return <tr key={index}>{quoteJSX}</tr> | ||||||
|               })} |               })} | ||||||
| @@ -158,25 +198,30 @@ const SuggestQuote = () => { | |||||||
|   const [message, setMessage] = useState('') |   const [message, setMessage] = useState('') | ||||||
|   const [isLoading, setIsLoading] = useState(false) |   const [isLoading, setIsLoading] = useState(false) | ||||||
|  |  | ||||||
|   const handleChange = (event) => { |   const handleChange = event => { | ||||||
|     const inputStateNew = { ...inputState } |     const inputStateNew = { ...inputState } | ||||||
|     inputStateNew[event.target.name] = event.target.value |     inputStateNew[event.target.name] = event.target.value | ||||||
|     setInputState(inputStateNew) |     setInputState(inputStateNew) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const handleSubmit = (event) => { |   const handleSubmit = event => { | ||||||
|     setIsLoading(true) |     setIsLoading(true) | ||||||
|     event.preventDefault() |     event.preventDefault() | ||||||
|     const token = user.token |     const token = user.token | ||||||
|     if (isAuth && token != null) { |     if (isAuth && token != null) { | ||||||
|       api.post('/quotes', inputState, { headers: { Authorization: token } }) |       api | ||||||
|  |         .post('/quotes', inputState, { headers: { Authorization: token } }) | ||||||
|         .then(({ data }) => { |         .then(({ data }) => { | ||||||
|           setInputState({ quote: '', author: '' }) |           setInputState({ quote: '', author: '' }) | ||||||
|           setMessage(`<p class="form-success"><b>Succès:</b> ${data.message}</p>`) |           setMessage( | ||||||
|  |             `<p class="form-success"><b>Succès:</b> ${data.message}</p>` | ||||||
|  |           ) | ||||||
|           setIsLoading(false) |           setIsLoading(false) | ||||||
|         }) |         }) | ||||||
|         .catch((error) => { |         .catch(error => { | ||||||
|           setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`) |           setMessage( | ||||||
|  |             `<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>` | ||||||
|  |           ) | ||||||
|           setIsLoading(false) |           setIsLoading(false) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| @@ -185,7 +230,11 @@ const SuggestQuote = () => { | |||||||
|   if (!isAuth) { |   if (!isAuth) { | ||||||
|     return ( |     return ( | ||||||
|       <p className='text-center'> |       <p className='text-center'> | ||||||
|                 Vous devez être <Link href='/users/login'><a>connecté</a></Link> pour proposer une citation. |         Vous devez être{' '} | ||||||
|  |         <Link href='/users/login'> | ||||||
|  |           <a>connecté</a> | ||||||
|  |         </Link>{' '} | ||||||
|  |         pour proposer une citation. | ||||||
|       </p> |       </p> | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
| @@ -195,42 +244,68 @@ const SuggestQuote = () => { | |||||||
|       <div className='row justify-content-center'> |       <div className='row justify-content-center'> | ||||||
|         <div className='col-24 text-center'> |         <div className='col-24 text-center'> | ||||||
|           <h2 style={{ margin: 0 }}>Proposer une citation : </h2> |           <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> |           <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> |       </div> | ||||||
|       <div style={{ marginBottom: '40px' }} className='row'> |       <div style={{ marginBottom: '40px' }} className='row'> | ||||||
|         <div className='col-24'> |         <div className='col-24'> | ||||||
|           <form onSubmit={handleSubmit}> |           <form onSubmit={handleSubmit}> | ||||||
|             <div className='form-group'> |             <div className='form-group'> | ||||||
|               <label htmlFor='quote' className='form-label'>Citation :</label> |               <label htmlFor='quote' className='form-label'> | ||||||
|               <textarea value={inputState.quote} onChange={handleChange} style={{ height: 'auto' }} id='quote' name='quote' type='text' className='form-control' rows='4' placeholder='La citation...' /> |                 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> | ||||||
|  |  | ||||||
|             <div className='form-group'> |             <div className='form-group'> | ||||||
|               <label htmlFor='author' className='form-label'>Auteur :</label> |               <label htmlFor='author' className='form-label'> | ||||||
|               <input value={inputState.author} onChange={handleChange} name='author' id='author' type='text' className='form-control' placeholder="L'auteur de la citation..." /> |                 Auteur : | ||||||
|  |               </label> | ||||||
|  |               <input | ||||||
|  |                 value={inputState.author} | ||||||
|  |                 onChange={handleChange} | ||||||
|  |                 name='author' | ||||||
|  |                 id='author' | ||||||
|  |                 type='text' | ||||||
|  |                 className='form-control' | ||||||
|  |                 placeholder="L'auteur de la citation..." | ||||||
|  |               /> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|             <div className='form-group text-center'> |             <div className='form-group text-center'> | ||||||
|               <button type='submit' className='btn btn-dark'>Envoyer</button> |               <button type='submit' className='btn btn-dark'> | ||||||
|  |                 Envoyer | ||||||
|  |               </button> | ||||||
|             </div> |             </div> | ||||||
|           </form> |           </form> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div className='form-result text-center'> |       <div className='form-result text-center'> | ||||||
|         { |         {isLoading ? <Loader /> : htmlParser(message)} | ||||||
|           (isLoading) |  | ||||||
|             ? <Loader /> |  | ||||||
|             : htmlParser(message) |  | ||||||
|         } |  | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| const FunctionTabManager = (props) => { | const FunctionTabManager = props => { | ||||||
|   return ( |   return ( | ||||||
|     <FunctionTabs setSlideIndex={props.setSlideIndex} slideIndex={props.slideIndex}> |     <FunctionTabs | ||||||
|  |       setSlideIndex={props.setSlideIndex} | ||||||
|  |       slideIndex={props.slideIndex} | ||||||
|  |     > | ||||||
|       <div className='FunctionComponent__slide'> |       <div className='FunctionComponent__slide'> | ||||||
|         <GenerateQuote /> |         <GenerateQuote /> | ||||||
|       </div> |       </div> | ||||||
| @@ -250,17 +325,24 @@ const FunctionTabManager = (props) => { | |||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| const randomQuote = (props) => ( | const randomQuote = props => ( | ||||||
|   <FunctionPage |   <FunctionPage | ||||||
|     FunctionTabManager={FunctionTabManager} |     FunctionTabManager={FunctionTabManager} | ||||||
|     {...props} |     {...props} | ||||||
|     tabNames={['⚙️ Utilisation', '📜 Liste', '✒️ Proposer', '📝 Article', '📬 Commentaires']} |     tabNames={[ | ||||||
|  |       '⚙️ Utilisation', | ||||||
|  |       '📜 Liste', | ||||||
|  |       '✒️ Proposer', | ||||||
|  |       '📝 Article', | ||||||
|  |       '📬 Commentaires' | ||||||
|  |     ]} | ||||||
|   /> |   /> | ||||||
| ) | ) | ||||||
|  |  | ||||||
| export async function getServerSideProps (context) { | export async function getServerSideProps (context) { | ||||||
|   return api.get('/functions/randomQuote') |   return api | ||||||
|     .then((response) => ({ props: response.data })) |     .get('/functions/randomQuote') | ||||||
|  |     .then(response => ({ props: response.data })) | ||||||
|     .catch(() => redirect(context, '/404')) |     .catch(() => redirect(context, '/404')) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -29,14 +29,14 @@ const PlayRightPrice = () => { | |||||||
|     setIsLoadingProduct(false) |     setIsLoadingProduct(false) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const handleChange = (event) => { |   const handleChange = event => { | ||||||
|     setEnteredPrice(event.target.value) |     setEnteredPrice(event.target.value) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const handleSubmit = async (event) => { |   const handleSubmit = async event => { | ||||||
|     event.preventDefault() |     event.preventDefault() | ||||||
|     const objectTry = {} |     const objectTry = {} | ||||||
|     const guessedPrice = Number((enteredPrice).replace(',', '.').replace(' ', '')) |     const guessedPrice = Number(enteredPrice.replace(',', '.').replace(' ', '')) | ||||||
|     if (!isNaN(guessedPrice)) { |     if (!isNaN(guessedPrice)) { | ||||||
|       objectTry.guessedPrice = guessedPrice |       objectTry.guessedPrice = guessedPrice | ||||||
|       objectTry.numberTry = attemptsArray.length + 1 |       objectTry.numberTry = attemptsArray.length + 1 | ||||||
| @@ -54,80 +54,114 @@ const PlayRightPrice = () => { | |||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div className='container-fluid'> |     <div className='container-fluid'> | ||||||
|       { |       {!isPlaying ? ( | ||||||
|         (!isPlaying) |         <div className='row justify-content-center'> | ||||||
|           ? ( |           <div className='form-group text-center'> | ||||||
|             <div className='row justify-content-center'> |             <button | ||||||
|               <div className='form-group text-center'> |               onClick={handlePlaying} | ||||||
|                 <button onClick={handlePlaying} type='submit' className='btn btn-dark'>Jouer</button> |               type='submit' | ||||||
|               </div> |               className='btn btn-dark' | ||||||
|  |             > | ||||||
|  |               Jouer | ||||||
|  |             </button> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       ) : isLoadingProduct ? ( | ||||||
|  |         <div className='row justify-content-center'> | ||||||
|  |           <Loader /> | ||||||
|  |         </div> | ||||||
|  |       ) : ( | ||||||
|  |         <> | ||||||
|  |           <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> |             </div> | ||||||
|           ) |           </div> | ||||||
|           : (isLoadingProduct) |  | ||||||
|             ? ( |           <div className='row justify-content-center'> | ||||||
|               <div className='row justify-content-center'> |             <div style={{ marginBottom: '25px' }} className='col-24'> | ||||||
|                 <Loader /> |               {attemptsArray.length > 0 && | ||||||
|               </div> |               attemptsArray[0].message === | ||||||
|             ) |                 'Bravo, vous avez trouvé le juste prix !' ? ( | ||||||
|             : ( |                   <div className='form-group text-center'> | ||||||
|               <> |                     <button | ||||||
|                 <div className='row justify-content-center'> |                       onClick={handlePlaying} | ||||||
|                   <div style={{ marginBottom: '20px' }} className='col-24 text-center'> |                       type='submit' | ||||||
|                     <h4>{productToGuess.name}</h4> |                       className='btn btn-dark' | ||||||
|                     <img src={productToGuess.image} alt={productToGuess.name} className='Product__image' /> |                     > | ||||||
|  |                     Rejouer ? | ||||||
|  |                     </button> | ||||||
|                   </div> |                   </div> | ||||||
|                 </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='row justify-content-center'> |                     <div className='form-group text-center'> | ||||||
|                   <div style={{ marginBottom: '25px' }} className='col-24'> |                       <button type='submit' className='btn btn-dark'> | ||||||
|                     {((attemptsArray.length > 0) && attemptsArray[0].message === 'Bravo, vous avez trouvé le juste prix !') |                       Deviner | ||||||
|                       ? ( |                       </button> | ||||||
|                         <div className='form-group text-center'> |                     </div> | ||||||
|                           <button onClick={handlePlaying} type='submit' className='btn btn-dark'>Rejouer ?</button> |                   </form> | ||||||
|                         </div> |                 )} | ||||||
|                       ) |             </div> | ||||||
|                       : ( |           </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'> |           <div | ||||||
|                             <button type='submit' className='btn btn-dark'>Deviner</button> |             style={{ marginBottom: '30px' }} | ||||||
|                           </div> |             className='row justify-content-center' | ||||||
|                         </form> |           > | ||||||
|                       )} |             {attemptsArray.map((attempt, index) => { | ||||||
|                   </div> |               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 style={{ marginBottom: '30px' }} className='row justify-content-center'> |             })} | ||||||
|                   {attemptsArray.map((attempt, index) => { |           </div> | ||||||
|                     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> |     </div> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| const FunctionTabManager = (props) => { | const FunctionTabManager = props => { | ||||||
|   return ( |   return ( | ||||||
|     <FunctionTabs setSlideIndex={props.setSlideIndex} slideIndex={props.slideIndex}> |     <FunctionTabs | ||||||
|  |       setSlideIndex={props.setSlideIndex} | ||||||
|  |       slideIndex={props.slideIndex} | ||||||
|  |     > | ||||||
|       <div style={{ marginTop: '10px' }}> |       <div style={{ marginTop: '10px' }}> | ||||||
|         <PlayRightPrice /> |         <PlayRightPrice /> | ||||||
|       </div> |       </div> | ||||||
| @@ -141,7 +175,7 @@ const FunctionTabManager = (props) => { | |||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| const rightPrice = (props) => ( | const rightPrice = props => ( | ||||||
|   <FunctionPage |   <FunctionPage | ||||||
|     FunctionTabManager={FunctionTabManager} |     FunctionTabManager={FunctionTabManager} | ||||||
|     {...props} |     {...props} | ||||||
| @@ -150,8 +184,9 @@ const rightPrice = (props) => ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| export async function getServerSideProps (context) { | export async function getServerSideProps (context) { | ||||||
|   return api.get('/functions/rightPrice') |   return api | ||||||
|     .then((response) => ({ props: response.data })) |     .get('/functions/rightPrice') | ||||||
|  |     .then(response => ({ props: response.data })) | ||||||
|     .catch(() => redirect(context, '/404')) |     .catch(() => redirect(context, '/404')) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,7 +19,9 @@ const ManageToDo = () => { | |||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     const getTasks = async () => { |     const getTasks = async () => { | ||||||
|       const { data } = await api.get('/tasks', { headers: { Authorization: user.token } }) |       const { data } = await api.get('/tasks', { | ||||||
|  |         headers: { Authorization: user.token } | ||||||
|  |       }) | ||||||
|       setTasks(data) |       setTasks(data) | ||||||
|     } |     } | ||||||
|     if (isAuth && user.token != null) { |     if (isAuth && user.token != null) { | ||||||
| @@ -27,16 +29,18 @@ const ManageToDo = () => { | |||||||
|     } |     } | ||||||
|   }, [isAuth]) |   }, [isAuth]) | ||||||
|  |  | ||||||
|   const handleChange = (event) => { |   const handleChange = event => { | ||||||
|     const inputStateNew = { ...inputState } |     const inputStateNew = { ...inputState } | ||||||
|     inputStateNew[event.target.name] = event.target.value |     inputStateNew[event.target.name] = event.target.value | ||||||
|     setInputState(inputStateNew) |     setInputState(inputStateNew) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const handleSubmit = async (event) => { |   const handleSubmit = async event => { | ||||||
|     event.preventDefault() |     event.preventDefault() | ||||||
|     try { |     try { | ||||||
|       const { data } = await api.post('/tasks', inputState, { headers: { Authorization: user.token } }) |       const { data } = await api.post('/tasks', inputState, { | ||||||
|  |         headers: { Authorization: user.token } | ||||||
|  |       }) | ||||||
|       const newTasks = [...tasks] |       const newTasks = [...tasks] | ||||||
|       newTasks.push(data) |       newTasks.push(data) | ||||||
|       setTasks(newTasks) |       setTasks(newTasks) | ||||||
| @@ -47,7 +51,9 @@ const ManageToDo = () => { | |||||||
|   const handleRemoveTask = async (id, index) => { |   const handleRemoveTask = async (id, index) => { | ||||||
|     const newTasks = [...tasks] |     const newTasks = [...tasks] | ||||||
|     try { |     try { | ||||||
|       await api.delete(`/tasks/${id}`, { headers: { Authorization: user.token } }) |       await api.delete(`/tasks/${id}`, { | ||||||
|  |         headers: { Authorization: user.token } | ||||||
|  |       }) | ||||||
|       newTasks.splice(index, 1) |       newTasks.splice(index, 1) | ||||||
|       setTasks(newTasks) |       setTasks(newTasks) | ||||||
|     } catch {} |     } catch {} | ||||||
| @@ -55,7 +61,11 @@ const ManageToDo = () => { | |||||||
|  |  | ||||||
|   const handleEditTask = async (id, index, isCompleted) => { |   const handleEditTask = async (id, index, isCompleted) => { | ||||||
|     try { |     try { | ||||||
|       await api.put(`/tasks/${id}`, { isCompleted: !isCompleted }, { headers: { Authorization: user.token } }) |       await api.put( | ||||||
|  |         `/tasks/${id}`, | ||||||
|  |         { isCompleted: !isCompleted }, | ||||||
|  |         { headers: { Authorization: user.token } } | ||||||
|  |       ) | ||||||
|       const newTasks = [...tasks] |       const newTasks = [...tasks] | ||||||
|       const taskObject = newTasks[index] |       const taskObject = newTasks[index] | ||||||
|       taskObject.isCompleted = !isCompleted |       taskObject.isCompleted = !isCompleted | ||||||
| @@ -66,7 +76,11 @@ const ManageToDo = () => { | |||||||
|   if (!isAuth) { |   if (!isAuth) { | ||||||
|     return ( |     return ( | ||||||
|       <p className='text-center'> |       <p className='text-center'> | ||||||
|                 Vous devez être <Link href='/users/login'><a>connecté</a></Link> pour gérer des "tâches à faire". |         Vous devez être{' '} | ||||||
|  |         <Link href='/users/login'> | ||||||
|  |           <a>connecté</a> | ||||||
|  |         </Link>{' '} | ||||||
|  |         pour gérer des "tâches à faire". | ||||||
|       </p> |       </p> | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
| @@ -77,31 +91,62 @@ const ManageToDo = () => { | |||||||
|         <div className='col-24'> |         <div className='col-24'> | ||||||
|           <form onSubmit={handleSubmit}> |           <form onSubmit={handleSubmit}> | ||||||
|             <div className='text-center'> |             <div className='text-center'> | ||||||
|               <label htmlFor='task' className='form-label'>Ajouter une tâche à faire :</label> |               <label htmlFor='task' className='form-label'> | ||||||
|               <input value={inputState.task} onChange={handleChange} name='task' id='task' type='text' className='form-control' placeholder='(e.g : Apprendre à coder)' /> |                 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> | ||||||
|  |  | ||||||
|             <div className='form-group text-center'> |             <div className='form-group text-center'> | ||||||
|               <button type='submit' className='btn btn-dark'>Envoyer</button> |               <button type='submit' className='btn btn-dark'> | ||||||
|  |                 Envoyer | ||||||
|  |               </button> | ||||||
|             </div> |             </div> | ||||||
|           </form> |           </form> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       {(tasks.length > 0) && |       {tasks.length > 0 && ( | ||||||
|         <div className='row justify-content-center'> |         <div className='row justify-content-center'> | ||||||
|           <div className='col-24 ManageToDo__container'> |           <div className='col-24 ManageToDo__container'> | ||||||
|             <ul className='ManageToDo__list'> |             <ul className='ManageToDo__list'> | ||||||
|               {tasks.map((task, index) => { |               {tasks.map((task, index) => { | ||||||
|                 return ( |                 return ( | ||||||
|                   <li key={task.id} className={`ManageToDo__list-item ${(task.isCompleted) ? 'isCompleted' : ''}`}> |                   <li | ||||||
|                     <span className='ManageToDo__list-item-span'>{task.task}</span> |                     key={task.id} | ||||||
|  |                     className={`ManageToDo__list-item ${ | ||||||
|  |                       task.isCompleted ? 'isCompleted' : '' | ||||||
|  |                     }`} | ||||||
|  |                   > | ||||||
|  |                     <span className='ManageToDo__list-item-span'> | ||||||
|  |                       {task.task} | ||||||
|  |                     </span> | ||||||
|                     <div> |                     <div> | ||||||
|                       <button className='ManageToDo__task-btn' title='Supprimer de la liste' onClick={() => handleRemoveTask(task.id, index)}> |                       <button | ||||||
|  |                         className='ManageToDo__task-btn' | ||||||
|  |                         title='Supprimer de la liste' | ||||||
|  |                         onClick={() => handleRemoveTask(task.id, index)} | ||||||
|  |                       > | ||||||
|                         <FontAwesomeIcon icon={faTrash} /> |                         <FontAwesomeIcon icon={faTrash} /> | ||||||
|                       </button> |                       </button> | ||||||
|                       <button className='ManageToDo__task-btn' onClick={() => handleEditTask(task.id, index, task.isCompleted)}> |                       <button | ||||||
|                         <FontAwesomeIcon {...(task.isCompleted) ? { icon: faTimes } : { icon: faCheck }} /> |                         className='ManageToDo__task-btn' | ||||||
|  |                         onClick={() => | ||||||
|  |                           handleEditTask(task.id, index, task.isCompleted)} | ||||||
|  |                       > | ||||||
|  |                         <FontAwesomeIcon | ||||||
|  |                           {...(task.isCompleted | ||||||
|  |                             ? { icon: faTimes } | ||||||
|  |                             : { icon: faCheck })} | ||||||
|  |                         /> | ||||||
|                       </button> |                       </button> | ||||||
|                     </div> |                     </div> | ||||||
|                   </li> |                   </li> | ||||||
| @@ -109,14 +154,18 @@ const ManageToDo = () => { | |||||||
|               })} |               })} | ||||||
|             </ul> |             </ul> | ||||||
|           </div> |           </div> | ||||||
|         </div>} |         </div> | ||||||
|  |       )} | ||||||
|     </div> |     </div> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| const FunctionTabManager = (props) => { | const FunctionTabManager = props => { | ||||||
|   return ( |   return ( | ||||||
|     <FunctionTabs setSlideIndex={props.setSlideIndex} slideIndex={props.slideIndex}> |     <FunctionTabs | ||||||
|  |       setSlideIndex={props.setSlideIndex} | ||||||
|  |       slideIndex={props.slideIndex} | ||||||
|  |     > | ||||||
|       <div className='FunctionComponent__slide'> |       <div className='FunctionComponent__slide'> | ||||||
|         <ManageToDo /> |         <ManageToDo /> | ||||||
|       </div> |       </div> | ||||||
| @@ -130,7 +179,7 @@ const FunctionTabManager = (props) => { | |||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| const toDoList = (props) => ( | const toDoList = props => ( | ||||||
|   <FunctionPage |   <FunctionPage | ||||||
|     FunctionTabManager={FunctionTabManager} |     FunctionTabManager={FunctionTabManager} | ||||||
|     {...props} |     {...props} | ||||||
| @@ -139,8 +188,9 @@ const toDoList = (props) => ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| export async function getServerSideProps (context) { | export async function getServerSideProps (context) { | ||||||
|   return api.get('/functions/toDoList') |   return api | ||||||
|     .then((response) => ({ props: response.data })) |     .get('/functions/toDoList') | ||||||
|  |     .then(response => ({ props: response.data })) | ||||||
|     .catch(() => redirect(context, '/404')) |     .catch(() => redirect(context, '/404')) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,26 +20,28 @@ const Home = () => { | |||||||
|     <> |     <> | ||||||
|       <HeadTag /> |       <HeadTag /> | ||||||
|       <div className='Home__container container-fluid text-center'> |       <div className='Home__container container-fluid text-center'> | ||||||
|  |  | ||||||
|         <AutoPlaySwipeableViews enableMouseEvents interval={15000}> |         <AutoPlaySwipeableViews enableMouseEvents interval={15000}> | ||||||
|  |  | ||||||
|           {/* Slide 1 */} |           {/* Slide 1 */} | ||||||
|           <div className='row align-items-center justify-content-center'> |           <div className='row align-items-center justify-content-center'> | ||||||
|             <div className='col-24'> |             <div className='col-24'> | ||||||
|               <h1 className='title-important'>FunctionProject</h1> |               <h1 className='title-important'>FunctionProject</h1> | ||||||
|               <p className='Home__description'> |               <p className='Home__description'> | ||||||
|                                 Apprenez la programmation grâce à l'apprentissage par projet alias fonction (en <span className='important'>JavaScript</span>). |                 Apprenez la programmation grâce à l'apprentissage par projet | ||||||
|  |                 alias fonction (en <span className='important'>JavaScript</span> | ||||||
|  |                 ). | ||||||
|                 <br /> |                 <br /> | ||||||
|                 <Link href='/about'> |                 <Link href='/about'> | ||||||
|                   <a>En savoir plus ? (à-propos)</a> |                   <a>En savoir plus ? (à-propos)</a> | ||||||
|                 </Link> |                 </Link> | ||||||
|                 <br /> |                 <br /> | ||||||
|                                 Découvrez la liste des fonctions disponibles : |                 Découvrez la liste des fonctions disponibles : | ||||||
|               </p> |               </p> | ||||||
|             </div> |             </div> | ||||||
|             <div className='col-24 Home__logo-spin'> |             <div className='col-24 Home__logo-spin'> | ||||||
|               <Link href='/functions'> |               <Link href='/functions'> | ||||||
|                 <a><Loader width='100%' height='13em' speed='5s' /></a> |                 <a> | ||||||
|  |                   <Loader width='100%' height='13em' speed='5s' /> | ||||||
|  |                 </a> | ||||||
|               </Link> |               </Link> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
| @@ -49,18 +51,27 @@ const Home = () => { | |||||||
|             <div className='col-24'> |             <div className='col-24'> | ||||||
|               <h1 className='title-important'>Code Source</h1> |               <h1 className='title-important'>Code Source</h1> | ||||||
|               <p className='Home__description'> |               <p className='Home__description'> | ||||||
|                                 Le partage est essentiel afin de progresser. <br /> |                 Le partage est essentiel afin de progresser. <br /> | ||||||
|                                 Par conséquent chaque fonction a un article expliquant comment elle fonctionne et <br /> |                 Par conséquent chaque fonction a un article expliquant comment | ||||||
|                                 le code source du projet est disponible sur mon profil GitHub : |                 elle fonctionne et <br /> | ||||||
|  |                 le code source du projet est disponible sur mon profil GitHub : | ||||||
|               </p> |               </p> | ||||||
|             </div> |             </div> | ||||||
|             <div className='col-24'> |             <div className='col-24'> | ||||||
|               <a target='_blank' rel='noopener noreferrer' href='https://github.com/Divlo/FunctionProject'><img className='Home__image-width' src='/images/GitHub.png' alt='GitHub' /></a> |               <a | ||||||
|  |                 target='_blank' | ||||||
|  |                 rel='noopener noreferrer' | ||||||
|  |                 href='https://github.com/Divlo/FunctionProject' | ||||||
|  |               > | ||||||
|  |                 <img | ||||||
|  |                   className='Home__image-width' | ||||||
|  |                   src='/images/GitHub.png' | ||||||
|  |                   alt='GitHub' | ||||||
|  |                 /> | ||||||
|  |               </a> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|  |  | ||||||
|         </AutoPlaySwipeableViews> |         </AutoPlaySwipeableViews> | ||||||
|  |  | ||||||
|       </div> |       </div> | ||||||
|     </> |     </> | ||||||
|   ) |   ) | ||||||
|   | |||||||
| @@ -12,11 +12,10 @@ import htmlParser from 'html-react-parser' | |||||||
| import Loader from '../../components/Loader' | import Loader from '../../components/Loader' | ||||||
| import ReactMarkdown from 'react-markdown' | import ReactMarkdown from 'react-markdown' | ||||||
| import CodeBlock from '../../components/CodeBlock' | import CodeBlock from '../../components/CodeBlock' | ||||||
| import api from '../../utils/api' | import api, { API_URL } from '../../utils/api' | ||||||
| import { API_URL } from '../../utils/config/config' |  | ||||||
| import '../../public/css/pages/profile.css' | import '../../public/css/pages/profile.css' | ||||||
|  |  | ||||||
| const Profile = (props) => { | const Profile = props => { | ||||||
|   const { isAuth, user, logoutUser } = useContext(UserContext) |   const { isAuth, user, logoutUser } = useContext(UserContext) | ||||||
|   const [isOpen, setIsOpen] = useState(false) |   const [isOpen, setIsOpen] = useState(false) | ||||||
|   const [inputState, setInputState] = useState({}) |   const [inputState, setInputState] = useState({}) | ||||||
| @@ -25,7 +24,12 @@ const Profile = (props) => { | |||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if (isAuth) { |     if (isAuth) { | ||||||
|       setInputState({ name: user.name, email: user.email, biography: user.biography, isPublicEmail: user.isPublicEmail }) |       setInputState({ | ||||||
|  |         name: user.name, | ||||||
|  |         email: user.email, | ||||||
|  |         biography: user.biography, | ||||||
|  |         isPublicEmail: user.isPublicEmail | ||||||
|  |       }) | ||||||
|     } |     } | ||||||
|   }, [isAuth]) |   }, [isAuth]) | ||||||
|  |  | ||||||
| @@ -33,11 +37,16 @@ const Profile = (props) => { | |||||||
|  |  | ||||||
|   const handleChange = (event, isTypeCheck = false) => { |   const handleChange = (event, isTypeCheck = false) => { | ||||||
|     const inputStateNew = { ...inputState } |     const inputStateNew = { ...inputState } | ||||||
|     inputStateNew[event.target.name] = (event.target.files != null) ? event.target.files[0] : (isTypeCheck) ? event.target.checked : event.target.value |     inputStateNew[event.target.name] = | ||||||
|  |       event.target.files != null | ||||||
|  |         ? event.target.files[0] | ||||||
|  |         : isTypeCheck | ||||||
|  |           ? event.target.checked | ||||||
|  |           : event.target.value | ||||||
|     setInputState(inputStateNew) |     setInputState(inputStateNew) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const handleSubmit = (event) => { |   const handleSubmit = event => { | ||||||
|     event.preventDefault() |     event.preventDefault() | ||||||
|     const token = user.token |     const token = user.token | ||||||
|     if (isAuth && token != null) { |     if (isAuth && token != null) { | ||||||
| @@ -49,14 +58,17 @@ const Profile = (props) => { | |||||||
|       formData.append('isPublicEmail', inputState.isPublicEmail) |       formData.append('isPublicEmail', inputState.isPublicEmail) | ||||||
|       formData.append('logo', inputState.logo) |       formData.append('logo', inputState.logo) | ||||||
|  |  | ||||||
|       api.put('/users/', formData, { headers: { Authorization: token } }) |       api | ||||||
|  |         .put('/users/', formData, { headers: { Authorization: token } }) | ||||||
|         .then(() => { |         .then(() => { | ||||||
|           setIsLoading(false) |           setIsLoading(false) | ||||||
|           logoutUser() |           logoutUser() | ||||||
|           redirect({}, '/users/login?isSuccessEdit=true') |           redirect({}, '/users/login?isSuccessEdit=true') | ||||||
|         }) |         }) | ||||||
|         .catch((error) => { |         .catch(error => { | ||||||
|           setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`) |           setMessage( | ||||||
|  |             `<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>` | ||||||
|  |           ) | ||||||
|           setIsLoading(false) |           setIsLoading(false) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| @@ -64,174 +76,316 @@ const Profile = (props) => { | |||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <HeadTag title={`${props.name} - FunctionProject`} description={`Profil utilisateur de ${props.name}. ${(props.biography != null) ? props.biography : ''}`} /> |       <HeadTag | ||||||
|  |         title={`${props.name} - FunctionProject`} | ||||||
|  |         description={`Profil utilisateur de ${props.name}. ${ | ||||||
|  |           props.biography != null ? props.biography : '' | ||||||
|  |         }`} | ||||||
|  |       /> | ||||||
|  |  | ||||||
|       {/* Édition du profil */} |       {/* Édition du profil */} | ||||||
|       {(isOpen) |       {isOpen ? ( | ||||||
|         ? ( |         <Modal toggleModal={toggleModal}> | ||||||
|           <Modal toggleModal={toggleModal}> |           <div className='Profile__container container-fluid'> | ||||||
|             <div className='Profile__container container-fluid'> |             <div className='Profile__row row'> | ||||||
|               <div className='Profile__row row'> |               <div className='col-24'> | ||||||
|                 <div className='col-24'> |                 <div className='Profile__Modal-top-container row'> | ||||||
|                   <div className='Profile__Modal-top-container row'> |                   <div className='col-24'> | ||||||
|                     <div className='col-24'> |                     <span | ||||||
|                       <span onClick={toggleModal} style={{ cursor: 'pointer', position: 'absolute', left: 0 }}> |                       onClick={toggleModal} | ||||||
|                         <FontAwesomeIcon icon={faTimes} style={{ width: '1.5rem', color: 'red' }} /> |                       style={{ | ||||||
|                       </span> |                         cursor: 'pointer', | ||||||
|                       <h2 className='text-center'>Éditer le profil</h2> |                         position: 'absolute', | ||||||
|                       <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> |                         left: 0 | ||||||
|                     </div> |                       }} | ||||||
|                   </div> |                     > | ||||||
|                 </div> |                       <FontAwesomeIcon | ||||||
|  |                         icon={faTimes} | ||||||
|                 <div className='col-24'> |                         style={{ width: '1.5rem', color: 'red' }} | ||||||
|                   <form onSubmit={handleSubmit}> |                       /> | ||||||
|                     <div className='form-group'> |                     </span> | ||||||
|                       <label className='form-label' htmlFor='name'>Nom :</label> |                     <h2 className='text-center'>Éditer le profil</h2> | ||||||
|                       <input value={inputState.name} onChange={handleChange} type='text' name='name' id='name' className='form-control' placeholder='Divlo' /> |                     <p className='text-center'> | ||||||
|                     </div> |                       <em> | ||||||
|  |                         (Vous devrez vous reconnecter après la sauvegarde){' '} | ||||||
|                     <div className='form-group'> |                         <br /> Si vous changez votre adresse email, vous devrez | ||||||
|                       <label className='form-label' htmlFor='email'>Email :</label> |                         la confirmer comme à l'inscription (vérifier vos | ||||||
|                       <input value={inputState.email} onChange={handleChange} type='email' name='email' id='email' className='form-control' placeholder='email@gmail.com' /> |                         emails). | ||||||
|                     </div> |                       </em> | ||||||
|  |                     </p> | ||||||
|                     <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> | ||||||
|               </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'> |               <div className='col-24'> | ||||||
|                     <img className='Profile__logo' src={API_URL + props.logo} alt={props.name} /> |                 <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> | ||||||
|  |  | ||||||
|                   <div className='col-24 text-center'> |                   <div className='form-group'> | ||||||
|                     {(props.biography != null) && <p>{props.biography}</p>} |                     <label className='form-label' htmlFor='email'> | ||||||
|                     {(props.email != null) && <p><span className='important'>Email :</span> {props.email}</p>} |                       Email : | ||||||
|                     <p><span className='important'>Date de création du compte :</span> {date.format(new Date(props.createdAt), 'DD/MM/YYYY à HH:mm', true)}</p> |                     </label> | ||||||
|  |                     <input | ||||||
|  |                       value={inputState.email} | ||||||
|  |                       onChange={handleChange} | ||||||
|  |                       type='email' | ||||||
|  |                       name='email' | ||||||
|  |                       id='email' | ||||||
|  |                       className='form-control' | ||||||
|  |                       placeholder='email@gmail.com' | ||||||
|  |                     /> | ||||||
|                   </div> |                   </div> | ||||||
|  |  | ||||||
|                   {(isAuth && user.name === props.name) && |                   <div className='form-group custom-control custom-switch'> | ||||||
|                     <button onClick={toggleModal} style={{ marginBottom: '25px' }} className='btn btn-dark'> |                     <input | ||||||
|                       <FontAwesomeIcon icon={faPen} style={{ cursor: 'pointer', width: '1rem' }} /> |                       onChange={event => handleChange(event, true)} | ||||||
|                                                 Éditez le profil |                       type='checkbox' | ||||||
|                     </button>} |                       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> | ||||||
|             </div> |             </div> | ||||||
|  |           </div> | ||||||
|             {(props.favoritesArray.length > 0) && |         </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='row justify-content-center'> | ||||||
|                 <div className='col-24 text-center'> |                 <div className='col-24 text-center'> | ||||||
|                   <h2>Dernières fonctions ajoutées aux <span className='important'>favoris</span> :</h2> |                   <img | ||||||
|  |                     className='Profile__logo' | ||||||
|  |                     src={API_URL + props.logo} | ||||||
|  |                     alt={props.name} | ||||||
|  |                   /> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div className='col-24'> |  | ||||||
|                   <div className='row justify-content-center'> |                 <div className='col-24 text-center'> | ||||||
|                     {props.favoritesArray.map((favorite) => { |                   {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', | ||||||
|  |                       true | ||||||
|  |                     )} | ||||||
|  |                   </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> | ||||||
|  |                   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  | ||||||
|  |                         <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> | ||||||
|  |                       <ReactMarkdown | ||||||
|  |                         source={comment.message} | ||||||
|  |                         renderers={{ code: CodeBlock }} | ||||||
|  |                       /> | ||||||
|  |                     </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> | ||||||
|  |               </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 ( |                       return ( | ||||||
|                         <FunctionCard key={favorite.id} {...favorite} /> |                         <tr key={index}> | ||||||
|  |                           <td className='table-row text-center'> | ||||||
|  |                             {currentQuote.quote} | ||||||
|  |                           </td> | ||||||
|  |                           <td className='table-row text-center'> | ||||||
|  |                             {currentQuote.author} | ||||||
|  |                           </td> | ||||||
|  |                         </tr> | ||||||
|                       ) |                       ) | ||||||
|                     })} |                     })} | ||||||
|                   </div> |                   </tbody> | ||||||
|                 </div> |                 </table> | ||||||
|               </div>} |               </div> | ||||||
|  |             </div> | ||||||
|             {(props.commentsArray.length > 0) && |           )} | ||||||
|               <div className='row justify-content-center'> |         </div> | ||||||
|                 <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  |  | ||||||
|                           <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> |  | ||||||
|                         <ReactMarkdown source={comment.message} renderers={{ code: CodeBlock }} /> |  | ||||||
|                       </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> |  | ||||||
|                 </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> |  | ||||||
|         )} |  | ||||||
|     </> |     </> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| export async function getServerSideProps (context) { | export async function getServerSideProps (context) { | ||||||
|   const { name } = context.params |   const { name } = context.params | ||||||
|   return api.get(`/users/${name}`) |   return api | ||||||
|     .then((response) => ({ props: response.data })) |     .get(`/users/${name}`) | ||||||
|  |     .then(response => ({ props: response.data })) | ||||||
|     .catch(() => redirect(context, '/404')) |     .catch(() => redirect(context, '/404')) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import axios from 'axios' | import axios from 'axios' | ||||||
| import { API_URL } from './config/config' |  | ||||||
|  | export const API_URL = process.env.NEXT_PUBLIC_API_URL | ||||||
|  |  | ||||||
| const api = axios.create({ | const api = axios.create({ | ||||||
|   baseURL: API_URL, |   baseURL: API_URL, | ||||||
|   | |||||||
| @@ -1 +0,0 @@ | |||||||
| export const API_URL = process.env.NEXT_PUBLIC_API_URL |  | ||||||
		Reference in New Issue
	
	Block a user