frontend: Système d'onglets pour /functions/slug
This commit is contained in:
		
							
								
								
									
										39
									
								
								website/components/FunctionTabs/FunctionTabs.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								website/components/FunctionTabs/FunctionTabs.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| .FunctionTabs__nav { | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|     padding-left: 0; | ||||
|     margin-bottom: 0; | ||||
|     list-style: none; | ||||
|     border-bottom: 1px solid #d9e2ef; | ||||
|     margin-bottom: -1px; | ||||
| } | ||||
| .FunctionTabs__nav-item { | ||||
|     margin-bottom: -1px; | ||||
|     cursor: pointer; | ||||
| } | ||||
| .FunctionTabs__nav-link { | ||||
|     color: var(--text-color); | ||||
|     border: 1px solid #0c0b0b38; | ||||
|     border-bottom: 0px; | ||||
|     border-top-left-radius: .375rem; | ||||
|     border-top-right-radius: .375rem; | ||||
|     display: block; | ||||
|     padding: .5rem 1rem; | ||||
| } | ||||
| .FunctionTabs__nav-link-active { | ||||
|     border-color: #d9e2ef #d9e2ef #fff; | ||||
|     color: var(--important); | ||||
| } | ||||
| .FunctionTabs__nav-link:hover { | ||||
|     border-color: #f1f4f8 #f1f4f8 #d9e2ef; | ||||
|     text-decoration: none; | ||||
| } | ||||
|  | ||||
| @media (max-width: 490px) { | ||||
|     .FunctionTabs__nav { | ||||
|         flex-direction: column; | ||||
|     } | ||||
|     .FunctionTabs__nav-link { | ||||
|         border-color: #f1f4f8 #f1f4f8 #d9e2ef; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										59
									
								
								website/components/FunctionTabs/FunctionTabs.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								website/components/FunctionTabs/FunctionTabs.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| import { Fragment, useState } from 'react'; | ||||
| import SwipeableViews from 'react-swipeable-views'; | ||||
| import './FunctionTabs.css'; | ||||
|  | ||||
| function FunctionTabs(props) { | ||||
|  | ||||
|     const [slideIndex, setSlideIndex] = useState(0); | ||||
|  | ||||
|     return ( | ||||
|         <Fragment> | ||||
|  | ||||
|             {/* Tabs */} | ||||
|             <div className="container"> | ||||
|                 <div className="row justify-content-center"> | ||||
|                     <ul className="FunctionTabs__nav"> | ||||
|                         {(props.type !== 'article') && | ||||
|                             <li className="FunctionTabs__nav-item"> | ||||
|                                 <a  | ||||
|                                     className={`FunctionTabs__nav-link ${(slideIndex === 0) ? "FunctionTabs__nav-link-active" : ""}`} | ||||
|                                     onClick={() => setSlideIndex(0)} | ||||
|                                 > | ||||
|                                    ⚙️ Utilisation | ||||
|                                 </a> | ||||
|                             </li> | ||||
|                         } | ||||
|                         <li className="FunctionTabs__nav-item"> | ||||
|                             <a  | ||||
|                                 className={`FunctionTabs__nav-link ${((slideIndex === 1 && props.type !== 'article') || (props.type === 'article' && slideIndex === 0)) ? "FunctionTabs__nav-link-active" : ""}`} | ||||
|                                 onClick={() => setSlideIndex((props.type === 'article') ? 0 : 1)} | ||||
|                             > | ||||
|                                📝 Article | ||||
|                             </a> | ||||
|                         </li> | ||||
|                         <li className="FunctionTabs__nav-item"> | ||||
|                             <a  | ||||
|                                 className={`FunctionTabs__nav-link ${((slideIndex === 2 && props.type !== 'article') || (props.type === 'article' && slideIndex === 1)) ? "FunctionTabs__nav-link-active" : ""}`} | ||||
|                                 onClick={() => setSlideIndex((props.type === 'article') ? 1 : 2)} | ||||
|                             > | ||||
|                                 📬 Commentaires | ||||
|                             </a> | ||||
|                         </li> | ||||
|                     </ul> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             {/* Tabs content */} | ||||
|             <div className="container-fluid"> | ||||
|                 <div className="row justify-content-center"> | ||||
|                     <SwipeableViews onChangeIndex={(index) => setSlideIndex(index)} index={slideIndex} enableMouseEvents> | ||||
|                         {props.children} | ||||
|                     </SwipeableViews> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|         </Fragment> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| export default FunctionTabs; | ||||
| @@ -1,26 +1,61 @@ | ||||
| import { Fragment } from 'react'; | ||||
| import { useRouter } from 'next/router'; | ||||
| import Link from 'next/link'; | ||||
| import HeadTag from '../../components/HeadTag'; | ||||
| import FunctionTabs from '../../components/FunctionTabs/FunctionTabs'; | ||||
| import redirect from '../../utils/redirect'; | ||||
| import api from '../../config/api'; | ||||
| import { API_URL } from '../../config/config'; | ||||
| import '../../public/css/pages/FunctionComponent.css'; | ||||
|  | ||||
| const FunctionComponent = () => { | ||||
|  | ||||
|     const { slug } = useRouter().query; | ||||
| const FunctionComponent = (props) => { | ||||
|     console.log(props); | ||||
|      | ||||
|     // Constantes | ||||
|     const createdAt = new Date(props.createdAt); | ||||
|     const publicationDate = `${('0'+createdAt.getDate()).slice(-2)}/${('0'+(createdAt.getMonth()+1)).slice(-2)}/${createdAt.getFullYear()}`; | ||||
|  | ||||
|     return ( | ||||
|         <Fragment> | ||||
|             <HeadTag  | ||||
|                 title={slug} | ||||
|                 description={slug} | ||||
|                 image={`${API_URL}/images/functions/${slug}.png`}  | ||||
|             /> | ||||
|             <div className="container text-center"> | ||||
|                 <div className="row justify-content-center"> | ||||
|                     <h1>{slug}</h1> | ||||
|             <HeadTag title={props.title} description={props.description} image={API_URL + props.image} /> | ||||
|  | ||||
|             <div className="container-fluid"> | ||||
|  | ||||
|                 <div className="container-fluid"> | ||||
|                     <div className="row justify-content-center text-center"> | ||||
|                         <div className="FunctionComponent__top col-24"> | ||||
|                             <img src={API_URL + props.image} alt={props.title} /> | ||||
|                             <h1 className="FunctionComponent__title title-important">{props.title}</h1> | ||||
|                             <p className="FunctionComponent__description">{props.description}</p> | ||||
|                             <div className="FunctionCard__info"> | ||||
|                                 <Link href={`/functions?categoryId=${props.categorieId}`}> | ||||
|                                     <a className="FunctionCard__category" style={{ backgroundColor: props.categorie.color, color: 'inherit' }}>{props.categorie.name}</a> | ||||
|                                 </Link> | ||||
|                                 <p className="FunctionCard__publication-date">{publicationDate}</p> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  | ||||
|                 <div className="container-fluid"> | ||||
|                     <div className="row justify-content-center"> | ||||
|                         <FunctionTabs type={props.type}> | ||||
|                             <div>Slide 1</div> | ||||
|                             <div>Slide 2</div> | ||||
|                             <div>Slide 3</div> | ||||
|                         </FunctionTabs> | ||||
|                     </div> | ||||
|                 </div> | ||||
|  | ||||
|             </div> | ||||
|         </Fragment> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| export async function getServerSideProps(context) { | ||||
|     const { slug } = context.params; | ||||
|     return api.get(`/functions/${slug}`) | ||||
|         .then((response) => ({ props: response.data })) | ||||
|         .catch(() => redirect(context, '/404')); | ||||
| } | ||||
|  | ||||
| export default FunctionComponent; | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { Fragment, useState, useEffect, useRef, useCallback } from 'react'; | ||||
| import { useRouter } from 'next/router'; | ||||
| import HeadTag from '../../components/HeadTag'; | ||||
| import FunctionCard from '../../components/FunctionCard/FunctionCard'; | ||||
| import Loader from '../../components/Loader'; | ||||
| @@ -8,6 +9,8 @@ import useAPI from '../../hooks/useAPI'; | ||||
|  | ||||
| const Functions = () => { | ||||
|  | ||||
|     const { categoryId } = useRouter().query; | ||||
|  | ||||
|     // State de recherche et de catégories | ||||
|     const [, categories]                = useAPI('/categories'); | ||||
|     const [inputSearch, setInputSearch] = useState({ search: "", selectedCategory: "0" }); | ||||
| @@ -39,6 +42,13 @@ const Functions = () => { | ||||
|         }); | ||||
|     } | ||||
|      | ||||
|     // Récupère la catégorie avec la query categoryId | ||||
|     useEffect(() => { | ||||
|         if (categoryId) { | ||||
|             handleChange({ target: { name: "selectedCategory", value: categoryId } }); | ||||
|         } | ||||
|     }, [categoryId]); | ||||
|  | ||||
|     // Récupère les fonctions si la page change | ||||
|     useEffect(() => { | ||||
|         getFunctionsData().then((data) => setFunctionsData({  | ||||
|   | ||||
| @@ -27,7 +27,7 @@ const Home = () => { | ||||
|                     {/* Slide 1 */} | ||||
|                     <div className="row align-items-center justify-content-center"> | ||||
|                         <div className="col-24"> | ||||
|                             <h1 className="Home__title-important important">FunctionProject</h1> | ||||
|                             <h1 className="title-important">FunctionProject</h1> | ||||
|                             <p className="Home__description"> | ||||
|                                 Apprenez la programmation grâce à l'apprentissage par projet alias fonction.<br/> | ||||
|                                 Découvrez la liste des fonctions disponibles :  | ||||
| @@ -43,7 +43,7 @@ const Home = () => { | ||||
|                     {/* Slide 2 */} | ||||
|                     <div className="row align-items-center justify-content-center"> | ||||
|                         <div className="col-24"> | ||||
|                             <h1 className="Home__title-important important">Code Source</h1> | ||||
|                             <h1 className="title-important">Code Source</h1> | ||||
|                             <p className="Home__description"> | ||||
|                                 Le partage est essentiel afin de progresser. <br/> | ||||
|                                 Par conséquent chaque fonction a un article expliquant comment elle fonctionne et <br/> | ||||
|   | ||||
| @@ -26,9 +26,6 @@ p { | ||||
|     font-size: 18px; | ||||
|     line-height: 1.9; | ||||
| } | ||||
| h2 { | ||||
|     font-size: 1.8em; | ||||
| } | ||||
| a:hover { | ||||
|     text-decoration: underline; | ||||
| } | ||||
| @@ -76,6 +73,10 @@ a:hover { | ||||
| .align-items-center { | ||||
|     align-items: center; | ||||
| } | ||||
| .title-important { | ||||
|     color: var(--important); | ||||
|     font-weight: 500; | ||||
| } | ||||
| a, .important { | ||||
|     color: var(--important); | ||||
|     text-decoration: none; | ||||
|   | ||||
							
								
								
									
										18
									
								
								website/public/css/pages/FunctionComponent.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								website/public/css/pages/FunctionComponent.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| .FunctionComponent__top { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     position: relative; | ||||
|     flex-direction: column; | ||||
|     word-wrap: break-word; | ||||
|     box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25); | ||||
|     border: 1px solid black; | ||||
|     border-radius: 1rem; | ||||
|     margin-top: 50px; | ||||
| } | ||||
| .FunctionComponent__title { | ||||
|     margin: 0; | ||||
| } | ||||
| .FunctionComponent__description { | ||||
|     word-break: break-all; | ||||
|     margin-bottom: 0; | ||||
| } | ||||
| @@ -4,9 +4,6 @@ | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
| } | ||||
| .Home__title-important { | ||||
|     font-weight: 500; | ||||
| } | ||||
| .Home__logo-spin { | ||||
|     cursor: pointer; | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								website/utils/redirect.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								website/utils/redirect.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| function redirect (ctx, path) { | ||||
|     ctx.res.writeHead(302, { Location: path }); | ||||
|     ctx.res.end(); | ||||
| } | ||||
|  | ||||
| module.exports = redirect; | ||||
		Reference in New Issue
	
	Block a user